diff options
author | Geert Uytterhoeven | 2014-12-03 14:41:45 +0100 |
---|---|---|
committer | Simon Horman | 2015-01-15 08:38:14 +0900 |
commit | 2173fc7cb681c38b9e5bc526211045caecf96e44 (patch) | |
tree | fcb881723f9258e8236677fc0fe2e95c3c9c7be0 /arch/arm/mach-shmobile/pm-rmobile.c | |
parent | 25717b857360760755b83b4e606d61e1fc38552f (diff) |
ARM: shmobile: R-Mobile: Add DT support for PM domains
Populate the PM domains from DT, and provide support to hook up devices
to their respective PM domain.
The always-on power area (e.g. C5 on r8a7740) is created as a PM domain
without software control, to allow Run-Time management of module clocks
for hardware blocks inside this area.
Special cases like PM domains containing CPUs, the console device, or
Coresight-ETM, are handled by scanning the DT topology.
As long as the ARM debug/perf code doesn't use resource management with
runtime PM support, the power area containing Coresight-ETM (e.g. D4 on
r8a7740) must be kept powered to avoid a crash during resume from s2ram
(dbg_cpu_pm_notify() calls reset_ctrl_regs() unconditionally, causing an
undefined instruction oops).
Initialization is done from core_initcall(), as the
"renesas,intc-irqpin" driver uses postcore_initcall().
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Diffstat (limited to 'arch/arm/mach-shmobile/pm-rmobile.c')
-rw-r--r-- | arch/arm/mach-shmobile/pm-rmobile.c | 217 |
1 files changed, 215 insertions, 2 deletions
diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c index a4fcd2cae718..85a7fdd9823b 100644 --- a/arch/arm/mach-shmobile/pm-rmobile.c +++ b/arch/arm/mach-shmobile/pm-rmobile.c @@ -3,6 +3,7 @@ * * Copyright (C) 2012 Renesas Solutions Corp. * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Copyright (C) 2014 Glider bvba * * based on pm-sh7372.c * Copyright (C) 2011 Magnus Damm @@ -13,10 +14,16 @@ */ #include <linux/console.h> #include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_clock.h> +#include <linux/slab.h> + #include <asm/io.h> + #include "pm-rmobile.h" /* SYSC */ @@ -30,8 +37,12 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd) { struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); - unsigned int mask = 1 << rmobile_pd->bit_shift; + unsigned int mask; + + if (rmobile_pd->bit_shift == ~0) + return -EBUSY; + mask = 1 << rmobile_pd->bit_shift; if (rmobile_pd->suspend) { int ret = rmobile_pd->suspend(); @@ -61,10 +72,14 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd) static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd, bool do_resume) { - unsigned int mask = 1 << rmobile_pd->bit_shift; + unsigned int mask; unsigned int retry_count; int ret = 0; + if (rmobile_pd->bit_shift == ~0) + return 0; + + mask = 1 << rmobile_pd->bit_shift; if (__raw_readl(rmobile_pd->base + PSTR) & mask) goto out; @@ -148,6 +163,8 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) __rmobile_pd_power_up(rmobile_pd, false); } +#ifdef CONFIG_ARCH_SHMOBILE_LEGACY + void rmobile_init_domains(struct rmobile_pm_domain domains[], int num) { int j; @@ -180,3 +197,199 @@ void rmobile_add_devices_to_domains(struct pm_domain_device data[], rmobile_add_device_to_domain_td(data[j].domain_name, data[j].pdev, &latencies); } + +#else /* !CONFIG_ARCH_SHMOBILE_LEGACY */ + +static int rmobile_pd_suspend_cpu(void) +{ + /* + * This domain contains the CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + return -EBUSY; +} + +static int rmobile_pd_suspend_console(void) +{ + /* + * Serial consoles make use of SCIF hardware located in this domain, + * hence keep the power domain on if "no_console_suspend" is set. + */ + return console_suspend_enabled ? 0 : -EBUSY; +} + +static int rmobile_pd_suspend_debug(void) +{ + /* + * This domain contains the Coresight-ETM hardware block and + * therefore it should only be turned off if the debug module is + * not in use. + */ + return -EBUSY; +} + +#define MAX_NUM_CPU_PDS 8 + +static unsigned int num_cpu_pds __initdata; +static struct device_node *cpu_pds[MAX_NUM_CPU_PDS] __initdata; +static struct device_node *console_pd __initdata; +static struct device_node *debug_pd __initdata; + +static void __init get_special_pds(void) +{ + struct device_node *np, *pd; + unsigned int i; + + /* PM domains containing CPUs */ + for_each_node_by_type(np, "cpu") { + pd = of_parse_phandle(np, "power-domains", 0); + if (!pd) + continue; + + for (i = 0; i < num_cpu_pds; i++) + if (pd == cpu_pds[i]) + break; + + if (i < num_cpu_pds) { + of_node_put(pd); + continue; + } + + if (num_cpu_pds == MAX_NUM_CPU_PDS) { + pr_warn("Too many CPU PM domains\n"); + of_node_put(pd); + continue; + } + + cpu_pds[num_cpu_pds++] = pd; + } + + /* PM domain containing console */ + if (of_stdout) + console_pd = of_parse_phandle(of_stdout, "power-domains", 0); + + /* PM domain containing Coresight-ETM */ + np = of_find_compatible_node(NULL, NULL, "arm,coresight-etm3x"); + if (np) { + debug_pd = of_parse_phandle(np, "power-domains", 0); + of_node_put(np); + } +} + +static void __init put_special_pds(void) +{ + unsigned int i; + + for (i = 0; i < num_cpu_pds; i++) + of_node_put(cpu_pds[i]); + of_node_put(console_pd); + of_node_put(debug_pd); +} + +static bool __init pd_contains_cpu(const struct device_node *pd) +{ + unsigned int i; + + for (i = 0; i < num_cpu_pds; i++) + if (pd == cpu_pds[i]) + return true; + + return false; +} + +static void __init rmobile_setup_pm_domain(struct device_node *np, + struct rmobile_pm_domain *pd) +{ + const char *name = pd->genpd.name; + + if (pd_contains_cpu(np)) { + pr_debug("PM domain %s contains CPU\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_cpu; + } else if (np == console_pd) { + pr_debug("PM domain %s contains serial console\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_console; + } else if (np == debug_pd) { + pr_debug("PM domain %s contains Coresight-ETM\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_debug; + } + + rmobile_init_pm_domain(pd); +} + +static int __init rmobile_add_pm_domains(void __iomem *base, + struct device_node *parent, + struct generic_pm_domain *genpd_parent) +{ + struct device_node *np; + + for_each_child_of_node(parent, np) { + struct rmobile_pm_domain *pd; + u32 idx = ~0; + + if (of_property_read_u32(np, "reg", &idx)) { + /* always-on domain */ + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->genpd.name = np->name; + pd->base = base; + pd->bit_shift = idx; + + rmobile_setup_pm_domain(np, pd); + if (genpd_parent) + pm_genpd_add_subdomain(genpd_parent, &pd->genpd); + of_genpd_add_provider_simple(np, &pd->genpd); + + rmobile_add_pm_domains(base, np, &pd->genpd); + } + return 0; +} + +static int __init rmobile_init_pm_domains(void) +{ + struct device_node *np, *pmd; + bool scanned = false; + void __iomem *base; + int ret = 0; + + for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { + base = of_iomap(np, 0); + if (!base) { + pr_warn("%s cannot map reg 0\n", np->full_name); + continue; + } + + pmd = of_get_child_by_name(np, "pm-domains"); + if (!pmd) { + pr_warn("%s lacks pm-domains node\n", np->full_name); + continue; + } + + if (!scanned) { + /* Find PM domains containing special blocks */ + get_special_pds(); + scanned = true; + } + + ret = rmobile_add_pm_domains(base, pmd, NULL); + of_node_put(pmd); + if (ret) { + of_node_put(np); + break; + } + } + + put_special_pds(); + + return ret; +} + +core_initcall(rmobile_init_pm_domains); + +#endif /* !CONFIG_ARCH_SHMOBILE_LEGACY */ |