diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 09:38:59 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 09:38:59 -0800 |
commit | 18656782a820f075cb5c168a2e381a8938b1550a (patch) | |
tree | db431928382a8ae2cc6a153ad28e5bb6a8d5d67e /arch/arm/mach-exynos/pm.c | |
parent | a233bb742aed62fc6164073d9835135f639b8828 (diff) | |
parent | 6f4554bdff6870c9e0f0b152bbec71d7a0f366f1 (diff) | |
download | linux-18656782a820f075cb5c168a2e381a8938b1550a.tar.gz linux-18656782a820f075cb5c168a2e381a8938b1550a.tar.bz2 linux-18656782a820f075cb5c168a2e381a8938b1550a.zip |
Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson:
"These are changes for drivers that are intimately tied to some SoC and
for some reason could not get merged through the respective subsystem
maintainer tree.
This time around, much of this is for at91, with the bulk of it being
syscon and udc drivers.
Also, there's:
- coupled cpuidle support for Samsung Exynos4210
- Renesas 73A0 common-clk work
- of/platform changes to tear down DMA mappings on device destruction
- a few updates to the TI Keystone knav code"
* tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (26 commits)
cpuidle: exynos: add coupled cpuidle support for exynos4210
ARM: EXYNOS: apply S5P_CENTRAL_SEQ_OPTION fix only when necessary
soc: ti: knav_qmss_queue: change knav_range_setup_acc_irq to static
soc: ti: knav_qmss_queue: makefile tweak to build as dynamic module
pcmcia: at91_cf: depend on !ARCH_MULTIPLATFORM
soc: ti: knav_qmss_queue: export API calls for use by user driver
of/platform: teardown DMA mappings on device destruction
usb: gadget: at91_udc: Allocate udc instance
usb: gadget: at91_udc: Update DT binding documentation
usb: gadget: at91_udc: Rework for multi-platform kernel support
usb: gadget: at91_udc: Simplify probe and remove functions
usb: gadget: at91_udc: Remove non-DT handling code
usb: gadget: at91_udc: Document DT clocks and clock-names property
usb: gadget: at91_udc: Drop uclk clock
usb: gadget: at91_udc: Fix clock names
mfd: syscon: Add Atmel SMC binding doc
mfd: syscon: Add atmel-smc registers definition
mfd: syscon: Add Atmel Matrix bus DT binding documentation
mfd: syscon: Add atmel-matrix registers definition
clk: shmobile: fix sparse NULL pointer warning
...
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 133 |
1 files changed, 129 insertions, 4 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index dfc8594e5b1f..e6209dadc00d 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -98,10 +98,6 @@ void exynos_pm_central_suspend(void) tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); tmp &= ~S5P_CENTRAL_LOWPWR_CFG; pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); - - /* Setting SEQ_OPTION register */ - pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, - S5P_CENTRAL_SEQ_OPTION); } int exynos_pm_central_resume(void) @@ -165,6 +161,13 @@ void exynos_enter_aftr(void) exynos_pm_central_suspend(); + if (of_machine_is_compatible("samsung,exynos4212") || + of_machine_is_compatible("samsung,exynos4412")) { + /* Setting SEQ_OPTION register */ + pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, + S5P_CENTRAL_SEQ_OPTION); + } + cpu_suspend(0, exynos_aftr_finisher); if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { @@ -177,3 +180,125 @@ void exynos_enter_aftr(void) cpu_pm_exit(); } + +static atomic_t cpu1_wakeup = ATOMIC_INIT(0); + +static int exynos_cpu0_enter_aftr(void) +{ + int ret = -1; + + /* + * If the other cpu is powered on, we have to power it off, because + * the AFTR state won't work otherwise + */ + if (cpu_online(1)) { + /* + * We reach a sync point with the coupled idle state, we know + * the other cpu will power down itself or will abort the + * sequence, let's wait for one of these to happen + */ + while (exynos_cpu_power_state(1)) { + /* + * The other cpu may skip idle and boot back + * up again + */ + if (atomic_read(&cpu1_wakeup)) + goto abort; + + /* + * The other cpu may bounce through idle and + * boot back up again, getting stuck in the + * boot rom code + */ + if (__raw_readl(cpu_boot_reg_base()) == 0) + goto abort; + + cpu_relax(); + } + } + + exynos_enter_aftr(); + ret = 0; + +abort: + if (cpu_online(1)) { + /* + * Set the boot vector to something non-zero + */ + __raw_writel(virt_to_phys(exynos_cpu_resume), + cpu_boot_reg_base()); + dsb(); + + /* + * Turn on cpu1 and wait for it to be on + */ + exynos_cpu_power_up(1); + while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN) + cpu_relax(); + + while (!atomic_read(&cpu1_wakeup)) { + /* + * Poke cpu1 out of the boot rom + */ + __raw_writel(virt_to_phys(exynos_cpu_resume), + cpu_boot_reg_base()); + + arch_send_wakeup_ipi_mask(cpumask_of(1)); + } + } + + return ret; +} + +static int exynos_wfi_finisher(unsigned long flags) +{ + cpu_do_idle(); + + return -1; +} + +static int exynos_cpu1_powerdown(void) +{ + int ret = -1; + + /* + * Idle sequence for cpu1 + */ + if (cpu_pm_enter()) + goto cpu1_aborted; + + /* + * Turn off cpu 1 + */ + exynos_cpu_power_down(1); + + ret = cpu_suspend(0, exynos_wfi_finisher); + + cpu_pm_exit(); + +cpu1_aborted: + dsb(); + /* + * Notify cpu 0 that cpu 1 is awake + */ + atomic_set(&cpu1_wakeup, 1); + + return ret; +} + +static void exynos_pre_enter_aftr(void) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base()); +} + +static void exynos_post_enter_aftr(void) +{ + atomic_set(&cpu1_wakeup, 0); +} + +struct cpuidle_exynos_data cpuidle_coupled_exynos_data = { + .cpu0_enter_aftr = exynos_cpu0_enter_aftr, + .cpu1_powerdown = exynos_cpu1_powerdown, + .pre_enter_aftr = exynos_pre_enter_aftr, + .post_enter_aftr = exynos_post_enter_aftr, +}; |