diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 18:38:59 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-17 18:38:59 +0100 |
commit | 18656782a820f075cb5c168a2e381a8938b1550a (patch) | |
tree | db431928382a8ae2cc6a153ad28e5bb6a8d5d67e /arch/arm/mach-exynos | |
parent | Merge tag 'dt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm... (diff) | |
parent | Merge tag 'samsung-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/... (diff) | |
download | linux-18656782a820f075cb5c168a2e381a8938b1550a.tar.xz 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')
-rw-r--r-- | arch/arm/mach-exynos/common.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/exynos.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 133 | ||||
-rw-r--r-- | arch/arm/mach-exynos/suspend.c | 4 |
5 files changed, 142 insertions, 5 deletions
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 865f878063cc..f70eca7ee705 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -13,6 +13,7 @@ #define __ARCH_ARM_MACH_EXYNOS_COMMON_H #include <linux/of.h> +#include <linux/platform_data/cpuidle-exynos.h> #define EXYNOS3250_SOC_ID 0xE3472000 #define EXYNOS3_SOC_MASK 0xFFFFF000 @@ -150,8 +151,11 @@ extern void exynos_pm_central_suspend(void); extern int exynos_pm_central_resume(void); extern void exynos_enter_aftr(void); +extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data; + extern void s5p_init_cpu(void __iomem *cpuid_addr); extern unsigned int samsung_rev(void); +extern void __iomem *cpu_boot_reg_base(void); static inline void pmu_raw_writel(u32 val, u32 offset) { diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 78eca99b98d1..2013f73797ed 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -211,6 +211,10 @@ static void __init exynos_dt_machine_init(void) if (!IS_ENABLED(CONFIG_SMP)) exynos_sysram_init(); +#ifdef CONFIG_ARM_EXYNOS_CPUIDLE + if (of_machine_is_compatible("samsung,exynos4210")) + exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data; +#endif if (of_machine_is_compatible("samsung,exynos4210") || of_machine_is_compatible("samsung,exynos4212") || (of_machine_is_compatible("samsung,exynos4412") && diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 7a1ebfeeeeb8..3f32c47a6d74 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -194,7 +194,7 @@ int exynos_cluster_power_state(int cluster) S5P_CORE_LOCAL_PWR_EN); } -static inline void __iomem *cpu_boot_reg_base(void) +void __iomem *cpu_boot_reg_base(void) { if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1) return pmu_base_addr + S5P_INFORM5; 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, +}; diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index 82e6b6fba23f..666ec3e5b03f 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c @@ -319,6 +319,10 @@ static int exynos_pm_suspend(void) { exynos_pm_central_suspend(); + /* Setting SEQ_OPTION register */ + pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, + S5P_CENTRAL_SEQ_OPTION); + if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) exynos_cpu_save_register(); |