Message ID | 1420562233-2015-8-git-send-email-mathieu.poirier@linaro.org |
---|---|
State | New |
Headers | show |
On 7 January 2015 at 04:39, Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> wrote: > Hi Mathieu, > > On Tue, Jan 06, 2015 at 04:37:11PM +0000, mathieu.poirier@linaro.org wrote: > > [...] > >> diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c >> index f61158c6ce71..4ff1009f2d16 100644 >> --- a/arch/arm/mach-vexpress/spc.c >> +++ b/arch/arm/mach-vexpress/spc.c >> @@ -28,6 +28,8 @@ >> #include <linux/pm_opp.h> >> #include <linux/slab.h> >> #include <linux/semaphore.h> >> +#include <linux/of_platform.h> >> +#include <linux/pm_domain.h> >> >> #include <asm/cacheflush.h> >> >> @@ -92,6 +94,9 @@ >> #define STAT_ERR(type) ((1 << 1) << (type << 2)) >> #define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type)) >> >> +static atomic_t gpd_A7_cluster_on = ATOMIC_INIT(0); >> +static atomic_t gpd_A15_cluster_on = ATOMIC_INIT(0); >> + >> struct ve_spc_opp { >> unsigned long freq; >> unsigned long u_volt; >> @@ -209,12 +214,19 @@ void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr) >> */ >> void ve_spc_powerdown(u32 cluster, bool enable) >> { >> + bool is_a15; >> u32 pwdrn_reg; >> >> if (cluster >= MAX_CLUSTERS) >> return; >> >> - pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN; >> + is_a15 = cluster_is_a15(cluster); >> + if (is_a15 && atomic_read(&gpd_A15_cluster_on)) >> + return; >> + else if (!is_a15 && atomic_read(&gpd_A7_cluster_on)) >> + return; >> + >> + pwdrn_reg = is_a15 ? A15_PWRDN_EN : A7_PWRDN_EN; >> writel_relaxed(enable, info->baseaddr + pwdrn_reg); > > I do not like this, for multiple reasons. > > (1) It might not do what you want. I am not sure that nuking the power > down request is safe. I have to check the power controller behaviour > when a core is going through power down sequence but the PWRDN_EN > bit is not set. You might end up with cores that are just being > reset on pending IRQ (defeating your purpose) or stuck forever. I understand your concerns. > (2) It must be done by attaching the power domains to CPUidle states. > Idle states are automagically disabled when the domain is turned on, it > is cleaner, and more robust than what you do here. I like that idea. > On the downside, > we have to work together to add power domain information to DT idle > states specifications/bindings. > > (see pm_genpd_attach_cpuidle() drivers/base/power/domain.c) I definitely will. > > (3) I do not like the is_a15 comparisons, I think this can be done with > cluster indexing the atomic variable, but since you are removing > this code ;-) it does not really matter. Agreed. > >> } >> >> @@ -447,6 +459,116 @@ static int ve_init_opp_table(struct device *cpu_dev) >> return ret; >> } >> >> +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF >> +static int scp_power_on_pd_A7(struct generic_pm_domain *domain) >> +{ >> + /* >> + * Simply tell mcpm we need power. Nothing else needs to be done >> + * as CPUs in the cluster are already powered when we reach this point. >> + */ >> + atomic_set(&gpd_A7_cluster_on, 1); >> + return 0; >> +} >> + >> +static int scp_power_off_pd_A7(struct generic_pm_domain *domain) >> +{ >> + atomic_set(&gpd_A7_cluster_on, 0); >> + return 0; >> +} >> + >> +static int scp_power_on_pd_A15(struct generic_pm_domain *domain) >> +{ >> + /* >> + * Simply tell mcpm we need power. Nothing else needs to be done >> + * as CPUs in the cluster are already powered when we reach this point. >> + */ >> + atomic_set(&gpd_A15_cluster_on, 1); >> + return 0; >> +} >> + >> +static int scp_power_off_pd_A15(struct generic_pm_domain *domain) >> +{ >> + atomic_set(&gpd_A15_cluster_on, 0); >> + return 0; >> +} >> + >> +static int (*const scp_power_fct[MAX_CLUSTERS * 2]) >> + (struct generic_pm_domain *domain) = { >> + scp_power_on_pd_A15, scp_power_off_pd_A15, >> + scp_power_on_pd_A7, scp_power_off_pd_A7}; > > I think you can remove the functions above and the corresponding atomic > variables, see my comment above. > >> +static void scp_init_pd(struct generic_pm_domain *pd, >> + struct device_node *np, >> + struct platform_device *pdev, int cluster) >> +{ >> + char name[50]; >> + int index = cluster * 2; >> + >> + snprintf(name, sizeof(name), "%s-%d", np->name, cluster); >> + >> + pd->name = kstrdup(name, GFP_KERNEL); >> + pd->power_on = scp_power_fct[index]; >> + pd->power_off = scp_power_fct[index + 1]; >> + platform_set_drvdata(pdev, pd); >> + >> + pm_genpd_init(pd, NULL, true); >> + pm_genpd_poweron(pd); >> +} >> + >> +static __init int ve_spc_gpd_init(void) >> +{ >> + struct genpd_onecell_data *data; >> + struct generic_pm_domain *pd, **cluster_pds; >> + struct platform_device *pdev; >> + struct device *dev; >> + struct device_node *np; >> + int count; >> + >> + np = of_find_compatible_node(NULL, NULL, >> + "arm,vexpress-power-controller"); > > See the bindings discussions, there is not such a thing as > vexpress-power-controller. I looked at other examples that prefixed the "power-controller" part with something specific. In thinking further on it "arm,power-controller,v2p-ca15_a7" is likely a better choice. > >> + if (!np) >> + return -EINVAL; >> + >> + cluster_pds = kzalloc(sizeof(struct generic_pm_domain *) * MAX_CLUSTERS, >> + GFP_KERNEL); >> + if (!cluster_pds) >> + goto err_cluster_kzalloc; > > You are freeing a pointer that failed to be allocated. > >> + >> + data = kzalloc(sizeof(struct genpd_onecell_data), GFP_KERNEL); >> + if (!data) >> + goto err_data; > > Mmm...is that the label you want to jump to ? it fails to put the OF > node. > >> + >> + pdev = of_find_device_by_node(np); >> + dev = &pdev->dev; >> + >> + for (count = 0; count < MAX_CLUSTERS; count++) { >> + pd = kzalloc(sizeof(struct generic_pm_domain), GFP_KERNEL); >> + if (!pd) >> + goto err_pd_kzalloc; > > This label does not free the data pointer. I think you should rework > the error exit paths. > >> + scp_init_pd(pd, np, pdev, count); >> + cluster_pds[count] = pd; >> + } >> + >> + data->num_domains = count; >> + data->domains = cluster_pds; >> + >> + of_genpd_add_provider_onecell(np, data); >> + return 0; >> + >> +err_pd_kzalloc: >> + while (--count >= 0) >> + kfree(cluster_pds[count]); >> + >> +err_cluster_kzalloc: >> + of_node_put(np); >> +err_data: >> + kfree(cluster_pds); >> + return -ENOMEM; >> + >> +} >> +arch_initcall(ve_spc_gpd_init); > > It should not be an arch initcall, call it from spc_init, and make it an > empty static inline if !CONFIG_PM_GENERIC_DOMAINS_OF, eg: That was my first intention but calling @platform_set_drvdata(); on the node returned by @of_find_compatible_node() crashed the system. I will investigate further. > > static inline int ve_spc_gpd_init(void) > { > return -ENOTSUPP; > } > > Lorenzo > >> +#endif >> + >> int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) >> { >> int ret; >> -- >> 1.9.1 >> >>
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index d6b16d9a7838..f90809bbdaa7 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -21,6 +21,7 @@ menuconfig ARCH_VEXPRESS select VEXPRESS_CONFIG select VEXPRESS_SYSCFG select MFD_VEXPRESS_SYSREG + select PM_GENERIC_DOMAINS if PM help This option enables support for systems using Cortex processor based ARM core and logic (FPGA) tiles on the Versatile Express motherboard, diff --git a/arch/arm/mach-vexpress/spc.c b/arch/arm/mach-vexpress/spc.c index f61158c6ce71..4ff1009f2d16 100644 --- a/arch/arm/mach-vexpress/spc.c +++ b/arch/arm/mach-vexpress/spc.c @@ -28,6 +28,8 @@ #include <linux/pm_opp.h> #include <linux/slab.h> #include <linux/semaphore.h> +#include <linux/of_platform.h> +#include <linux/pm_domain.h> #include <asm/cacheflush.h> @@ -92,6 +94,9 @@ #define STAT_ERR(type) ((1 << 1) << (type << 2)) #define RESPONSE_MASK(type) (STAT_COMPLETE(type) | STAT_ERR(type)) +static atomic_t gpd_A7_cluster_on = ATOMIC_INIT(0); +static atomic_t gpd_A15_cluster_on = ATOMIC_INIT(0); + struct ve_spc_opp { unsigned long freq; unsigned long u_volt; @@ -209,12 +214,19 @@ void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr) */ void ve_spc_powerdown(u32 cluster, bool enable) { + bool is_a15; u32 pwdrn_reg; if (cluster >= MAX_CLUSTERS) return; - pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN; + is_a15 = cluster_is_a15(cluster); + if (is_a15 && atomic_read(&gpd_A15_cluster_on)) + return; + else if (!is_a15 && atomic_read(&gpd_A7_cluster_on)) + return; + + pwdrn_reg = is_a15 ? A15_PWRDN_EN : A7_PWRDN_EN; writel_relaxed(enable, info->baseaddr + pwdrn_reg); } @@ -447,6 +459,116 @@ static int ve_init_opp_table(struct device *cpu_dev) return ret; } +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF +static int scp_power_on_pd_A7(struct generic_pm_domain *domain) +{ + /* + * Simply tell mcpm we need power. Nothing else needs to be done + * as CPUs in the cluster are already powered when we reach this point. + */ + atomic_set(&gpd_A7_cluster_on, 1); + return 0; +} + +static int scp_power_off_pd_A7(struct generic_pm_domain *domain) +{ + atomic_set(&gpd_A7_cluster_on, 0); + return 0; +} + +static int scp_power_on_pd_A15(struct generic_pm_domain *domain) +{ + /* + * Simply tell mcpm we need power. Nothing else needs to be done + * as CPUs in the cluster are already powered when we reach this point. + */ + atomic_set(&gpd_A15_cluster_on, 1); + return 0; +} + +static int scp_power_off_pd_A15(struct generic_pm_domain *domain) +{ + atomic_set(&gpd_A15_cluster_on, 0); + return 0; +} + +static int (*const scp_power_fct[MAX_CLUSTERS * 2]) + (struct generic_pm_domain *domain) = { + scp_power_on_pd_A15, scp_power_off_pd_A15, + scp_power_on_pd_A7, scp_power_off_pd_A7}; + +static void scp_init_pd(struct generic_pm_domain *pd, + struct device_node *np, + struct platform_device *pdev, int cluster) +{ + char name[50]; + int index = cluster * 2; + + snprintf(name, sizeof(name), "%s-%d", np->name, cluster); + + pd->name = kstrdup(name, GFP_KERNEL); + pd->power_on = scp_power_fct[index]; + pd->power_off = scp_power_fct[index + 1]; + platform_set_drvdata(pdev, pd); + + pm_genpd_init(pd, NULL, true); + pm_genpd_poweron(pd); +} + +static __init int ve_spc_gpd_init(void) +{ + struct genpd_onecell_data *data; + struct generic_pm_domain *pd, **cluster_pds; + struct platform_device *pdev; + struct device *dev; + struct device_node *np; + int count; + + np = of_find_compatible_node(NULL, NULL, + "arm,vexpress-power-controller"); + if (!np) + return -EINVAL; + + cluster_pds = kzalloc(sizeof(struct generic_pm_domain *) * MAX_CLUSTERS, + GFP_KERNEL); + if (!cluster_pds) + goto err_cluster_kzalloc; + + data = kzalloc(sizeof(struct genpd_onecell_data), GFP_KERNEL); + if (!data) + goto err_data; + + pdev = of_find_device_by_node(np); + dev = &pdev->dev; + + for (count = 0; count < MAX_CLUSTERS; count++) { + pd = kzalloc(sizeof(struct generic_pm_domain), GFP_KERNEL); + if (!pd) + goto err_pd_kzalloc; + scp_init_pd(pd, np, pdev, count); + cluster_pds[count] = pd; + } + + data->num_domains = count; + data->domains = cluster_pds; + + of_genpd_add_provider_onecell(np, data); + return 0; + +err_pd_kzalloc: + while (--count >= 0) + kfree(cluster_pds[count]); + +err_cluster_kzalloc: + of_node_put(np); +err_data: + kfree(cluster_pds); + return -ENOMEM; + +} +arch_initcall(ve_spc_gpd_init); +#endif + int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq) { int ret;