diff options
Diffstat (limited to 'arch/arm/mach-imx/pm-imx6q.c')
-rw-r--r-- | arch/arm/mach-imx/pm-imx6q.c | 281 |
1 files changed, 276 insertions, 5 deletions
diff --git a/arch/arm/mach-imx/pm-imx6q.c b/arch/arm/mach-imx/pm-imx6q.c index 30af3c0e1bf3..a7b1e58a3ac1 100644 --- a/arch/arm/mach-imx/pm-imx6q.c +++ b/arch/arm/mach-imx/pm-imx6q.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 2011-2013 Freescale Semiconductor, Inc. | 2 | * Copyright 2011-2014 Freescale Semiconductor, Inc. |
3 | * Copyright 2011 Linaro Ltd. | 3 | * Copyright 2011 Linaro Ltd. |
4 | * | 4 | * |
5 | * The code contained herein is licensed under the GNU General Public | 5 | * The code contained herein is licensed under the GNU General Public |
@@ -14,16 +14,19 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/io.h> | 15 | #include <linux/io.h> |
16 | #include <linux/irq.h> | 16 | #include <linux/irq.h> |
17 | #include <linux/genalloc.h> | ||
17 | #include <linux/mfd/syscon.h> | 18 | #include <linux/mfd/syscon.h> |
18 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> | 19 | #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> |
19 | #include <linux/of.h> | 20 | #include <linux/of.h> |
20 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/of_platform.h> | ||
21 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
22 | #include <linux/suspend.h> | 24 | #include <linux/suspend.h> |
23 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
26 | #include <asm/fncpy.h> | ||
24 | #include <asm/proc-fns.h> | 27 | #include <asm/proc-fns.h> |
25 | #include <asm/suspend.h> | 28 | #include <asm/suspend.h> |
26 | #include <asm/hardware/cache-l2x0.h> | 29 | #include <asm/tlb.h> |
27 | 30 | ||
28 | #include "common.h" | 31 | #include "common.h" |
29 | #include "hardware.h" | 32 | #include "hardware.h" |
@@ -58,7 +61,85 @@ | |||
58 | #define CGPR 0x64 | 61 | #define CGPR 0x64 |
59 | #define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) | 62 | #define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17) |
60 | 63 | ||
64 | #define MX6Q_SUSPEND_OCRAM_SIZE 0x1000 | ||
65 | #define MX6_MAX_MMDC_IO_NUM 33 | ||
66 | |||
61 | static void __iomem *ccm_base; | 67 | static void __iomem *ccm_base; |
68 | static void __iomem *suspend_ocram_base; | ||
69 | static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase); | ||
70 | |||
71 | /* | ||
72 | * suspend ocram space layout: | ||
73 | * ======================== high address ====================== | ||
74 | * . | ||
75 | * . | ||
76 | * . | ||
77 | * ^ | ||
78 | * ^ | ||
79 | * ^ | ||
80 | * imx6_suspend code | ||
81 | * PM_INFO structure(imx6_cpu_pm_info) | ||
82 | * ======================== low address ======================= | ||
83 | */ | ||
84 | |||
85 | struct imx6_pm_base { | ||
86 | phys_addr_t pbase; | ||
87 | void __iomem *vbase; | ||
88 | }; | ||
89 | |||
90 | struct imx6_pm_socdata { | ||
91 | u32 cpu_type; | ||
92 | const char *mmdc_compat; | ||
93 | const char *src_compat; | ||
94 | const char *iomuxc_compat; | ||
95 | const char *gpc_compat; | ||
96 | const u32 mmdc_io_num; | ||
97 | const u32 *mmdc_io_offset; | ||
98 | }; | ||
99 | |||
100 | static const u32 imx6q_mmdc_io_offset[] __initconst = { | ||
101 | 0x5ac, 0x5b4, 0x528, 0x520, /* DQM0 ~ DQM3 */ | ||
102 | 0x514, 0x510, 0x5bc, 0x5c4, /* DQM4 ~ DQM7 */ | ||
103 | 0x56c, 0x578, 0x588, 0x594, /* CAS, RAS, SDCLK_0, SDCLK_1 */ | ||
104 | 0x5a8, 0x5b0, 0x524, 0x51c, /* SDQS0 ~ SDQS3 */ | ||
105 | 0x518, 0x50c, 0x5b8, 0x5c0, /* SDQS4 ~ SDQS7 */ | ||
106 | 0x784, 0x788, 0x794, 0x79c, /* GPR_B0DS ~ GPR_B3DS */ | ||
107 | 0x7a0, 0x7a4, 0x7a8, 0x748, /* GPR_B4DS ~ GPR_B7DS */ | ||
108 | 0x59c, 0x5a0, 0x750, 0x774, /* SODT0, SODT1, MODE_CTL, MODE */ | ||
109 | 0x74c, /* GPR_ADDS */ | ||
110 | }; | ||
111 | |||
112 | static const struct imx6_pm_socdata imx6q_pm_data __initconst = { | ||
113 | .cpu_type = MXC_CPU_IMX6Q, | ||
114 | .mmdc_compat = "fsl,imx6q-mmdc", | ||
115 | .src_compat = "fsl,imx6q-src", | ||
116 | .iomuxc_compat = "fsl,imx6q-iomuxc", | ||
117 | .gpc_compat = "fsl,imx6q-gpc", | ||
118 | .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset), | ||
119 | .mmdc_io_offset = imx6q_mmdc_io_offset, | ||
120 | }; | ||
121 | |||
122 | /* | ||
123 | * This structure is for passing necessary data for low level ocram | ||
124 | * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct | ||
125 | * definition is changed, the offset definition in | ||
126 | * arch/arm/mach-imx/suspend-imx6.S must be also changed accordingly, | ||
127 | * otherwise, the suspend to ocram function will be broken! | ||
128 | */ | ||
129 | struct imx6_cpu_pm_info { | ||
130 | phys_addr_t pbase; /* The physical address of pm_info. */ | ||
131 | phys_addr_t resume_addr; /* The physical resume address for asm code */ | ||
132 | u32 cpu_type; | ||
133 | u32 pm_info_size; /* Size of pm_info. */ | ||
134 | struct imx6_pm_base mmdc_base; | ||
135 | struct imx6_pm_base src_base; | ||
136 | struct imx6_pm_base iomuxc_base; | ||
137 | struct imx6_pm_base ccm_base; | ||
138 | struct imx6_pm_base gpc_base; | ||
139 | struct imx6_pm_base l2_base; | ||
140 | u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */ | ||
141 | u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */ | ||
142 | } __aligned(8); | ||
62 | 143 | ||
63 | void imx6q_set_int_mem_clk_lpm(void) | 144 | void imx6q_set_int_mem_clk_lpm(void) |
64 | { | 145 | { |
@@ -177,7 +258,17 @@ int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) | |||
177 | 258 | ||
178 | static int imx6q_suspend_finish(unsigned long val) | 259 | static int imx6q_suspend_finish(unsigned long val) |
179 | { | 260 | { |
180 | cpu_do_idle(); | 261 | if (!imx6_suspend_in_ocram_fn) { |
262 | cpu_do_idle(); | ||
263 | } else { | ||
264 | /* | ||
265 | * call low level suspend function in ocram, | ||
266 | * as we need to float DDR IO. | ||
267 | */ | ||
268 | local_flush_tlb_all(); | ||
269 | imx6_suspend_in_ocram_fn(suspend_ocram_base); | ||
270 | } | ||
271 | |||
181 | return 0; | 272 | return 0; |
182 | } | 273 | } |
183 | 274 | ||
@@ -187,7 +278,12 @@ static int imx6q_pm_enter(suspend_state_t state) | |||
187 | case PM_SUSPEND_MEM: | 278 | case PM_SUSPEND_MEM: |
188 | imx6q_set_lpm(STOP_POWER_OFF); | 279 | imx6q_set_lpm(STOP_POWER_OFF); |
189 | imx6q_enable_wb(true); | 280 | imx6q_enable_wb(true); |
190 | imx6q_enable_rbc(true); | 281 | /* |
282 | * For suspend into ocram, asm code already take care of | ||
283 | * RBC setting, so we do NOT need to do that here. | ||
284 | */ | ||
285 | if (!imx6_suspend_in_ocram_fn) | ||
286 | imx6q_enable_rbc(true); | ||
191 | imx_gpc_pre_suspend(); | 287 | imx_gpc_pre_suspend(); |
192 | imx_anatop_pre_suspend(); | 288 | imx_anatop_pre_suspend(); |
193 | imx_set_cpu_jump(0, v7_cpu_resume); | 289 | imx_set_cpu_jump(0, v7_cpu_resume); |
@@ -218,12 +314,172 @@ void __init imx6q_pm_set_ccm_base(void __iomem *base) | |||
218 | ccm_base = base; | 314 | ccm_base = base; |
219 | } | 315 | } |
220 | 316 | ||
221 | void __init imx6q_pm_init(void) | 317 | static int __init imx6_pm_get_base(struct imx6_pm_base *base, |
318 | const char *compat) | ||
319 | { | ||
320 | struct device_node *node; | ||
321 | struct resource res; | ||
322 | int ret = 0; | ||
323 | |||
324 | node = of_find_compatible_node(NULL, NULL, compat); | ||
325 | if (!node) { | ||
326 | ret = -ENODEV; | ||
327 | goto out; | ||
328 | } | ||
329 | |||
330 | ret = of_address_to_resource(node, 0, &res); | ||
331 | if (ret) | ||
332 | goto put_node; | ||
333 | |||
334 | base->pbase = res.start; | ||
335 | base->vbase = ioremap(res.start, resource_size(&res)); | ||
336 | if (!base->vbase) | ||
337 | ret = -ENOMEM; | ||
338 | |||
339 | put_node: | ||
340 | of_node_put(node); | ||
341 | out: | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | static int __init imx6q_ocram_suspend_init(const struct imx6_pm_socdata | ||
346 | *socdata) | ||
347 | { | ||
348 | phys_addr_t ocram_pbase; | ||
349 | struct device_node *node; | ||
350 | struct platform_device *pdev; | ||
351 | struct imx6_cpu_pm_info *pm_info; | ||
352 | struct gen_pool *ocram_pool; | ||
353 | unsigned long ocram_base; | ||
354 | int i, ret = 0; | ||
355 | const u32 *mmdc_offset_array; | ||
356 | |||
357 | if (!socdata) { | ||
358 | pr_warn("%s: invalid argument!\n", __func__); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
362 | node = of_find_compatible_node(NULL, NULL, "mmio-sram"); | ||
363 | if (!node) { | ||
364 | pr_warn("%s: failed to find ocram node!\n", __func__); | ||
365 | return -ENODEV; | ||
366 | } | ||
367 | |||
368 | pdev = of_find_device_by_node(node); | ||
369 | if (!pdev) { | ||
370 | pr_warn("%s: failed to find ocram device!\n", __func__); | ||
371 | ret = -ENODEV; | ||
372 | goto put_node; | ||
373 | } | ||
374 | |||
375 | ocram_pool = dev_get_gen_pool(&pdev->dev); | ||
376 | if (!ocram_pool) { | ||
377 | pr_warn("%s: ocram pool unavailable!\n", __func__); | ||
378 | ret = -ENODEV; | ||
379 | goto put_node; | ||
380 | } | ||
381 | |||
382 | ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE); | ||
383 | if (!ocram_base) { | ||
384 | pr_warn("%s: unable to alloc ocram!\n", __func__); | ||
385 | ret = -ENOMEM; | ||
386 | goto put_node; | ||
387 | } | ||
388 | |||
389 | ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); | ||
390 | |||
391 | suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, | ||
392 | MX6Q_SUSPEND_OCRAM_SIZE, false); | ||
393 | |||
394 | pm_info = suspend_ocram_base; | ||
395 | pm_info->pbase = ocram_pbase; | ||
396 | pm_info->resume_addr = virt_to_phys(v7_cpu_resume); | ||
397 | pm_info->pm_info_size = sizeof(*pm_info); | ||
398 | |||
399 | /* | ||
400 | * ccm physical address is not used by asm code currently, | ||
401 | * so get ccm virtual address directly, as we already have | ||
402 | * it from ccm driver. | ||
403 | */ | ||
404 | pm_info->ccm_base.vbase = ccm_base; | ||
405 | |||
406 | ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat); | ||
407 | if (ret) { | ||
408 | pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret); | ||
409 | goto put_node; | ||
410 | } | ||
411 | |||
412 | ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat); | ||
413 | if (ret) { | ||
414 | pr_warn("%s: failed to get src base %d!\n", __func__, ret); | ||
415 | goto src_map_failed; | ||
416 | } | ||
417 | |||
418 | ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat); | ||
419 | if (ret) { | ||
420 | pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret); | ||
421 | goto iomuxc_map_failed; | ||
422 | } | ||
423 | |||
424 | ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat); | ||
425 | if (ret) { | ||
426 | pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); | ||
427 | goto gpc_map_failed; | ||
428 | } | ||
429 | |||
430 | ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache"); | ||
431 | if (ret) { | ||
432 | pr_warn("%s: failed to get pl310-cache base %d!\n", | ||
433 | __func__, ret); | ||
434 | goto pl310_cache_map_failed; | ||
435 | } | ||
436 | |||
437 | pm_info->cpu_type = socdata->cpu_type; | ||
438 | pm_info->mmdc_io_num = socdata->mmdc_io_num; | ||
439 | mmdc_offset_array = socdata->mmdc_io_offset; | ||
440 | |||
441 | for (i = 0; i < pm_info->mmdc_io_num; i++) { | ||
442 | pm_info->mmdc_io_val[i][0] = | ||
443 | mmdc_offset_array[i]; | ||
444 | pm_info->mmdc_io_val[i][1] = | ||
445 | readl_relaxed(pm_info->iomuxc_base.vbase + | ||
446 | mmdc_offset_array[i]); | ||
447 | } | ||
448 | |||
449 | imx6_suspend_in_ocram_fn = fncpy( | ||
450 | suspend_ocram_base + sizeof(*pm_info), | ||
451 | &imx6_suspend, | ||
452 | MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info)); | ||
453 | |||
454 | goto put_node; | ||
455 | |||
456 | pl310_cache_map_failed: | ||
457 | iounmap(&pm_info->gpc_base.vbase); | ||
458 | gpc_map_failed: | ||
459 | iounmap(&pm_info->iomuxc_base.vbase); | ||
460 | iomuxc_map_failed: | ||
461 | iounmap(&pm_info->src_base.vbase); | ||
462 | src_map_failed: | ||
463 | iounmap(&pm_info->mmdc_base.vbase); | ||
464 | put_node: | ||
465 | of_node_put(node); | ||
466 | |||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | static void __init imx6_pm_common_init(const struct imx6_pm_socdata | ||
471 | *socdata) | ||
222 | { | 472 | { |
223 | struct regmap *gpr; | 473 | struct regmap *gpr; |
474 | int ret; | ||
224 | 475 | ||
225 | WARN_ON(!ccm_base); | 476 | WARN_ON(!ccm_base); |
226 | 477 | ||
478 | ret = imx6q_ocram_suspend_init(socdata); | ||
479 | if (ret) | ||
480 | pr_warn("%s: failed to initialize ocram suspend %d!\n", | ||
481 | __func__, ret); | ||
482 | |||
227 | /* | 483 | /* |
228 | * This is for SW workaround step #1 of ERR007265, see comments | 484 | * This is for SW workaround step #1 of ERR007265, see comments |
229 | * in imx6q_set_lpm for details of this errata. | 485 | * in imx6q_set_lpm for details of this errata. |
@@ -239,3 +495,18 @@ void __init imx6q_pm_init(void) | |||
239 | 495 | ||
240 | suspend_set_ops(&imx6q_pm_ops); | 496 | suspend_set_ops(&imx6q_pm_ops); |
241 | } | 497 | } |
498 | |||
499 | void __init imx6q_pm_init(void) | ||
500 | { | ||
501 | imx6_pm_common_init(&imx6q_pm_data); | ||
502 | } | ||
503 | |||
504 | void __init imx6dl_pm_init(void) | ||
505 | { | ||
506 | imx6_pm_common_init(NULL); | ||
507 | } | ||
508 | |||
509 | void __init imx6sl_pm_init(void) | ||
510 | { | ||
511 | imx6_pm_common_init(NULL); | ||
512 | } | ||