diff options
author | Geert Uytterhoeven <geert+renesas@glider.be> | 2014-12-03 08:41:45 -0500 |
---|---|---|
committer | Simon Horman <horms+renesas@verge.net.au> | 2015-01-14 18:38:14 -0500 |
commit | 2173fc7cb681c38b9e5bc526211045caecf96e44 (patch) | |
tree | fcb881723f9258e8236677fc0fe2e95c3c9c7be0 | |
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>
-rw-r--r-- | arch/arm/mach-shmobile/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm-rmobile.c | 217 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm-rmobile.h | 2 |
3 files changed, 218 insertions, 4 deletions
diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index d107b9386bda..894c68760060 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig | |||
@@ -7,6 +7,7 @@ config PM_RCAR | |||
7 | 7 | ||
8 | config PM_RMOBILE | 8 | config PM_RMOBILE |
9 | bool | 9 | bool |
10 | select PM_GENERIC_DOMAINS | ||
10 | 11 | ||
11 | config ARCH_RCAR_GEN1 | 12 | config ARCH_RCAR_GEN1 |
12 | bool | 13 | bool |
@@ -23,7 +24,7 @@ config ARCH_RCAR_GEN2 | |||
23 | 24 | ||
24 | config ARCH_RMOBILE | 25 | config ARCH_RMOBILE |
25 | bool | 26 | bool |
26 | select PM_RMOBILE if PM && !ARCH_SHMOBILE_MULTI | 27 | select PM_RMOBILE if PM |
27 | select SYS_SUPPORTS_SH_CMT | 28 | select SYS_SUPPORTS_SH_CMT |
28 | select SYS_SUPPORTS_SH_TMU | 29 | select SYS_SUPPORTS_SH_TMU |
29 | 30 | ||
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 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2012 Renesas Solutions Corp. | 4 | * Copyright (C) 2012 Renesas Solutions Corp. |
5 | * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 5 | * Copyright (C) 2012 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
6 | * Copyright (C) 2014 Glider bvba | ||
6 | * | 7 | * |
7 | * based on pm-sh7372.c | 8 | * based on pm-sh7372.c |
8 | * Copyright (C) 2011 Magnus Damm | 9 | * Copyright (C) 2011 Magnus Damm |
@@ -13,10 +14,16 @@ | |||
13 | */ | 14 | */ |
14 | #include <linux/console.h> | 15 | #include <linux/console.h> |
15 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
17 | #include <linux/of.h> | ||
18 | #include <linux/of_address.h> | ||
19 | #include <linux/of_platform.h> | ||
16 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
17 | #include <linux/pm.h> | 21 | #include <linux/pm.h> |
18 | #include <linux/pm_clock.h> | 22 | #include <linux/pm_clock.h> |
23 | #include <linux/slab.h> | ||
24 | |||
19 | #include <asm/io.h> | 25 | #include <asm/io.h> |
26 | |||
20 | #include "pm-rmobile.h" | 27 | #include "pm-rmobile.h" |
21 | 28 | ||
22 | /* SYSC */ | 29 | /* SYSC */ |
@@ -30,8 +37,12 @@ | |||
30 | static int rmobile_pd_power_down(struct generic_pm_domain *genpd) | 37 | static int rmobile_pd_power_down(struct generic_pm_domain *genpd) |
31 | { | 38 | { |
32 | struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); | 39 | struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); |
33 | unsigned int mask = 1 << rmobile_pd->bit_shift; | 40 | unsigned int mask; |
41 | |||
42 | if (rmobile_pd->bit_shift == ~0) | ||
43 | return -EBUSY; | ||
34 | 44 | ||
45 | mask = 1 << rmobile_pd->bit_shift; | ||
35 | if (rmobile_pd->suspend) { | 46 | if (rmobile_pd->suspend) { |
36 | int ret = rmobile_pd->suspend(); | 47 | int ret = rmobile_pd->suspend(); |
37 | 48 | ||
@@ -61,10 +72,14 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd) | |||
61 | static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd, | 72 | static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd, |
62 | bool do_resume) | 73 | bool do_resume) |
63 | { | 74 | { |
64 | unsigned int mask = 1 << rmobile_pd->bit_shift; | 75 | unsigned int mask; |
65 | unsigned int retry_count; | 76 | unsigned int retry_count; |
66 | int ret = 0; | 77 | int ret = 0; |
67 | 78 | ||
79 | if (rmobile_pd->bit_shift == ~0) | ||
80 | return 0; | ||
81 | |||
82 | mask = 1 << rmobile_pd->bit_shift; | ||
68 | if (__raw_readl(rmobile_pd->base + PSTR) & mask) | 83 | if (__raw_readl(rmobile_pd->base + PSTR) & mask) |
69 | goto out; | 84 | goto out; |
70 | 85 | ||
@@ -148,6 +163,8 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) | |||
148 | __rmobile_pd_power_up(rmobile_pd, false); | 163 | __rmobile_pd_power_up(rmobile_pd, false); |
149 | } | 164 | } |
150 | 165 | ||
166 | #ifdef CONFIG_ARCH_SHMOBILE_LEGACY | ||
167 | |||
151 | void rmobile_init_domains(struct rmobile_pm_domain domains[], int num) | 168 | void rmobile_init_domains(struct rmobile_pm_domain domains[], int num) |
152 | { | 169 | { |
153 | int j; | 170 | int j; |
@@ -180,3 +197,199 @@ void rmobile_add_devices_to_domains(struct pm_domain_device data[], | |||
180 | rmobile_add_device_to_domain_td(data[j].domain_name, | 197 | rmobile_add_device_to_domain_td(data[j].domain_name, |
181 | data[j].pdev, &latencies); | 198 | data[j].pdev, &latencies); |
182 | } | 199 | } |
200 | |||
201 | #else /* !CONFIG_ARCH_SHMOBILE_LEGACY */ | ||
202 | |||
203 | static int rmobile_pd_suspend_cpu(void) | ||
204 | { | ||
205 | /* | ||
206 | * This domain contains the CPU core and therefore it should | ||
207 | * only be turned off if the CPU is not in use. | ||
208 | */ | ||
209 | return -EBUSY; | ||
210 | } | ||
211 | |||
212 | static int rmobile_pd_suspend_console(void) | ||
213 | { | ||
214 | /* | ||
215 | * Serial consoles make use of SCIF hardware located in this domain, | ||
216 | * hence keep the power domain on if "no_console_suspend" is set. | ||
217 | */ | ||
218 | return console_suspend_enabled ? 0 : -EBUSY; | ||
219 | } | ||
220 | |||
221 | static int rmobile_pd_suspend_debug(void) | ||
222 | { | ||
223 | /* | ||
224 | * This domain contains the Coresight-ETM hardware block and | ||
225 | * therefore it should only be turned off if the debug module is | ||
226 | * not in use. | ||
227 | */ | ||
228 | return -EBUSY; | ||
229 | } | ||
230 | |||
231 | #define MAX_NUM_CPU_PDS 8 | ||
232 | |||
233 | static unsigned int num_cpu_pds __initdata; | ||
234 | static struct device_node *cpu_pds[MAX_NUM_CPU_PDS] __initdata; | ||
235 | static struct device_node *console_pd __initdata; | ||
236 | static struct device_node *debug_pd __initdata; | ||
237 | |||
238 | static void __init get_special_pds(void) | ||
239 | { | ||
240 | struct device_node *np, *pd; | ||
241 | unsigned int i; | ||
242 | |||
243 | /* PM domains containing CPUs */ | ||
244 | for_each_node_by_type(np, "cpu") { | ||
245 | pd = of_parse_phandle(np, "power-domains", 0); | ||
246 | if (!pd) | ||
247 | continue; | ||
248 | |||
249 | for (i = 0; i < num_cpu_pds; i++) | ||
250 | if (pd == cpu_pds[i]) | ||
251 | break; | ||
252 | |||
253 | if (i < num_cpu_pds) { | ||
254 | of_node_put(pd); | ||
255 | continue; | ||
256 | } | ||
257 | |||
258 | if (num_cpu_pds == MAX_NUM_CPU_PDS) { | ||
259 | pr_warn("Too many CPU PM domains\n"); | ||
260 | of_node_put(pd); | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | cpu_pds[num_cpu_pds++] = pd; | ||
265 | } | ||
266 | |||
267 | /* PM domain containing console */ | ||
268 | if (of_stdout) | ||
269 | console_pd = of_parse_phandle(of_stdout, "power-domains", 0); | ||
270 | |||
271 | /* PM domain containing Coresight-ETM */ | ||
272 | np = of_find_compatible_node(NULL, NULL, "arm,coresight-etm3x"); | ||
273 | if (np) { | ||
274 | debug_pd = of_parse_phandle(np, "power-domains", 0); | ||
275 | of_node_put(np); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static void __init put_special_pds(void) | ||
280 | { | ||
281 | unsigned int i; | ||
282 | |||
283 | for (i = 0; i < num_cpu_pds; i++) | ||
284 | of_node_put(cpu_pds[i]); | ||
285 | of_node_put(console_pd); | ||
286 | of_node_put(debug_pd); | ||
287 | } | ||
288 | |||
289 | static bool __init pd_contains_cpu(const struct device_node *pd) | ||
290 | { | ||
291 | unsigned int i; | ||
292 | |||
293 | for (i = 0; i < num_cpu_pds; i++) | ||
294 | if (pd == cpu_pds[i]) | ||
295 | return true; | ||
296 | |||
297 | return false; | ||
298 | } | ||
299 | |||
300 | static void __init rmobile_setup_pm_domain(struct device_node *np, | ||
301 | struct rmobile_pm_domain *pd) | ||
302 | { | ||
303 | const char *name = pd->genpd.name; | ||
304 | |||
305 | if (pd_contains_cpu(np)) { | ||
306 | pr_debug("PM domain %s contains CPU\n", name); | ||
307 | pd->gov = &pm_domain_always_on_gov; | ||
308 | pd->suspend = rmobile_pd_suspend_cpu; | ||
309 | } else if (np == console_pd) { | ||
310 | pr_debug("PM domain %s contains serial console\n", name); | ||
311 | pd->gov = &pm_domain_always_on_gov; | ||
312 | pd->suspend = rmobile_pd_suspend_console; | ||
313 | } else if (np == debug_pd) { | ||
314 | pr_debug("PM domain %s contains Coresight-ETM\n", name); | ||
315 | pd->gov = &pm_domain_always_on_gov; | ||
316 | pd->suspend = rmobile_pd_suspend_debug; | ||
317 | } | ||
318 | |||
319 | rmobile_init_pm_domain(pd); | ||
320 | } | ||
321 | |||
322 | static int __init rmobile_add_pm_domains(void __iomem *base, | ||
323 | struct device_node *parent, | ||
324 | struct generic_pm_domain *genpd_parent) | ||
325 | { | ||
326 | struct device_node *np; | ||
327 | |||
328 | for_each_child_of_node(parent, np) { | ||
329 | struct rmobile_pm_domain *pd; | ||
330 | u32 idx = ~0; | ||
331 | |||
332 | if (of_property_read_u32(np, "reg", &idx)) { | ||
333 | /* always-on domain */ | ||
334 | } | ||
335 | |||
336 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | ||
337 | if (!pd) | ||
338 | return -ENOMEM; | ||
339 | |||
340 | pd->genpd.name = np->name; | ||
341 | pd->base = base; | ||
342 | pd->bit_shift = idx; | ||
343 | |||
344 | rmobile_setup_pm_domain(np, pd); | ||
345 | if (genpd_parent) | ||
346 | pm_genpd_add_subdomain(genpd_parent, &pd->genpd); | ||
347 | of_genpd_add_provider_simple(np, &pd->genpd); | ||
348 | |||
349 | rmobile_add_pm_domains(base, np, &pd->genpd); | ||
350 | } | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int __init rmobile_init_pm_domains(void) | ||
355 | { | ||
356 | struct device_node *np, *pmd; | ||
357 | bool scanned = false; | ||
358 | void __iomem *base; | ||
359 | int ret = 0; | ||
360 | |||
361 | for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { | ||
362 | base = of_iomap(np, 0); | ||
363 | if (!base) { | ||
364 | pr_warn("%s cannot map reg 0\n", np->full_name); | ||
365 | continue; | ||
366 | } | ||
367 | |||
368 | pmd = of_get_child_by_name(np, "pm-domains"); | ||
369 | if (!pmd) { | ||
370 | pr_warn("%s lacks pm-domains node\n", np->full_name); | ||
371 | continue; | ||
372 | } | ||
373 | |||
374 | if (!scanned) { | ||
375 | /* Find PM domains containing special blocks */ | ||
376 | get_special_pds(); | ||
377 | scanned = true; | ||
378 | } | ||
379 | |||
380 | ret = rmobile_add_pm_domains(base, pmd, NULL); | ||
381 | of_node_put(pmd); | ||
382 | if (ret) { | ||
383 | of_node_put(np); | ||
384 | break; | ||
385 | } | ||
386 | } | ||
387 | |||
388 | put_special_pds(); | ||
389 | |||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | core_initcall(rmobile_init_pm_domains); | ||
394 | |||
395 | #endif /* !CONFIG_ARCH_SHMOBILE_LEGACY */ | ||
diff --git a/arch/arm/mach-shmobile/pm-rmobile.h b/arch/arm/mach-shmobile/pm-rmobile.h index 0602130bb260..53219786f539 100644 --- a/arch/arm/mach-shmobile/pm-rmobile.h +++ b/arch/arm/mach-shmobile/pm-rmobile.h | |||
@@ -37,7 +37,7 @@ struct pm_domain_device { | |||
37 | struct platform_device *pdev; | 37 | struct platform_device *pdev; |
38 | }; | 38 | }; |
39 | 39 | ||
40 | #ifdef CONFIG_PM_RMOBILE | 40 | #if defined(CONFIG_PM_RMOBILE) && defined(CONFIG_ARCH_SHMOBILE_LEGACY) |
41 | extern void rmobile_init_domains(struct rmobile_pm_domain domains[], int num); | 41 | extern void rmobile_init_domains(struct rmobile_pm_domain domains[], int num); |
42 | extern void rmobile_add_device_to_domain_td(const char *domain_name, | 42 | extern void rmobile_add_device_to_domain_td(const char *domain_name, |
43 | struct platform_device *pdev, | 43 | struct platform_device *pdev, |