diff mbox

[v2] arm/ls1021a: Add Sleep feature for ls1021

Message ID 1442308030-21231-1-git-send-email-dongsheng.wang@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dongsheng Wang Sept. 15, 2015, 9:07 a.m. UTC
From: Wang Dongsheng <dongsheng.wang@freescale.com>

Add system STANDBY implement for ls1021 platform.

Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
---
*v2*:
- Remove PSCI code. Just implement STANDBY in platform code.

Comments

Dongsheng Wang Sept. 23, 2015, 2:45 a.m. UTC | #1
Hi all,

Any feedback about this patch?

Regards,
-Dongsheng

> -----Original Message-----
> From: Dongsheng Wang [mailto:dongsheng.wang@freescale.com]
> Sent: Tuesday, September 15, 2015 5:07 PM
> To: shawnguo@kernel.org
> Cc: mark.rutland@arm.com; Wang Huan-B18965; Jin Zhengxiong-R64188; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; Wang Dongsheng-B40534;
> Zhao Chenhui-B35336
> Subject: [PATCH v2] arm/ls1021a: Add Sleep feature for ls1021
> 
> From: Wang Dongsheng <dongsheng.wang@freescale.com>
> 
> Add system STANDBY implement for ls1021 platform.
> 
> Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
> Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> ---
> *v2*:
> - Remove PSCI code. Just implement STANDBY in platform code.
> diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
> index fb689d8..d7a2d1d 100644
> --- a/arch/arm/mach-imx/Makefile
> +++ b/arch/arm/mach-imx/Makefile
> @@ -90,6 +90,7 @@ ifeq ($(CONFIG_SUSPEND),y)
>  AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
>  obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
>  obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
> +obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o
>  endif
>  obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
> 
> diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
> new file mode 100644
> index 0000000..90775cf
> --- /dev/null
> +++ b/arch/arm/mach-imx/pm-ls1.c
> @@ -0,0 +1,225 @@
> +/*
> + * Support Power Management Control for LS1
> + *
> + * Copyright 2015 Freescale Semiconductor Inc.
> + *
> + * This program is free software; you can redistribute	it and/or modify it
> + * under  the terms of	the GNU General	 Public License as published by
> the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_address.h>
> +#include <linux/suspend.h>
> +
> +#include <asm/cacheflush.h>
> +#include <asm/suspend.h>
> +
> +#define FSL_SLEEP			0x1
> +#define FSL_DEEP_SLEEP			0x2
> +
> +#define CCSR_SCFG_CLUSTERPMCR		0x904
> +#define CCSR_SCFG_CLUSTERPMCR_WFIL2EN	0x80000000
> +#define CCSR_SCFG_CLUSTERPM_ENABLE	1
> +#define CCSR_SCFG_CLUSTERPM_DISABLE	0
> +
> +#define CCSR_RCPM_POWMGTCSR		0x130
> +#define CCSR_RCPM_POWMGTCSR_LPM20_REQ	0x00100000
> +#define CCSR_RCPM_IPPDEXPCR0		0x140
> +#define CCSR_RCPM_IPPDEXPCR1		0x144
> +#define CCSR_RCPM_IPPDEXPCR1_OCRAM1	0x10000000
> +
> +#define SLEEP_ARRAY_SIZE		3
> +
> +static u32 ippdexpcr0, ippdexpcr1;
> +
> +struct ls1_pm_baseaddr {
> +	void __iomem *rcpm;
> +	void __iomem *scfg;
> +};
> +
> +static struct ls1_pm_baseaddr ls1_pm_base;
> +
> +static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set)
> +{
> +	u32 val;
> +
> +	val = ioread32be(addr);
> +	val = (val & ~clear) | set;
> +	iowrite32be(val, addr);
> +}
> +
> +static int ls1_pm_iomap(suspend_state_t state)
> +{
> +	struct device_node *np;
> +	void *base;
> +
> +	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
> +	if (!np) {
> +		pr_err("Missing SCFG node in the device tree\n");
> +		return -EINVAL;
> +	}
> +
> +	base = of_iomap(np, 0);
> +	of_node_put(np);
> +	if (!base) {
> +		pr_err("Couldn't map SCFG registers\n");
> +		return -EINVAL;
> +	}
> +
> +	ls1_pm_base.scfg = base;
> +
> +	return 0;
> +}
> +
> +static void ls1_pm_uniomap(suspend_state_t state)
> +{
> +	iounmap(ls1_pm_base.scfg);
> +}
> +
> +/* set IP powerdown exception, make them work during sleep/deep sleep */
> +static void ls1_set_powerdown(void)
> +{
> +	iowrite32be(ippdexpcr0, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
> +	iowrite32be(ippdexpcr1,	ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
> +}
> +
> +static void ls1_set_power_except(struct device *dev, int on)
> +{
> +	int ret;
> +	u32 value[SLEEP_ARRAY_SIZE];
> +
> +	/*
> +	 * Get the values in the "rcpm-wakeup" property. There are three values.
> +	 * The first points to the RCPM node, the second is the value of
> +	 * the ippdexpcr0 register, and the third is the value of
> +	 * the ippdexpcr1 register.
> +	 */
> +	ret = of_property_read_u32_array(dev->of_node, "rcpm-wakeup",
> +					 value, SLEEP_ARRAY_SIZE);
> +	if (ret) {
> +		dev_err(dev, "%s: Can not find the \"sleep\" property.\n",
> +			__func__);
> +		return;
> +	}
> +
> +	ippdexpcr0 |= value[1];
> +	ippdexpcr1 |= value[2];
> +
> +	pr_debug("%s: set %s as a wakeup source", __func__,
> +		 dev->of_node->full_name);
> +}
> +
> +static void ls1_set_wakeup_device(struct device *dev, void *enable)
> +{
> +	/* set each device which can act as wakeup source */
> +	if (device_may_wakeup(dev))
> +		ls1_set_power_except(dev, *((int *)enable));
> +}
> +
> +/* enable cluster to enter the PCL10 state */
> +static void ls1_set_clusterpm(int enable)
> +{
> +	u32 val = 0;
> +
> +	if (enable)
> +		val = CCSR_SCFG_CLUSTERPMCR_WFIL2EN;
> +
> +	iowrite32be(val, ls1_pm_base.scfg + CCSR_SCFG_CLUSTERPMCR);
> +}
> +
> +static int ls1_suspend_enter(suspend_state_t state)
> +{
> +	int ret = 0;
> +
> +	ls1_set_powerdown();
> +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_ENABLE);
> +
> +	switch (state) {
> +	case PM_SUSPEND_STANDBY:
> +		flush_cache_louis();
> +		ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR,
> +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ,
> +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ);
> +		cpu_do_idle();
> +		break;
> +	case PM_SUSPEND_MEM:
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_DISABLE);
> +
> +	return ret;
> +}
> +
> +static int ls1_suspend_valid(suspend_state_t state)
> +{
> +	if (state == PM_SUSPEND_STANDBY)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static int ls1_suspend_begin(suspend_state_t state)
> +{
> +	int res = -EINVAL;
> +
> +	ippdexpcr0 = 0;
> +	ippdexpcr1 = 0;
> +
> +	if (state == PM_SUSPEND_STANDBY)
> +		res = ls1_pm_iomap(state);
> +
> +	if (!res)
> +		dpm_for_each_dev(NULL, ls1_set_wakeup_device);
> +
> +	return res;
> +}
> +
> +static void ls1_suspend_end(void)
> +{
> +	ls1_pm_uniomap(PM_SUSPEND_STANDBY);
> +}
> +
> +static const struct platform_suspend_ops ls1_suspend_ops = {
> +	.valid = ls1_suspend_valid,
> +	.enter = ls1_suspend_enter,
> +	.begin = ls1_suspend_begin,
> +	.end = ls1_suspend_end,
> +};
> +
> +static const struct of_device_id rcpm_matches[] = {
> +	{
> +		.compatible = "fsl,ls1021a-rcpm",
> +	},
> +	{}
> +};
> +
> +static int __init ls1_pm_init(void)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +
> +	np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
> +	if (!np) {
> +		pr_err("%s: can't find the rcpm node.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ls1_pm_base.rcpm = of_iomap(np, 0);
> +	of_node_put(np);
> +	if (!ls1_pm_base.rcpm)
> +		return -ENOMEM;
> +
> +	suspend_set_ops(&ls1_suspend_ops);
> +
> +	pr_info("Freescale Power Management Control Registered\n");
> +
> +	return 0;
> +}
> +arch_initcall(ls1_pm_init);
> --
> 2.1.0.27.g96db324
Shawn Guo Sept. 23, 2015, 3:13 p.m. UTC | #2
On Tue, Sep 15, 2015 at 05:07:10PM +0800, Dongsheng Wang wrote:
> From: Wang Dongsheng <dongsheng.wang@freescale.com>
> 
> Add system STANDBY implement for ls1021 platform.
> 
> Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
> Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> ---
> *v2*:
> - Remove PSCI code. Just implement STANDBY in platform code.

Why stepping back?  We are encouraged to move to PSCI, aren't we?

> diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
> index fb689d8..d7a2d1d 100644
> --- a/arch/arm/mach-imx/Makefile
> +++ b/arch/arm/mach-imx/Makefile
> @@ -90,6 +90,7 @@ ifeq ($(CONFIG_SUSPEND),y)
>  AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
>  obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
>  obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
> +obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o
>  endif
>  obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
>  
> diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
> new file mode 100644
> index 0000000..90775cf
> --- /dev/null
> +++ b/arch/arm/mach-imx/pm-ls1.c
> @@ -0,0 +1,225 @@
> +/*
> + * Support Power Management Control for LS1
> + *
> + * Copyright 2015 Freescale Semiconductor Inc.
> + *
> + * This program is free software; you can redistribute	it and/or modify it
> + * under  the terms of	the GNU General	 Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_address.h>
> +#include <linux/suspend.h>
> +
> +#include <asm/cacheflush.h>
> +#include <asm/suspend.h>
> +
> +#define FSL_SLEEP			0x1
> +#define FSL_DEEP_SLEEP			0x2

Unused defines.

> +
> +#define CCSR_SCFG_CLUSTERPMCR		0x904
> +#define CCSR_SCFG_CLUSTERPMCR_WFIL2EN	0x80000000
> +#define CCSR_SCFG_CLUSTERPM_ENABLE	1
> +#define CCSR_SCFG_CLUSTERPM_DISABLE	0
> +
> +#define CCSR_RCPM_POWMGTCSR		0x130
> +#define CCSR_RCPM_POWMGTCSR_LPM20_REQ	0x00100000
> +#define CCSR_RCPM_IPPDEXPCR0		0x140
> +#define CCSR_RCPM_IPPDEXPCR1		0x144
> +#define CCSR_RCPM_IPPDEXPCR1_OCRAM1	0x10000000
> +
> +#define SLEEP_ARRAY_SIZE		3

The name of the macro doesn't appealing.
> +
> +static u32 ippdexpcr0, ippdexpcr1;

It makes more sense to have a structure holds all these variables and
the following base addresses.

> +
> +struct ls1_pm_baseaddr {
> +	void __iomem *rcpm;
> +	void __iomem *scfg;
> +};
> +
> +static struct ls1_pm_baseaddr ls1_pm_base;
> +
> +static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set)
> +{
> +	u32 val;
> +
> +	val = ioread32be(addr);
> +	val = (val & ~clear) | set;
> +	iowrite32be(val, addr);
> +}
> +
> +static int ls1_pm_iomap(suspend_state_t state)

How is argument 'state' used?

> +{
> +	struct device_node *np;
> +	void *base;
> +
> +	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
> +	if (!np) {
> +		pr_err("Missing SCFG node in the device tree\n");
> +		return -EINVAL;
> +	}
> +
> +	base = of_iomap(np, 0);
> +	of_node_put(np);
> +	if (!base) {
> +		pr_err("Couldn't map SCFG registers\n");
> +		return -EINVAL;
> +	}
> +
> +	ls1_pm_base.scfg = base;
> +
> +	return 0;
> +}
> +
> +static void ls1_pm_uniomap(suspend_state_t state)
> +{
> +	iounmap(ls1_pm_base.scfg);
> +}
> +
> +/* set IP powerdown exception, make them work during sleep/deep sleep */
> +static void ls1_set_powerdown(void)
> +{
> +	iowrite32be(ippdexpcr0, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
> +	iowrite32be(ippdexpcr1,	ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
> +}
> +
> +static void ls1_set_power_except(struct device *dev, int on)

How argument 'on' is used?

> +{
> +	int ret;
> +	u32 value[SLEEP_ARRAY_SIZE];
> +
> +	/*
> +	 * Get the values in the "rcpm-wakeup" property. There are three values.
> +	 * The first points to the RCPM node, the second is the value of
> +	 * the ippdexpcr0 register, and the third is the value of
> +	 * the ippdexpcr1 register.
> +	 */
> +	ret = of_property_read_u32_array(dev->of_node, "rcpm-wakeup",
> +					 value, SLEEP_ARRAY_SIZE);

The property should be documented in device tree bindings.  And you need
a good reason for why these register values should be defined by device
tree.

> +	if (ret) {
> +		dev_err(dev, "%s: Can not find the \"sleep\" property.\n",
> +			__func__);
> +		return;
> +	}
> +
> +	ippdexpcr0 |= value[1];
> +	ippdexpcr1 |= value[2];
> +
> +	pr_debug("%s: set %s as a wakeup source", __func__,

When you have device pointer, you should use dev_dbg() instead of
pr_debug().

> +		 dev->of_node->full_name);
> +}
> +
> +static void ls1_set_wakeup_device(struct device *dev, void *enable)
> +{
> +	/* set each device which can act as wakeup source */
> +	if (device_may_wakeup(dev))
> +		ls1_set_power_except(dev, *((int *)enable));
> +}
> +
> +/* enable cluster to enter the PCL10 state */
> +static void ls1_set_clusterpm(int enable)
> +{
> +	u32 val = 0;
> +
> +	if (enable)
> +		val = CCSR_SCFG_CLUSTERPMCR_WFIL2EN;
> +
> +	iowrite32be(val, ls1_pm_base.scfg + CCSR_SCFG_CLUSTERPMCR);
> +}
> +
> +static int ls1_suspend_enter(suspend_state_t state)
> +{
> +	int ret = 0;
> +
> +	ls1_set_powerdown();
> +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_ENABLE);
> +
> +	switch (state) {
> +	case PM_SUSPEND_STANDBY:
> +		flush_cache_louis();
> +		ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR,
> +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ,
> +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ);
> +		cpu_do_idle();
> +		break;
> +	case PM_SUSPEND_MEM:
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_DISABLE);
> +
> +	return ret;
> +}
> +
> +static int ls1_suspend_valid(suspend_state_t state)
> +{
> +	if (state == PM_SUSPEND_STANDBY)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static int ls1_suspend_begin(suspend_state_t state)
> +{
> +	int res = -EINVAL;

The convention of return variable is 'ret' than 'res'.

> +
> +	ippdexpcr0 = 0;
> +	ippdexpcr1 = 0;
> +
> +	if (state == PM_SUSPEND_STANDBY)
> +		res = ls1_pm_iomap(state);
> +
> +	if (!res)
> +		dpm_for_each_dev(NULL, ls1_set_wakeup_device);
> +
> +	return res;
> +}
> +
> +static void ls1_suspend_end(void)
> +{
> +	ls1_pm_uniomap(PM_SUSPEND_STANDBY);
> +}

The .begin() and .end() hooks will be called each time that system
enters/exits suspend, right?  Are you sure the setup you're doing in
ls1_suspend_begin() and ls1_suspend_end() is needed each time?  Or they
only need to be done in ls1_pm_init() for once?

> +
> +static const struct platform_suspend_ops ls1_suspend_ops = {
> +	.valid = ls1_suspend_valid,
> +	.enter = ls1_suspend_enter,
> +	.begin = ls1_suspend_begin,
> +	.end = ls1_suspend_end,
> +};
> +
> +static const struct of_device_id rcpm_matches[] = {
> +	{
> +		.compatible = "fsl,ls1021a-rcpm",

Undocumented compatible.

> +	},
> +	{}
> +};
> +
> +static int __init ls1_pm_init(void)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +
> +	np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);

You are simply trying to find "fsl,ls1021a-rcpm" node, so why not just
use of_find_compatible_node() like you try to find "fsl,ls1021a-scfg"
in ls1_pm_iomap().

> +	if (!np) {
> +		pr_err("%s: can't find the rcpm node.\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ls1_pm_base.rcpm = of_iomap(np, 0);
> +	of_node_put(np);
> +	if (!ls1_pm_base.rcpm)
> +		return -ENOMEM;

Right.  Why cannot iomap of scfg be done here just like rcpm?

> +
> +	suspend_set_ops(&ls1_suspend_ops);
> +
> +	pr_info("Freescale Power Management Control Registered\n");
> +
> +	return 0;
> +}
> +arch_initcall(ls1_pm_init);

Bear it in mind that we're now in multi-platform world, where a single
kernel image will run multiple targets, LS1021A, IMX, Vybrid, and even
non-FSL SoCs.  You cannot use such initcall here, which will get the
function called on non-LS1021A platform.

Shawn
Dongsheng Wang Sept. 24, 2015, 9:28 a.m. UTC | #3
> > Add system STANDBY implement for ls1021 platform.
> >
> > Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
> > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> > ---
> > *v2*:
> > - Remove PSCI code. Just implement STANDBY in platform code.
> 
> Why stepping back?  We are encouraged to move to PSCI, aren't we?
> 

As Lorenzo said, PSCI v1.0 support will be in kernel v4.4. So I remove
psci for this patch.

> > diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
> > index fb689d8..d7a2d1d 100644
> > --- a/arch/arm/mach-imx/Makefile
> > +++ b/arch/arm/mach-imx/Makefile
> > @@ -90,6 +90,7 @@ ifeq ($(CONFIG_SUSPEND),y)
> >  AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
> >  obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
> >  obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
> > +obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o
> >  endif
> >  obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
> >
> > diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
> > new file mode 100644
> > index 0000000..90775cf
> > --- /dev/null
> > +++ b/arch/arm/mach-imx/pm-ls1.c
> > @@ -0,0 +1,225 @@
> > +/*
> > + * Support Power Management Control for LS1
> > + *
> > + * Copyright 2015 Freescale Semiconductor Inc.
> > + *
> > + * This program is free software; you can redistribute	it and/or modify it
> > + * under  the terms of	the GNU General	 Public License as published by
> the
> > + * Free Software Foundation;  either version 2 of the  License, or (at your
> > + * option) any later version.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/of_address.h>
> > +#include <linux/suspend.h>
> > +
> > +#include <asm/cacheflush.h>
> > +#include <asm/suspend.h>
> > +
> > +#define FSL_SLEEP			0x1
> > +#define FSL_DEEP_SLEEP			0x2
> 
> Unused defines.
> 

This macro is for next feature deep sleep. I can remove it in this patch.

> > +
> > +#define CCSR_SCFG_CLUSTERPMCR		0x904
> > +#define CCSR_SCFG_CLUSTERPMCR_WFIL2EN	0x80000000
> > +#define CCSR_SCFG_CLUSTERPM_ENABLE	1
> > +#define CCSR_SCFG_CLUSTERPM_DISABLE	0
> > +
> > +#define CCSR_RCPM_POWMGTCSR		0x130
> > +#define CCSR_RCPM_POWMGTCSR_LPM20_REQ	0x00100000
> > +#define CCSR_RCPM_IPPDEXPCR0		0x140
> > +#define CCSR_RCPM_IPPDEXPCR1		0x144
> > +#define CCSR_RCPM_IPPDEXPCR1_OCRAM1	0x10000000
> > +
> > +#define SLEEP_ARRAY_SIZE		3
> 
> The name of the macro doesn't appealing.

Change to RCPM_WAKEUP_CELLS.

> > +
> > +static u32 ippdexpcr0, ippdexpcr1;
> 
> It makes more sense to have a structure holds all these variables and
> the following base addresses.
> 
> > +
> > +struct ls1_pm_baseaddr {
> > +	void __iomem *rcpm;
> > +	void __iomem *scfg;
> > +};
> > +
> > +static struct ls1_pm_baseaddr ls1_pm_base;
> > +
> > +static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set)
> > +{
> > +	u32 val;
> > +
> > +	val = ioread32be(addr);
> > +	val = (val & ~clear) | set;
> > +	iowrite32be(val, addr);
> > +}
> > +
> > +static int ls1_pm_iomap(suspend_state_t state)
> 
> How is argument 'state' used?
> 

It will be used for distinguish between STANDBY and MEM. Because MEM need more iomap.
In fact as you said we can move all of iomap to initialization function. Write this function
just for save memory in STANDBY, if we decision to remove ls1_pm_iomap() I can put all of iomap
to initialization function. :)

> > +{
> > +	struct device_node *np;
> > +	void *base;
> > +
> > +	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
> > +	if (!np) {
> > +		pr_err("Missing SCFG node in the device tree\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	base = of_iomap(np, 0);
> > +	of_node_put(np);
> > +	if (!base) {
> > +		pr_err("Couldn't map SCFG registers\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ls1_pm_base.scfg = base;
> > +
> > +	return 0;
> > +}
> > +
> > +static void ls1_pm_uniomap(suspend_state_t state)
> > +{
> > +	iounmap(ls1_pm_base.scfg);
> > +}
> > +
> > +/* set IP powerdown exception, make them work during sleep/deep sleep */
> > +static void ls1_set_powerdown(void)
> > +{
> > +	iowrite32be(ippdexpcr0, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
> > +	iowrite32be(ippdexpcr1,	ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
> > +}
> > +
> > +static void ls1_set_power_except(struct device *dev, int on)
> 
> How argument 'on' is used?
> 

Useless for now.

> > +{
> > +	int ret;
> > +	u32 value[SLEEP_ARRAY_SIZE];
> > +
> > +	/*
> > +	 * Get the values in the "rcpm-wakeup" property. There are three values.
> > +	 * The first points to the RCPM node, the second is the value of
> > +	 * the ippdexpcr0 register, and the third is the value of
> > +	 * the ippdexpcr1 register.
> > +	 */
> > +	ret = of_property_read_u32_array(dev->of_node, "rcpm-wakeup",
> > +					 value, SLEEP_ARRAY_SIZE);
> 
> The property should be documented in device tree bindings.  And you need
> a good reason for why these register values should be defined by device
> tree.
> 

Yes, this property binding is upstream. Please see
https://patchwork.kernel.org/patch/7254371/
https://patchwork.kernel.org/patch/7254381/  


> > +	if (ret) {
> > +		dev_err(dev, "%s: Can not find the \"sleep\" property.\n",
> > +			__func__);
> > +		return;
> > +	}
> > +
> > +	ippdexpcr0 |= value[1];
> > +	ippdexpcr1 |= value[2];
> > +
> > +	pr_debug("%s: set %s as a wakeup source", __func__,
> 
> When you have device pointer, you should use dev_dbg() instead of
> pr_debug().
> 

Thanks.

> > +		 dev->of_node->full_name);
> > +}
> > +
> > +static void ls1_set_wakeup_device(struct device *dev, void *enable)
> > +{
> > +	/* set each device which can act as wakeup source */
> > +	if (device_may_wakeup(dev))
> > +		ls1_set_power_except(dev, *((int *)enable));
> > +}
> > +
> > +/* enable cluster to enter the PCL10 state */
> > +static void ls1_set_clusterpm(int enable)
> > +{
> > +	u32 val = 0;
> > +
> > +	if (enable)
> > +		val = CCSR_SCFG_CLUSTERPMCR_WFIL2EN;
> > +
> > +	iowrite32be(val, ls1_pm_base.scfg + CCSR_SCFG_CLUSTERPMCR);
> > +}
> > +
> > +static int ls1_suspend_enter(suspend_state_t state)
> > +{
> > +	int ret = 0;
> > +
> > +	ls1_set_powerdown();
> > +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_ENABLE);
> > +
> > +	switch (state) {
> > +	case PM_SUSPEND_STANDBY:
> > +		flush_cache_louis();
> > +		ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR,
> > +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ,
> > +				    CCSR_RCPM_POWMGTCSR_LPM20_REQ);
> > +		cpu_do_idle();
> > +		break;
> > +	case PM_SUSPEND_MEM:
> > +	default:
> > +		ret = -EINVAL;
> > +		break;
> > +	}
> > +
> > +	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_DISABLE);
> > +
> > +	return ret;
> > +}
> > +
> > +static int ls1_suspend_valid(suspend_state_t state)
> > +{
> > +	if (state == PM_SUSPEND_STANDBY)
> > +		return 1;
> > +
> > +	return 0;
> > +}
> > +
> > +static int ls1_suspend_begin(suspend_state_t state)
> > +{
> > +	int res = -EINVAL;
> 
> The convention of return variable is 'ret' than 'res'.
> 

:)

> > +
> > +	ippdexpcr0 = 0;
> > +	ippdexpcr1 = 0;
> > +
> > +	if (state == PM_SUSPEND_STANDBY)
> > +		res = ls1_pm_iomap(state);
> > +
> > +	if (!res)
> > +		dpm_for_each_dev(NULL, ls1_set_wakeup_device);
> > +
> > +	return res;
> > +}
> > +
> > +static void ls1_suspend_end(void)
> > +{
> > +	ls1_pm_uniomap(PM_SUSPEND_STANDBY);
> > +}
> 
> The .begin() and .end() hooks will be called each time that system
> enters/exits suspend, right?  Are you sure the setup you're doing in
> ls1_suspend_begin() and ls1_suspend_end() is needed each time?  Or they
> only need to be done in ls1_pm_init() for once?
> 

Please see my above comments.

> > +
> > +static const struct platform_suspend_ops ls1_suspend_ops = {
> > +	.valid = ls1_suspend_valid,
> > +	.enter = ls1_suspend_enter,
> > +	.begin = ls1_suspend_begin,
> > +	.end = ls1_suspend_end,
> > +};
> > +
> > +static const struct of_device_id rcpm_matches[] = {
> > +	{
> > +		.compatible = "fsl,ls1021a-rcpm",
> 
> Undocumented compatible.

Please see my above comments.

> 
> > +	},
> > +	{}
> > +};
> > +
> > +static int __init ls1_pm_init(void)
> > +{
> > +	const struct of_device_id *match;
> > +	struct device_node *np;
> > +
> > +	np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
> 
> You are simply trying to find "fsl,ls1021a-rcpm" node, so why not just
> use of_find_compatible_node() like you try to find "fsl,ls1021a-scfg"
> in ls1_pm_iomap().
> 

Yes, thanks.

> > +	if (!np) {
> > +		pr_err("%s: can't find the rcpm node.\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	ls1_pm_base.rcpm = of_iomap(np, 0);
> > +	of_node_put(np);
> > +	if (!ls1_pm_base.rcpm)
> > +		return -ENOMEM;
> 
> Right.  Why cannot iomap of scfg be done here just like rcpm?
> 
> > +
> > +	suspend_set_ops(&ls1_suspend_ops);
> > +
> > +	pr_info("Freescale Power Management Control Registered\n");
> > +
> > +	return 0;
> > +}
> > +arch_initcall(ls1_pm_init);
> 
> Bear it in mind that we're now in multi-platform world, where a single
> kernel image will run multiple targets, LS1021A, IMX, Vybrid, and even
> non-FSL SoCs.  You cannot use such initcall here, which will get the
> function called on non-LS1021A platform.

We can call this function directly in LS1021A platform, just call it in
.init_machine, right?

Regards,
-Dongsheng
Shawn Guo Sept. 24, 2015, 11:40 a.m. UTC | #4
On Thu, Sep 24, 2015 at 09:28:31AM +0000, Wang Dongsheng wrote:
> > > Add system STANDBY implement for ls1021 platform.
> > >
> > > Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
> > > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> > > ---
> > > *v2*:
> > > - Remove PSCI code. Just implement STANDBY in platform code.
> > 
> > Why stepping back?  We are encouraged to move to PSCI, aren't we?
> > 
> 
> As Lorenzo said, PSCI v1.0 support will be in kernel v4.4. So I remove
> psci for this patch.

v4.4 is not far away, and we should probably wait instead of pushing
the code that needs to be revised quite soon.

Shawn
Dongsheng Wang Sept. 25, 2015, 2:40 a.m. UTC | #5
> > > > Add system STANDBY implement for ls1021 platform.
> > > >
> > > > Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
> > > > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> > > > ---
> > > > *v2*:
> > > > - Remove PSCI code. Just implement STANDBY in platform code.
> > >
> > > Why stepping back?  We are encouraged to move to PSCI, aren't we?
> > >
> >
> > As Lorenzo said, PSCI v1.0 support will be in kernel v4.4. So I remove
> > psci for this patch.
> 
> v4.4 is not far away, and we should probably wait instead of pushing
> the code that needs to be revised quite soon.
> 

Ok, thanks for your review.

Regards,
-Dongsheng
diff mbox

Patch

diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index fb689d8..d7a2d1d 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -90,6 +90,7 @@  ifeq ($(CONFIG_SUSPEND),y)
 AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
 obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
+obj-$(CONFIG_SOC_LS1021A) += pm-ls1.o
 endif
 obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
 
diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
new file mode 100644
index 0000000..90775cf
--- /dev/null
+++ b/arch/arm/mach-imx/pm-ls1.c
@@ -0,0 +1,225 @@ 
+/*
+ * Support Power Management Control for LS1
+ *
+ * Copyright 2015 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute	it and/or modify it
+ * under  the terms of	the GNU General	 Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/suspend.h>
+
+#include <asm/cacheflush.h>
+#include <asm/suspend.h>
+
+#define FSL_SLEEP			0x1
+#define FSL_DEEP_SLEEP			0x2
+
+#define CCSR_SCFG_CLUSTERPMCR		0x904
+#define CCSR_SCFG_CLUSTERPMCR_WFIL2EN	0x80000000
+#define CCSR_SCFG_CLUSTERPM_ENABLE	1
+#define CCSR_SCFG_CLUSTERPM_DISABLE	0
+
+#define CCSR_RCPM_POWMGTCSR		0x130
+#define CCSR_RCPM_POWMGTCSR_LPM20_REQ	0x00100000
+#define CCSR_RCPM_IPPDEXPCR0		0x140
+#define CCSR_RCPM_IPPDEXPCR1		0x144
+#define CCSR_RCPM_IPPDEXPCR1_OCRAM1	0x10000000
+
+#define SLEEP_ARRAY_SIZE		3
+
+static u32 ippdexpcr0, ippdexpcr1;
+
+struct ls1_pm_baseaddr {
+	void __iomem *rcpm;
+	void __iomem *scfg;
+};
+
+static struct ls1_pm_baseaddr ls1_pm_base;
+
+static inline void ls1_clrsetbits_be32(void __iomem *addr, u32 clear, u32 set)
+{
+	u32 val;
+
+	val = ioread32be(addr);
+	val = (val & ~clear) | set;
+	iowrite32be(val, addr);
+}
+
+static int ls1_pm_iomap(suspend_state_t state)
+{
+	struct device_node *np;
+	void *base;
+
+	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
+	if (!np) {
+		pr_err("Missing SCFG node in the device tree\n");
+		return -EINVAL;
+	}
+
+	base = of_iomap(np, 0);
+	of_node_put(np);
+	if (!base) {
+		pr_err("Couldn't map SCFG registers\n");
+		return -EINVAL;
+	}
+
+	ls1_pm_base.scfg = base;
+
+	return 0;
+}
+
+static void ls1_pm_uniomap(suspend_state_t state)
+{
+	iounmap(ls1_pm_base.scfg);
+}
+
+/* set IP powerdown exception, make them work during sleep/deep sleep */
+static void ls1_set_powerdown(void)
+{
+	iowrite32be(ippdexpcr0, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
+	iowrite32be(ippdexpcr1,	ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
+}
+
+static void ls1_set_power_except(struct device *dev, int on)
+{
+	int ret;
+	u32 value[SLEEP_ARRAY_SIZE];
+
+	/*
+	 * Get the values in the "rcpm-wakeup" property. There are three values.
+	 * The first points to the RCPM node, the second is the value of
+	 * the ippdexpcr0 register, and the third is the value of
+	 * the ippdexpcr1 register.
+	 */
+	ret = of_property_read_u32_array(dev->of_node, "rcpm-wakeup",
+					 value, SLEEP_ARRAY_SIZE);
+	if (ret) {
+		dev_err(dev, "%s: Can not find the \"sleep\" property.\n",
+			__func__);
+		return;
+	}
+
+	ippdexpcr0 |= value[1];
+	ippdexpcr1 |= value[2];
+
+	pr_debug("%s: set %s as a wakeup source", __func__,
+		 dev->of_node->full_name);
+}
+
+static void ls1_set_wakeup_device(struct device *dev, void *enable)
+{
+	/* set each device which can act as wakeup source */
+	if (device_may_wakeup(dev))
+		ls1_set_power_except(dev, *((int *)enable));
+}
+
+/* enable cluster to enter the PCL10 state */
+static void ls1_set_clusterpm(int enable)
+{
+	u32 val = 0;
+
+	if (enable)
+		val = CCSR_SCFG_CLUSTERPMCR_WFIL2EN;
+
+	iowrite32be(val, ls1_pm_base.scfg + CCSR_SCFG_CLUSTERPMCR);
+}
+
+static int ls1_suspend_enter(suspend_state_t state)
+{
+	int ret = 0;
+
+	ls1_set_powerdown();
+	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_ENABLE);
+
+	switch (state) {
+	case PM_SUSPEND_STANDBY:
+		flush_cache_louis();
+		ls1_clrsetbits_be32(ls1_pm_base.rcpm + CCSR_RCPM_POWMGTCSR,
+				    CCSR_RCPM_POWMGTCSR_LPM20_REQ,
+				    CCSR_RCPM_POWMGTCSR_LPM20_REQ);
+		cpu_do_idle();
+		break;
+	case PM_SUSPEND_MEM:
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	ls1_set_clusterpm(CCSR_SCFG_CLUSTERPM_DISABLE);
+
+	return ret;
+}
+
+static int ls1_suspend_valid(suspend_state_t state)
+{
+	if (state == PM_SUSPEND_STANDBY)
+		return 1;
+
+	return 0;
+}
+
+static int ls1_suspend_begin(suspend_state_t state)
+{
+	int res = -EINVAL;
+
+	ippdexpcr0 = 0;
+	ippdexpcr1 = 0;
+
+	if (state == PM_SUSPEND_STANDBY)
+		res = ls1_pm_iomap(state);
+
+	if (!res)
+		dpm_for_each_dev(NULL, ls1_set_wakeup_device);
+
+	return res;
+}
+
+static void ls1_suspend_end(void)
+{
+	ls1_pm_uniomap(PM_SUSPEND_STANDBY);
+}
+
+static const struct platform_suspend_ops ls1_suspend_ops = {
+	.valid = ls1_suspend_valid,
+	.enter = ls1_suspend_enter,
+	.begin = ls1_suspend_begin,
+	.end = ls1_suspend_end,
+};
+
+static const struct of_device_id rcpm_matches[] = {
+	{
+		.compatible = "fsl,ls1021a-rcpm",
+	},
+	{}
+};
+
+static int __init ls1_pm_init(void)
+{
+	const struct of_device_id *match;
+	struct device_node *np;
+
+	np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
+	if (!np) {
+		pr_err("%s: can't find the rcpm node.\n", __func__);
+		return -EINVAL;
+	}
+
+	ls1_pm_base.rcpm = of_iomap(np, 0);
+	of_node_put(np);
+	if (!ls1_pm_base.rcpm)
+		return -ENOMEM;
+
+	suspend_set_ops(&ls1_suspend_ops);
+
+	pr_info("Freescale Power Management Control Registered\n");
+
+	return 0;
+}
+arch_initcall(ls1_pm_init);