.. | .. |
---|
3 | 3 | // Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. |
---|
4 | 4 | // http://www.samsung.com |
---|
5 | 5 | // |
---|
6 | | -// EXYNOS - Suspend support |
---|
| 6 | +// Exynos - Suspend support |
---|
7 | 7 | // |
---|
8 | 8 | // Based on arch/arm/mach-s3c2410/pm.c |
---|
9 | 9 | // Copyright (c) 2006 Simtec Electronics |
---|
.. | .. |
---|
30 | 30 | #include <asm/smp_scu.h> |
---|
31 | 31 | #include <asm/suspend.h> |
---|
32 | 32 | |
---|
33 | | -#include <plat/pm-common.h> |
---|
34 | | - |
---|
35 | 33 | #include "common.h" |
---|
| 34 | +#include "smc.h" |
---|
36 | 35 | |
---|
37 | 36 | #define REG_TABLE_END (-1U) |
---|
38 | 37 | |
---|
.. | .. |
---|
59 | 58 | int (*cpu_suspend)(unsigned long); |
---|
60 | 59 | }; |
---|
61 | 60 | |
---|
62 | | -static const struct exynos_pm_data *pm_data __ro_after_init; |
---|
| 61 | +/* Used only on Exynos542x/5800 */ |
---|
| 62 | +struct exynos_pm_state { |
---|
| 63 | + int cpu_state; |
---|
| 64 | + unsigned int pmu_spare3; |
---|
| 65 | + void __iomem *sysram_base; |
---|
| 66 | + phys_addr_t sysram_phys; |
---|
| 67 | + bool secure_firmware; |
---|
| 68 | +}; |
---|
63 | 69 | |
---|
64 | | -static int exynos5420_cpu_state; |
---|
65 | | -static unsigned int exynos_pmu_spare3; |
---|
| 70 | +static const struct exynos_pm_data *pm_data __ro_after_init; |
---|
| 71 | +static struct exynos_pm_state pm_state; |
---|
66 | 72 | |
---|
67 | 73 | /* |
---|
68 | 74 | * GIC wake-up support |
---|
.. | .. |
---|
87 | 93 | { 44, BIT(2) }, /* RTC tick */ |
---|
88 | 94 | { /* sentinel */ }, |
---|
89 | 95 | }; |
---|
| 96 | + |
---|
| 97 | +static u32 exynos_read_eint_wakeup_mask(void) |
---|
| 98 | +{ |
---|
| 99 | + return pmu_raw_readl(EXYNOS_EINT_WAKEUP_MASK); |
---|
| 100 | +} |
---|
90 | 101 | |
---|
91 | 102 | static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) |
---|
92 | 103 | { |
---|
.. | .. |
---|
257 | 268 | unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); |
---|
258 | 269 | unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); |
---|
259 | 270 | |
---|
260 | | - writel_relaxed(0x0, sysram_base_addr + EXYNOS5420_CPU_STATE); |
---|
261 | | - |
---|
262 | | - if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) { |
---|
| 271 | + if (IS_ENABLED(CONFIG_EXYNOS_MCPM)) { |
---|
263 | 272 | mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume); |
---|
264 | 273 | mcpm_cpu_suspend(); |
---|
265 | 274 | } |
---|
.. | .. |
---|
272 | 281 | |
---|
273 | 282 | static void exynos_pm_set_wakeup_mask(void) |
---|
274 | 283 | { |
---|
275 | | - /* Set wake-up mask registers */ |
---|
276 | | - pmu_raw_writel(exynos_get_eint_wake_mask(), EXYNOS_EINT_WAKEUP_MASK); |
---|
277 | | - pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK); |
---|
| 284 | + /* |
---|
| 285 | + * Set wake-up mask registers |
---|
| 286 | + * EXYNOS_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend. |
---|
| 287 | + */ |
---|
| 288 | + pmu_raw_writel(exynos_irqwake_intmask & ~BIT(31), S5P_WAKEUP_MASK); |
---|
278 | 289 | } |
---|
279 | 290 | |
---|
280 | 291 | static void exynos_pm_enter_sleep_mode(void) |
---|
.. | .. |
---|
321 | 332 | /* Set wake-up mask registers */ |
---|
322 | 333 | exynos_pm_set_wakeup_mask(); |
---|
323 | 334 | |
---|
324 | | - exynos_pmu_spare3 = pmu_raw_readl(S5P_PMU_SPARE3); |
---|
| 335 | + pm_state.pmu_spare3 = pmu_raw_readl(S5P_PMU_SPARE3); |
---|
325 | 336 | /* |
---|
326 | 337 | * The cpu state needs to be saved and restored so that the |
---|
327 | 338 | * secondary CPUs will enter low power start. Though the U-Boot |
---|
.. | .. |
---|
329 | 340 | * needs to restore it back in case, the primary cpu fails to |
---|
330 | 341 | * suspend for any reason. |
---|
331 | 342 | */ |
---|
332 | | - exynos5420_cpu_state = readl_relaxed(sysram_base_addr + |
---|
333 | | - EXYNOS5420_CPU_STATE); |
---|
| 343 | + pm_state.cpu_state = readl_relaxed(pm_state.sysram_base + |
---|
| 344 | + EXYNOS5420_CPU_STATE); |
---|
| 345 | + writel_relaxed(0x0, pm_state.sysram_base + EXYNOS5420_CPU_STATE); |
---|
| 346 | + if (pm_state.secure_firmware) |
---|
| 347 | + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(pm_state.sysram_phys + |
---|
| 348 | + EXYNOS5420_CPU_STATE), |
---|
| 349 | + 0, 0); |
---|
334 | 350 | |
---|
335 | 351 | exynos_pm_enter_sleep_mode(); |
---|
336 | 352 | |
---|
337 | 353 | /* ensure at least INFORM0 has the resume address */ |
---|
338 | | - if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) |
---|
| 354 | + if (IS_ENABLED(CONFIG_EXYNOS_MCPM)) |
---|
339 | 355 | pmu_raw_writel(__pa_symbol(mcpm_entry_point), S5P_INFORM0); |
---|
340 | 356 | |
---|
341 | 357 | tmp = pmu_raw_readl(EXYNOS_L2_OPTION(0)); |
---|
.. | .. |
---|
439 | 455 | mpidr = read_cpuid_mpidr(); |
---|
440 | 456 | cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); |
---|
441 | 457 | |
---|
442 | | - if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) |
---|
| 458 | + if (IS_ENABLED(CONFIG_EXYNOS_MCPM)) |
---|
443 | 459 | WARN_ON(mcpm_cpu_powered_up()); |
---|
444 | 460 | |
---|
445 | 461 | if (IS_ENABLED(CONFIG_HW_PERF_EVENTS) && cluster != 0) { |
---|
.. | .. |
---|
467 | 483 | EXYNOS5_ARM_CORE0_SYS_PWR_REG); |
---|
468 | 484 | |
---|
469 | 485 | /* Restore the sysram cpu state register */ |
---|
470 | | - writel_relaxed(exynos5420_cpu_state, |
---|
471 | | - sysram_base_addr + EXYNOS5420_CPU_STATE); |
---|
| 486 | + writel_relaxed(pm_state.cpu_state, |
---|
| 487 | + pm_state.sysram_base + EXYNOS5420_CPU_STATE); |
---|
| 488 | + if (pm_state.secure_firmware) |
---|
| 489 | + exynos_smc(SMC_CMD_REG, |
---|
| 490 | + SMC_REG_ID_SFR_W(pm_state.sysram_phys + |
---|
| 491 | + EXYNOS5420_CPU_STATE), |
---|
| 492 | + EXYNOS_AFTR_MAGIC, 0); |
---|
472 | 493 | |
---|
473 | 494 | pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL, |
---|
474 | 495 | S5P_CENTRAL_SEQ_OPTION); |
---|
.. | .. |
---|
476 | 497 | if (exynos_pm_central_resume()) |
---|
477 | 498 | goto early_wakeup; |
---|
478 | 499 | |
---|
479 | | - pmu_raw_writel(exynos_pmu_spare3, S5P_PMU_SPARE3); |
---|
| 500 | + pmu_raw_writel(pm_state.pmu_spare3, S5P_PMU_SPARE3); |
---|
480 | 501 | |
---|
481 | 502 | early_wakeup: |
---|
482 | 503 | |
---|
.. | .. |
---|
502 | 523 | |
---|
503 | 524 | static int exynos_suspend_enter(suspend_state_t state) |
---|
504 | 525 | { |
---|
| 526 | + u32 eint_wakeup_mask = exynos_read_eint_wakeup_mask(); |
---|
505 | 527 | int ret; |
---|
506 | 528 | |
---|
507 | | - s3c_pm_debug_init(); |
---|
| 529 | + pr_debug("%s: suspending the system...\n", __func__); |
---|
508 | 530 | |
---|
509 | | - S3C_PMDBG("%s: suspending the system...\n", __func__); |
---|
510 | | - |
---|
511 | | - S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__, |
---|
512 | | - exynos_irqwake_intmask, exynos_get_eint_wake_mask()); |
---|
| 531 | + pr_debug("%s: wakeup masks: %08x,%08x\n", __func__, |
---|
| 532 | + exynos_irqwake_intmask, eint_wakeup_mask); |
---|
513 | 533 | |
---|
514 | 534 | if (exynos_irqwake_intmask == -1U |
---|
515 | | - && exynos_get_eint_wake_mask() == -1U) { |
---|
| 535 | + && eint_wakeup_mask == EXYNOS_EINT_WAKEUP_MASK_DISABLED) { |
---|
516 | 536 | pr_err("%s: No wake-up sources!\n", __func__); |
---|
517 | 537 | pr_err("%s: Aborting sleep\n", __func__); |
---|
518 | 538 | return -EINVAL; |
---|
519 | 539 | } |
---|
520 | 540 | |
---|
521 | | - s3c_pm_save_uarts(); |
---|
522 | 541 | if (pm_data->pm_prepare) |
---|
523 | 542 | pm_data->pm_prepare(); |
---|
524 | 543 | flush_cache_all(); |
---|
525 | | - s3c_pm_check_store(); |
---|
526 | 544 | |
---|
527 | 545 | ret = call_firmware_op(suspend); |
---|
528 | 546 | if (ret == -ENOSYS) |
---|
.. | .. |
---|
532 | 550 | |
---|
533 | 551 | if (pm_data->pm_resume_prepare) |
---|
534 | 552 | pm_data->pm_resume_prepare(); |
---|
535 | | - s3c_pm_restore_uarts(); |
---|
536 | 553 | |
---|
537 | | - S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, |
---|
| 554 | + pr_debug("%s: wakeup stat: %08x\n", __func__, |
---|
538 | 555 | pmu_raw_readl(S5P_WAKEUP_STAT)); |
---|
539 | 556 | |
---|
540 | | - s3c_pm_check_restore(); |
---|
541 | | - |
---|
542 | | - S3C_PMDBG("%s: resuming the system...\n", __func__); |
---|
| 557 | + pr_debug("%s: resuming the system...\n", __func__); |
---|
543 | 558 | |
---|
544 | 559 | return 0; |
---|
545 | 560 | } |
---|
.. | .. |
---|
562 | 577 | return ret; |
---|
563 | 578 | } |
---|
564 | 579 | |
---|
565 | | - s3c_pm_check_prepare(); |
---|
566 | | - |
---|
567 | 580 | return 0; |
---|
568 | 581 | } |
---|
569 | 582 | |
---|
570 | 583 | static void exynos_suspend_finish(void) |
---|
571 | 584 | { |
---|
572 | 585 | int ret; |
---|
573 | | - |
---|
574 | | - s3c_pm_check_cleanup(); |
---|
575 | 586 | |
---|
576 | 587 | ret = regulator_suspend_finish(); |
---|
577 | 588 | if (ret) |
---|
.. | .. |
---|
675 | 686 | |
---|
676 | 687 | register_syscore_ops(&exynos_pm_syscore_ops); |
---|
677 | 688 | suspend_set_ops(&exynos_suspend_ops); |
---|
| 689 | + |
---|
| 690 | + /* |
---|
| 691 | + * Applicable as of now only to Exynos542x. If booted under secure |
---|
| 692 | + * firmware, the non-secure region of sysram should be used. |
---|
| 693 | + */ |
---|
| 694 | + if (exynos_secure_firmware_available()) { |
---|
| 695 | + pm_state.sysram_phys = sysram_base_phys; |
---|
| 696 | + pm_state.sysram_base = sysram_ns_base_addr; |
---|
| 697 | + pm_state.secure_firmware = true; |
---|
| 698 | + } else { |
---|
| 699 | + pm_state.sysram_base = sysram_base_addr; |
---|
| 700 | + } |
---|
678 | 701 | } |
---|