1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/delay.h>
14#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/irq.h>
17#include <linux/genalloc.h>
18#include <linux/mfd/syscon.h>
19#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/of_platform.h>
23#include <linux/regmap.h>
24#include <linux/suspend.h>
25#include <asm/cacheflush.h>
26#include <asm/fncpy.h>
27#include <asm/proc-fns.h>
28#include <asm/suspend.h>
29#include <asm/tlb.h>
30
31#include "common.h"
32#include "hardware.h"
33
34#define CCR 0x0
35#define BM_CCR_WB_COUNT (0x7 << 16)
36#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
37#define BM_CCR_RBC_EN (0x1 << 27)
38
39#define CLPCR 0x54
40#define BP_CLPCR_LPM 0
41#define BM_CLPCR_LPM (0x3 << 0)
42#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
43#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
44#define BM_CLPCR_SBYOS (0x1 << 6)
45#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
46#define BM_CLPCR_VSTBY (0x1 << 8)
47#define BP_CLPCR_STBY_COUNT 9
48#define BM_CLPCR_STBY_COUNT (0x3 << 9)
49#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
50#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
51#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
52#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
53#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
54#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
55#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
56#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
57#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
58#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
59#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
60
61#define CGPR 0x64
62#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17)
63
64#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
65#define MX6_MAX_MMDC_IO_NUM 33
66
67static void __iomem *ccm_base;
68static void __iomem *suspend_ocram_base;
69static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85struct imx6_pm_base {
86 phys_addr_t pbase;
87 void __iomem *vbase;
88};
89
90struct imx6_pm_socdata {
91 u32 ddr_type;
92 const char *mmdc_compat;
93 const char *src_compat;
94 const char *iomuxc_compat;
95 const char *gpc_compat;
96 const char *pl310_compat;
97 const u32 mmdc_io_num;
98 const u32 *mmdc_io_offset;
99};
100
101static const u32 imx6q_mmdc_io_offset[] __initconst = {
102 0x5ac, 0x5b4, 0x528, 0x520,
103 0x514, 0x510, 0x5bc, 0x5c4,
104 0x56c, 0x578, 0x588, 0x594,
105 0x5a8, 0x5b0, 0x524, 0x51c,
106 0x518, 0x50c, 0x5b8, 0x5c0,
107 0x784, 0x788, 0x794, 0x79c,
108 0x7a0, 0x7a4, 0x7a8, 0x748,
109 0x59c, 0x5a0, 0x750, 0x774,
110 0x74c,
111};
112
113static const u32 imx6dl_mmdc_io_offset[] __initconst = {
114 0x470, 0x474, 0x478, 0x47c,
115 0x480, 0x484, 0x488, 0x48c,
116 0x464, 0x490, 0x4ac, 0x4b0,
117 0x4bc, 0x4c0, 0x4c4, 0x4c8,
118 0x4cc, 0x4d0, 0x4d4, 0x4d8,
119 0x764, 0x770, 0x778, 0x77c,
120 0x780, 0x784, 0x78c, 0x748,
121 0x4b4, 0x4b8, 0x750, 0x760,
122 0x74c,
123};
124
125static const u32 imx6sl_mmdc_io_offset[] __initconst = {
126 0x30c, 0x310, 0x314, 0x318,
127 0x5c4, 0x5cc, 0x5d4, 0x5d8,
128 0x300, 0x31c, 0x338, 0x5ac,
129 0x33c, 0x340, 0x5b0, 0x5c0,
130 0x330, 0x334, 0x320,
131};
132
133static const u32 imx6sx_mmdc_io_offset[] __initconst = {
134 0x2ec, 0x2f0, 0x2f4, 0x2f8,
135 0x60c, 0x610, 0x61c, 0x620,
136 0x300, 0x2fc, 0x32c, 0x5f4,
137 0x310, 0x314, 0x5f8, 0x608,
138 0x330, 0x334, 0x338, 0x33c,
139};
140
141static const u32 imx6ul_mmdc_io_offset[] __initconst = {
142 0x244, 0x248, 0x24c, 0x250,
143 0x27c, 0x498, 0x4a4, 0x490,
144 0x280, 0x284, 0x260, 0x264,
145 0x494, 0x4b0,
146};
147
148static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
149 .mmdc_compat = "fsl,imx6q-mmdc",
150 .src_compat = "fsl,imx6q-src",
151 .iomuxc_compat = "fsl,imx6q-iomuxc",
152 .gpc_compat = "fsl,imx6q-gpc",
153 .pl310_compat = "arm,pl310-cache",
154 .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
155 .mmdc_io_offset = imx6q_mmdc_io_offset,
156};
157
158static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
159 .mmdc_compat = "fsl,imx6q-mmdc",
160 .src_compat = "fsl,imx6q-src",
161 .iomuxc_compat = "fsl,imx6dl-iomuxc",
162 .gpc_compat = "fsl,imx6q-gpc",
163 .pl310_compat = "arm,pl310-cache",
164 .mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
165 .mmdc_io_offset = imx6dl_mmdc_io_offset,
166};
167
168static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
169 .mmdc_compat = "fsl,imx6sl-mmdc",
170 .src_compat = "fsl,imx6sl-src",
171 .iomuxc_compat = "fsl,imx6sl-iomuxc",
172 .gpc_compat = "fsl,imx6sl-gpc",
173 .pl310_compat = "arm,pl310-cache",
174 .mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
175 .mmdc_io_offset = imx6sl_mmdc_io_offset,
176};
177
178static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
179 .mmdc_compat = "fsl,imx6sx-mmdc",
180 .src_compat = "fsl,imx6sx-src",
181 .iomuxc_compat = "fsl,imx6sx-iomuxc",
182 .gpc_compat = "fsl,imx6sx-gpc",
183 .pl310_compat = "arm,pl310-cache",
184 .mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset),
185 .mmdc_io_offset = imx6sx_mmdc_io_offset,
186};
187
188static const struct imx6_pm_socdata imx6ul_pm_data __initconst = {
189 .mmdc_compat = "fsl,imx6ul-mmdc",
190 .src_compat = "fsl,imx6ul-src",
191 .iomuxc_compat = "fsl,imx6ul-iomuxc",
192 .gpc_compat = "fsl,imx6ul-gpc",
193 .pl310_compat = NULL,
194 .mmdc_io_num = ARRAY_SIZE(imx6ul_mmdc_io_offset),
195 .mmdc_io_offset = imx6ul_mmdc_io_offset,
196};
197
198
199
200
201
202
203
204
205struct imx6_cpu_pm_info {
206 phys_addr_t pbase;
207 phys_addr_t resume_addr;
208 u32 ddr_type;
209 u32 pm_info_size;
210 struct imx6_pm_base mmdc_base;
211 struct imx6_pm_base src_base;
212 struct imx6_pm_base iomuxc_base;
213 struct imx6_pm_base ccm_base;
214 struct imx6_pm_base gpc_base;
215 struct imx6_pm_base l2_base;
216 u32 mmdc_io_num;
217 u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2];
218} __aligned(8);
219
220void imx6_set_int_mem_clk_lpm(bool enable)
221{
222 u32 val = readl_relaxed(ccm_base + CGPR);
223
224 val &= ~BM_CGPR_INT_MEM_CLK_LPM;
225 if (enable)
226 val |= BM_CGPR_INT_MEM_CLK_LPM;
227 writel_relaxed(val, ccm_base + CGPR);
228}
229
230void imx6_enable_rbc(bool enable)
231{
232 u32 val;
233
234
235
236
237
238 imx_gpc_mask_all();
239
240
241 val = readl_relaxed(ccm_base + CCR);
242 val &= ~BM_CCR_RBC_EN;
243 val |= enable ? BM_CCR_RBC_EN : 0;
244 writel_relaxed(val, ccm_base + CCR);
245
246
247 val = readl_relaxed(ccm_base + CCR);
248 val &= ~BM_CCR_RBC_BYPASS_COUNT;
249 val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
250 writel(val, ccm_base + CCR);
251
252
253
254
255
256
257 udelay(65);
258
259
260 imx_gpc_restore_all();
261}
262
263static void imx6q_enable_wb(bool enable)
264{
265 u32 val;
266
267
268 val = readl_relaxed(ccm_base + CLPCR);
269 val &= ~BM_CLPCR_WB_PER_AT_LPM;
270 val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
271 writel_relaxed(val, ccm_base + CLPCR);
272
273
274 val = readl_relaxed(ccm_base + CCR);
275 val &= ~BM_CCR_WB_COUNT;
276 val |= enable ? BM_CCR_WB_COUNT : 0;
277 writel_relaxed(val, ccm_base + CCR);
278}
279
280int imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
281{
282 u32 val = readl_relaxed(ccm_base + CLPCR);
283
284 val &= ~BM_CLPCR_LPM;
285 switch (mode) {
286 case WAIT_CLOCKED:
287 break;
288 case WAIT_UNCLOCKED:
289 val |= 0x1 << BP_CLPCR_LPM;
290 val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
291 break;
292 case STOP_POWER_ON:
293 val |= 0x2 << BP_CLPCR_LPM;
294 val &= ~BM_CLPCR_VSTBY;
295 val &= ~BM_CLPCR_SBYOS;
296 if (cpu_is_imx6sl())
297 val |= BM_CLPCR_BYPASS_PMIC_READY;
298 if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul())
299 val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
300 else
301 val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
302 break;
303 case WAIT_UNCLOCKED_POWER_OFF:
304 val |= 0x1 << BP_CLPCR_LPM;
305 val &= ~BM_CLPCR_VSTBY;
306 val &= ~BM_CLPCR_SBYOS;
307 break;
308 case STOP_POWER_OFF:
309 val |= 0x2 << BP_CLPCR_LPM;
310 val |= 0x3 << BP_CLPCR_STBY_COUNT;
311 val |= BM_CLPCR_VSTBY;
312 val |= BM_CLPCR_SBYOS;
313 if (cpu_is_imx6sl() || cpu_is_imx6sx())
314 val |= BM_CLPCR_BYPASS_PMIC_READY;
315 if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul())
316 val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
317 else
318 val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
319 break;
320 default:
321 return -EINVAL;
322 }
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 imx_gpc_hwirq_unmask(0);
339 writel_relaxed(val, ccm_base + CLPCR);
340 imx_gpc_hwirq_mask(0);
341
342 return 0;
343}
344
345static int imx6q_suspend_finish(unsigned long val)
346{
347 if (!imx6_suspend_in_ocram_fn) {
348 cpu_do_idle();
349 } else {
350
351
352
353
354 local_flush_tlb_all();
355
356 if (!((struct imx6_cpu_pm_info *)
357 suspend_ocram_base)->l2_base.vbase)
358 flush_cache_all();
359 imx6_suspend_in_ocram_fn(suspend_ocram_base);
360 }
361
362 return 0;
363}
364
365static int imx6q_pm_enter(suspend_state_t state)
366{
367 switch (state) {
368 case PM_SUSPEND_STANDBY:
369 imx6_set_lpm(STOP_POWER_ON);
370 imx6_set_int_mem_clk_lpm(true);
371 imx_gpc_pre_suspend(false);
372 if (cpu_is_imx6sl())
373 imx6sl_set_wait_clk(true);
374
375 cpu_do_idle();
376 if (cpu_is_imx6sl())
377 imx6sl_set_wait_clk(false);
378 imx_gpc_post_resume();
379 imx6_set_lpm(WAIT_CLOCKED);
380 break;
381 case PM_SUSPEND_MEM:
382 imx6_set_lpm(STOP_POWER_OFF);
383 imx6_set_int_mem_clk_lpm(false);
384 imx6q_enable_wb(true);
385
386
387
388
389 if (!imx6_suspend_in_ocram_fn)
390 imx6_enable_rbc(true);
391 imx_gpc_pre_suspend(true);
392 imx_anatop_pre_suspend();
393
394 cpu_suspend(0, imx6q_suspend_finish);
395 if (cpu_is_imx6q() || cpu_is_imx6dl())
396 imx_smp_prepare();
397 imx_anatop_post_resume();
398 imx_gpc_post_resume();
399 imx6_enable_rbc(false);
400 imx6q_enable_wb(false);
401 imx6_set_int_mem_clk_lpm(true);
402 imx6_set_lpm(WAIT_CLOCKED);
403 break;
404 default:
405 return -EINVAL;
406 }
407
408 return 0;
409}
410
411static int imx6q_pm_valid(suspend_state_t state)
412{
413 return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
414}
415
416static const struct platform_suspend_ops imx6q_pm_ops = {
417 .enter = imx6q_pm_enter,
418 .valid = imx6q_pm_valid,
419};
420
421static int __init imx6_pm_get_base(struct imx6_pm_base *base,
422 const char *compat)
423{
424 struct device_node *node;
425 struct resource res;
426 int ret = 0;
427
428 node = of_find_compatible_node(NULL, NULL, compat);
429 if (!node) {
430 ret = -ENODEV;
431 goto out;
432 }
433
434 ret = of_address_to_resource(node, 0, &res);
435 if (ret)
436 goto put_node;
437
438 base->pbase = res.start;
439 base->vbase = ioremap(res.start, resource_size(&res));
440 if (!base->vbase)
441 ret = -ENOMEM;
442
443put_node:
444 of_node_put(node);
445out:
446 return ret;
447}
448
449static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
450{
451 phys_addr_t ocram_pbase;
452 struct device_node *node;
453 struct platform_device *pdev;
454 struct imx6_cpu_pm_info *pm_info;
455 struct gen_pool *ocram_pool;
456 unsigned long ocram_base;
457 int i, ret = 0;
458 const u32 *mmdc_offset_array;
459
460 suspend_set_ops(&imx6q_pm_ops);
461
462 if (!socdata) {
463 pr_warn("%s: invalid argument!\n", __func__);
464 return -EINVAL;
465 }
466
467 node = of_find_compatible_node(NULL, NULL, "mmio-sram");
468 if (!node) {
469 pr_warn("%s: failed to find ocram node!\n", __func__);
470 return -ENODEV;
471 }
472
473 pdev = of_find_device_by_node(node);
474 if (!pdev) {
475 pr_warn("%s: failed to find ocram device!\n", __func__);
476 ret = -ENODEV;
477 goto put_node;
478 }
479
480 ocram_pool = gen_pool_get(&pdev->dev, NULL);
481 if (!ocram_pool) {
482 pr_warn("%s: ocram pool unavailable!\n", __func__);
483 ret = -ENODEV;
484 goto put_node;
485 }
486
487 ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE);
488 if (!ocram_base) {
489 pr_warn("%s: unable to alloc ocram!\n", __func__);
490 ret = -ENOMEM;
491 goto put_node;
492 }
493
494 ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
495
496 suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
497 MX6Q_SUSPEND_OCRAM_SIZE, false);
498
499 memset(suspend_ocram_base, 0, sizeof(*pm_info));
500 pm_info = suspend_ocram_base;
501 pm_info->pbase = ocram_pbase;
502 pm_info->resume_addr = __pa_symbol(v7_cpu_resume);
503 pm_info->pm_info_size = sizeof(*pm_info);
504
505
506
507
508
509 pm_info->ccm_base.vbase = ccm_base;
510
511 ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat);
512 if (ret) {
513 pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret);
514 goto put_node;
515 }
516
517 ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat);
518 if (ret) {
519 pr_warn("%s: failed to get src base %d!\n", __func__, ret);
520 goto src_map_failed;
521 }
522
523 ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat);
524 if (ret) {
525 pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret);
526 goto iomuxc_map_failed;
527 }
528
529 ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat);
530 if (ret) {
531 pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
532 goto gpc_map_failed;
533 }
534
535 if (socdata->pl310_compat) {
536 ret = imx6_pm_get_base(&pm_info->l2_base, socdata->pl310_compat);
537 if (ret) {
538 pr_warn("%s: failed to get pl310-cache base %d!\n",
539 __func__, ret);
540 goto pl310_cache_map_failed;
541 }
542 }
543
544 pm_info->ddr_type = imx_mmdc_get_ddr_type();
545 pm_info->mmdc_io_num = socdata->mmdc_io_num;
546 mmdc_offset_array = socdata->mmdc_io_offset;
547
548 for (i = 0; i < pm_info->mmdc_io_num; i++) {
549 pm_info->mmdc_io_val[i][0] =
550 mmdc_offset_array[i];
551 pm_info->mmdc_io_val[i][1] =
552 readl_relaxed(pm_info->iomuxc_base.vbase +
553 mmdc_offset_array[i]);
554 }
555
556 imx6_suspend_in_ocram_fn = fncpy(
557 suspend_ocram_base + sizeof(*pm_info),
558 &imx6_suspend,
559 MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
560
561 goto put_node;
562
563pl310_cache_map_failed:
564 iounmap(pm_info->gpc_base.vbase);
565gpc_map_failed:
566 iounmap(pm_info->iomuxc_base.vbase);
567iomuxc_map_failed:
568 iounmap(pm_info->src_base.vbase);
569src_map_failed:
570 iounmap(pm_info->mmdc_base.vbase);
571put_node:
572 of_node_put(node);
573
574 return ret;
575}
576
577static void __init imx6_pm_common_init(const struct imx6_pm_socdata
578 *socdata)
579{
580 struct regmap *gpr;
581 int ret;
582
583 WARN_ON(!ccm_base);
584
585 if (IS_ENABLED(CONFIG_SUSPEND)) {
586 ret = imx6q_suspend_init(socdata);
587 if (ret)
588 pr_warn("%s: No DDR LPM support with suspend %d!\n",
589 __func__, ret);
590 }
591
592
593
594
595
596
597
598
599 gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
600 if (!IS_ERR(gpr))
601 regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
602 IMX6Q_GPR1_GINT);
603}
604
605void __init imx6_pm_ccm_init(const char *ccm_compat)
606{
607 struct device_node *np;
608 u32 val;
609
610 np = of_find_compatible_node(NULL, NULL, ccm_compat);
611 ccm_base = of_iomap(np, 0);
612 BUG_ON(!ccm_base);
613
614
615
616
617
618 val = readl_relaxed(ccm_base + CLPCR);
619 val &= ~BM_CLPCR_LPM;
620 writel_relaxed(val, ccm_base + CLPCR);
621}
622
623void __init imx6q_pm_init(void)
624{
625 imx6_pm_common_init(&imx6q_pm_data);
626}
627
628void __init imx6dl_pm_init(void)
629{
630 imx6_pm_common_init(&imx6dl_pm_data);
631}
632
633void __init imx6sl_pm_init(void)
634{
635 imx6_pm_common_init(&imx6sl_pm_data);
636}
637
638void __init imx6sx_pm_init(void)
639{
640 imx6_pm_common_init(&imx6sx_pm_data);
641}
642
643void __init imx6ul_pm_init(void)
644{
645 imx6_pm_common_init(&imx6ul_pm_data);
646}
647