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 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
100static const u32 imx6q_mmdc_io_offset[] __initconst = {
101 0x5ac, 0x5b4, 0x528, 0x520,
102 0x514, 0x510, 0x5bc, 0x5c4,
103 0x56c, 0x578, 0x588, 0x594,
104 0x5a8, 0x5b0, 0x524, 0x51c,
105 0x518, 0x50c, 0x5b8, 0x5c0,
106 0x784, 0x788, 0x794, 0x79c,
107 0x7a0, 0x7a4, 0x7a8, 0x748,
108 0x59c, 0x5a0, 0x750, 0x774,
109 0x74c,
110};
111
112static const u32 imx6dl_mmdc_io_offset[] __initconst = {
113 0x470, 0x474, 0x478, 0x47c,
114 0x480, 0x484, 0x488, 0x48c,
115 0x464, 0x490, 0x4ac, 0x4b0,
116 0x4bc, 0x4c0, 0x4c4, 0x4c8,
117 0x4cc, 0x4d0, 0x4d4, 0x4d8,
118 0x764, 0x770, 0x778, 0x77c,
119 0x780, 0x784, 0x78c, 0x748,
120 0x4b4, 0x4b8, 0x750, 0x760,
121 0x74c,
122};
123
124static const u32 imx6sl_mmdc_io_offset[] __initconst = {
125 0x30c, 0x310, 0x314, 0x318,
126 0x5c4, 0x5cc, 0x5d4, 0x5d8,
127 0x300, 0x31c, 0x338, 0x5ac,
128 0x33c, 0x340, 0x5b0, 0x5c0,
129 0x330, 0x334, 0x320,
130};
131
132static const u32 imx6sx_mmdc_io_offset[] __initconst = {
133 0x2ec, 0x2f0, 0x2f4, 0x2f8,
134 0x60c, 0x610, 0x61c, 0x620,
135 0x300, 0x2fc, 0x32c, 0x5f4,
136 0x310, 0x314, 0x5f8, 0x608,
137 0x330, 0x334, 0x338, 0x33c,
138};
139
140static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
141 .cpu_type = MXC_CPU_IMX6Q,
142 .mmdc_compat = "fsl,imx6q-mmdc",
143 .src_compat = "fsl,imx6q-src",
144 .iomuxc_compat = "fsl,imx6q-iomuxc",
145 .gpc_compat = "fsl,imx6q-gpc",
146 .mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
147 .mmdc_io_offset = imx6q_mmdc_io_offset,
148};
149
150static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
151 .cpu_type = MXC_CPU_IMX6DL,
152 .mmdc_compat = "fsl,imx6q-mmdc",
153 .src_compat = "fsl,imx6q-src",
154 .iomuxc_compat = "fsl,imx6dl-iomuxc",
155 .gpc_compat = "fsl,imx6q-gpc",
156 .mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
157 .mmdc_io_offset = imx6dl_mmdc_io_offset,
158};
159
160static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
161 .cpu_type = MXC_CPU_IMX6SL,
162 .mmdc_compat = "fsl,imx6sl-mmdc",
163 .src_compat = "fsl,imx6sl-src",
164 .iomuxc_compat = "fsl,imx6sl-iomuxc",
165 .gpc_compat = "fsl,imx6sl-gpc",
166 .mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
167 .mmdc_io_offset = imx6sl_mmdc_io_offset,
168};
169
170static const struct imx6_pm_socdata imx6sx_pm_data __initconst = {
171 .cpu_type = MXC_CPU_IMX6SX,
172 .mmdc_compat = "fsl,imx6sx-mmdc",
173 .src_compat = "fsl,imx6sx-src",
174 .iomuxc_compat = "fsl,imx6sx-iomuxc",
175 .gpc_compat = "fsl,imx6sx-gpc",
176 .mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset),
177 .mmdc_io_offset = imx6sx_mmdc_io_offset,
178};
179
180
181
182
183
184
185
186
187struct imx6_cpu_pm_info {
188 phys_addr_t pbase;
189 phys_addr_t resume_addr;
190 u32 cpu_type;
191 u32 pm_info_size;
192 struct imx6_pm_base mmdc_base;
193 struct imx6_pm_base src_base;
194 struct imx6_pm_base iomuxc_base;
195 struct imx6_pm_base ccm_base;
196 struct imx6_pm_base gpc_base;
197 struct imx6_pm_base l2_base;
198 u32 mmdc_io_num;
199 u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2];
200} __aligned(8);
201
202void imx6q_set_int_mem_clk_lpm(bool enable)
203{
204 u32 val = readl_relaxed(ccm_base + CGPR);
205
206 val &= ~BM_CGPR_INT_MEM_CLK_LPM;
207 if (enable)
208 val |= BM_CGPR_INT_MEM_CLK_LPM;
209 writel_relaxed(val, ccm_base + CGPR);
210}
211
212static void imx6q_enable_rbc(bool enable)
213{
214 u32 val;
215
216
217
218
219
220 imx_gpc_mask_all();
221
222
223 val = readl_relaxed(ccm_base + CCR);
224 val &= ~BM_CCR_RBC_EN;
225 val |= enable ? BM_CCR_RBC_EN : 0;
226 writel_relaxed(val, ccm_base + CCR);
227
228
229 val = readl_relaxed(ccm_base + CCR);
230 val &= ~BM_CCR_RBC_BYPASS_COUNT;
231 val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
232 writel(val, ccm_base + CCR);
233
234
235
236
237
238
239 udelay(65);
240
241
242 imx_gpc_restore_all();
243}
244
245static void imx6q_enable_wb(bool enable)
246{
247 u32 val;
248
249
250 val = readl_relaxed(ccm_base + CLPCR);
251 val &= ~BM_CLPCR_WB_PER_AT_LPM;
252 val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
253 writel_relaxed(val, ccm_base + CLPCR);
254
255
256 val = readl_relaxed(ccm_base + CCR);
257 val &= ~BM_CCR_WB_COUNT;
258 val |= enable ? BM_CCR_WB_COUNT : 0;
259 writel_relaxed(val, ccm_base + CCR);
260}
261
262int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
263{
264 struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
265 u32 val = readl_relaxed(ccm_base + CLPCR);
266
267 val &= ~BM_CLPCR_LPM;
268 switch (mode) {
269 case WAIT_CLOCKED:
270 break;
271 case WAIT_UNCLOCKED:
272 val |= 0x1 << BP_CLPCR_LPM;
273 val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
274 break;
275 case STOP_POWER_ON:
276 val |= 0x2 << BP_CLPCR_LPM;
277 val &= ~BM_CLPCR_VSTBY;
278 val &= ~BM_CLPCR_SBYOS;
279 if (cpu_is_imx6sl())
280 val |= BM_CLPCR_BYPASS_PMIC_READY;
281 if (cpu_is_imx6sl() || cpu_is_imx6sx())
282 val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
283 else
284 val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
285 break;
286 case WAIT_UNCLOCKED_POWER_OFF:
287 val |= 0x1 << BP_CLPCR_LPM;
288 val &= ~BM_CLPCR_VSTBY;
289 val &= ~BM_CLPCR_SBYOS;
290 break;
291 case STOP_POWER_OFF:
292 val |= 0x2 << BP_CLPCR_LPM;
293 val |= 0x3 << BP_CLPCR_STBY_COUNT;
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())
299 val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
300 else
301 val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
302 break;
303 default:
304 return -EINVAL;
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319 imx_gpc_irq_unmask(iomuxc_irq_data);
320 writel_relaxed(val, ccm_base + CLPCR);
321 imx_gpc_irq_mask(iomuxc_irq_data);
322
323 return 0;
324}
325
326static int imx6q_suspend_finish(unsigned long val)
327{
328 if (!imx6_suspend_in_ocram_fn) {
329 cpu_do_idle();
330 } else {
331
332
333
334
335 local_flush_tlb_all();
336 imx6_suspend_in_ocram_fn(suspend_ocram_base);
337 }
338
339 return 0;
340}
341
342static int imx6q_pm_enter(suspend_state_t state)
343{
344 switch (state) {
345 case PM_SUSPEND_STANDBY:
346 imx6q_set_lpm(STOP_POWER_ON);
347 imx6q_set_int_mem_clk_lpm(true);
348 imx_gpc_pre_suspend(false);
349 if (cpu_is_imx6sl())
350 imx6sl_set_wait_clk(true);
351
352 cpu_do_idle();
353 if (cpu_is_imx6sl())
354 imx6sl_set_wait_clk(false);
355 imx_gpc_post_resume();
356 imx6q_set_lpm(WAIT_CLOCKED);
357 break;
358 case PM_SUSPEND_MEM:
359 imx6q_set_lpm(STOP_POWER_OFF);
360 imx6q_set_int_mem_clk_lpm(false);
361 imx6q_enable_wb(true);
362
363
364
365
366 if (!imx6_suspend_in_ocram_fn)
367 imx6q_enable_rbc(true);
368 imx_gpc_pre_suspend(true);
369 imx_anatop_pre_suspend();
370 imx_set_cpu_jump(0, v7_cpu_resume);
371
372 cpu_suspend(0, imx6q_suspend_finish);
373 if (cpu_is_imx6q() || cpu_is_imx6dl())
374 imx_smp_prepare();
375 imx_anatop_post_resume();
376 imx_gpc_post_resume();
377 imx6q_enable_rbc(false);
378 imx6q_enable_wb(false);
379 imx6q_set_int_mem_clk_lpm(true);
380 imx6q_set_lpm(WAIT_CLOCKED);
381 break;
382 default:
383 return -EINVAL;
384 }
385
386 return 0;
387}
388
389static int imx6q_pm_valid(suspend_state_t state)
390{
391 return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
392}
393
394static const struct platform_suspend_ops imx6q_pm_ops = {
395 .enter = imx6q_pm_enter,
396 .valid = imx6q_pm_valid,
397};
398
399void __init imx6q_pm_set_ccm_base(void __iomem *base)
400{
401 ccm_base = base;
402}
403
404static int __init imx6_pm_get_base(struct imx6_pm_base *base,
405 const char *compat)
406{
407 struct device_node *node;
408 struct resource res;
409 int ret = 0;
410
411 node = of_find_compatible_node(NULL, NULL, compat);
412 if (!node) {
413 ret = -ENODEV;
414 goto out;
415 }
416
417 ret = of_address_to_resource(node, 0, &res);
418 if (ret)
419 goto put_node;
420
421 base->pbase = res.start;
422 base->vbase = ioremap(res.start, resource_size(&res));
423 if (!base->vbase)
424 ret = -ENOMEM;
425
426put_node:
427 of_node_put(node);
428out:
429 return ret;
430}
431
432static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
433{
434 phys_addr_t ocram_pbase;
435 struct device_node *node;
436 struct platform_device *pdev;
437 struct imx6_cpu_pm_info *pm_info;
438 struct gen_pool *ocram_pool;
439 unsigned long ocram_base;
440 int i, ret = 0;
441 const u32 *mmdc_offset_array;
442
443 suspend_set_ops(&imx6q_pm_ops);
444
445 if (!socdata) {
446 pr_warn("%s: invalid argument!\n", __func__);
447 return -EINVAL;
448 }
449
450 node = of_find_compatible_node(NULL, NULL, "mmio-sram");
451 if (!node) {
452 pr_warn("%s: failed to find ocram node!\n", __func__);
453 return -ENODEV;
454 }
455
456 pdev = of_find_device_by_node(node);
457 if (!pdev) {
458 pr_warn("%s: failed to find ocram device!\n", __func__);
459 ret = -ENODEV;
460 goto put_node;
461 }
462
463 ocram_pool = dev_get_gen_pool(&pdev->dev);
464 if (!ocram_pool) {
465 pr_warn("%s: ocram pool unavailable!\n", __func__);
466 ret = -ENODEV;
467 goto put_node;
468 }
469
470 ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE);
471 if (!ocram_base) {
472 pr_warn("%s: unable to alloc ocram!\n", __func__);
473 ret = -ENOMEM;
474 goto put_node;
475 }
476
477 ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
478
479 suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
480 MX6Q_SUSPEND_OCRAM_SIZE, false);
481
482 pm_info = suspend_ocram_base;
483 pm_info->pbase = ocram_pbase;
484 pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
485 pm_info->pm_info_size = sizeof(*pm_info);
486
487
488
489
490
491
492 pm_info->ccm_base.vbase = ccm_base;
493
494 ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat);
495 if (ret) {
496 pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret);
497 goto put_node;
498 }
499
500 ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat);
501 if (ret) {
502 pr_warn("%s: failed to get src base %d!\n", __func__, ret);
503 goto src_map_failed;
504 }
505
506 ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat);
507 if (ret) {
508 pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret);
509 goto iomuxc_map_failed;
510 }
511
512 ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat);
513 if (ret) {
514 pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
515 goto gpc_map_failed;
516 }
517
518 ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
519 if (ret) {
520 pr_warn("%s: failed to get pl310-cache base %d!\n",
521 __func__, ret);
522 goto pl310_cache_map_failed;
523 }
524
525 pm_info->cpu_type = socdata->cpu_type;
526 pm_info->mmdc_io_num = socdata->mmdc_io_num;
527 mmdc_offset_array = socdata->mmdc_io_offset;
528
529 for (i = 0; i < pm_info->mmdc_io_num; i++) {
530 pm_info->mmdc_io_val[i][0] =
531 mmdc_offset_array[i];
532 pm_info->mmdc_io_val[i][1] =
533 readl_relaxed(pm_info->iomuxc_base.vbase +
534 mmdc_offset_array[i]);
535 }
536
537 imx6_suspend_in_ocram_fn = fncpy(
538 suspend_ocram_base + sizeof(*pm_info),
539 &imx6_suspend,
540 MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
541
542 goto put_node;
543
544pl310_cache_map_failed:
545 iounmap(&pm_info->gpc_base.vbase);
546gpc_map_failed:
547 iounmap(&pm_info->iomuxc_base.vbase);
548iomuxc_map_failed:
549 iounmap(&pm_info->src_base.vbase);
550src_map_failed:
551 iounmap(&pm_info->mmdc_base.vbase);
552put_node:
553 of_node_put(node);
554
555 return ret;
556}
557
558static void __init imx6_pm_common_init(const struct imx6_pm_socdata
559 *socdata)
560{
561 struct regmap *gpr;
562 int ret;
563
564 WARN_ON(!ccm_base);
565
566 if (IS_ENABLED(CONFIG_SUSPEND)) {
567 ret = imx6q_suspend_init(socdata);
568 if (ret)
569 pr_warn("%s: No DDR LPM support with suspend %d!\n",
570 __func__, ret);
571 }
572
573
574
575
576
577
578
579
580 gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
581 if (!IS_ERR(gpr))
582 regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
583 IMX6Q_GPR1_GINT);
584}
585
586void __init imx6q_pm_init(void)
587{
588 imx6_pm_common_init(&imx6q_pm_data);
589}
590
591void __init imx6dl_pm_init(void)
592{
593 imx6_pm_common_init(&imx6dl_pm_data);
594}
595
596void __init imx6sl_pm_init(void)
597{
598 imx6_pm_common_init(&imx6sl_pm_data);
599}
600
601void __init imx6sx_pm_init(void)
602{
603 imx6_pm_common_init(&imx6sx_pm_data);
604}
605