diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-03-29 02:02:56 +0100 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-03-29 02:02:56 +0100 |
commit | 2c793fa3493d4e7b7f6cc3392458ef9d94884dac (patch) | |
tree | f8718b2203759197fc93b8c321e51580336e0174 /arch/arm | |
parent | c441ab93d2b0ffec1852c1fd2186d262c2031d16 (diff) | |
parent | 8dec067dc9c59f9fdcaf4357c22994cde3647eb8 (diff) |
Merge tag 'samsung-pm-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/cleanup3
Merge "Samsung PM related 2nd updates for v3.15" from Kukjin Kim:
From Tomasz Figa <t.figa@samsung.com>:
Current Samsung PM code is heavily unprepared for multiplatform
systems. The design implies accessing functions and global
variables defined in particular mach- subdirectory from common
code in plat-, which is not allowed when building ARCH_MULTIPLATFORM.
In addition there is a lot of forced code unification, which makes
common function handle any possible quirks of all supported SoCs.
In the end this design turned out to not work too well, ending with
a lot of empty functions exported from mach-, just because code in
common pm.c calls them. Moreover, recent trend of moving lower level
suspend/resume code to proper drivers, like pinctrl or clk, made a
lot of code there redundant, especially on DT-only platforms like
Exynos.
Note that this branch is based on previous tags/samsung-pm-1 and merge
tags/samsung-cleanup-2 because of fix build error from recent changes
of <linux/serial_s3c.h>
* tag 'samsung-pm-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung:
ARM: EXYNOS: Fix compilation error in cpuidle.c
ARM: S5P64X0: Explicitly include linux/serial_s3c.h in mach/pm-core.h
ARM: S3C64XX: Fix build for implicit serial_s3c.h inclusion
serial: s3c: Fix build of header without serial_core.h preinclusion
ARM: EXYNOS: Allow wake-up using GIC interrupts
ARM: EXYNOS: Stop using legacy Samsung PM code
ARM: EXYNOS: Remove PM initcalls and useless indirection
ARM: EXYNOS: Fix abuse of CONFIG_PM
ARM: SAMSUNG: Move s3c_pm_check_* prototypes to plat/pm-common.h
ARM: SAMSUNG: Move common save/restore helpers to separate file
ARM: SAMSUNG: Move Samsung PM debug code into separate file
ARM: SAMSUNG: Consolidate PM debug functions
ARM: SAMSUNG: Use debug_ll_addr() to get UART base address
ARM: SAMSUNG: Save UART DIVSLOT register based on SoC type
ARM: SAMSUNG: Add soc_is_s3c2410() helper
ARM: EXYNOS: Do not resume l2x0 if not enabled before suspend
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm')
22 files changed, 535 insertions, 400 deletions
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 4c414af75ef..fcd78c201c0 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -24,7 +24,7 @@ config ARCH_EXYNOS4 select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 select PINCTRL - select PM_GENERIC_DOMAINS if PM + select PM_GENERIC_DOMAINS if PM_RUNTIME select S5P_DEV_MFC help Samsung EXYNOS4 SoCs based systems @@ -47,10 +47,8 @@ config CPU_EXYNOS4210 default y depends on ARCH_EXYNOS4 select ARCH_HAS_BANDGAP - select ARM_CPU_SUSPEND if PM + select ARM_CPU_SUSPEND if PM_SLEEP select PINCTRL_EXYNOS - select S5P_PM if PM - select S5P_SLEEP if PM select SAMSUNG_DMADEV help Enable EXYNOS4210 CPU support @@ -61,8 +59,6 @@ config SOC_EXYNOS4212 depends on ARCH_EXYNOS4 select ARCH_HAS_BANDGAP select PINCTRL_EXYNOS - select S5P_PM if PM - select S5P_SLEEP if PM select SAMSUNG_DMADEV help Enable EXYNOS4212 SoC support @@ -83,9 +79,7 @@ config SOC_EXYNOS5250 depends on ARCH_EXYNOS5 select ARCH_HAS_BANDGAP select PINCTRL_EXYNOS - select PM_GENERIC_DOMAINS if PM - select S5P_PM if PM - select S5P_SLEEP if PM + select PM_GENERIC_DOMAINS if PM_RUNTIME select S5P_DEV_MFC select SAMSUNG_DMADEV help @@ -95,9 +89,7 @@ config SOC_EXYNOS5420 bool "SAMSUNG EXYNOS5420" default y depends on ARCH_EXYNOS5 - select PM_GENERIC_DOMAINS if PM - select S5P_PM if PM - select S5P_SLEEP if PM + select PM_GENERIC_DOMAINS if PM_RUNTIME help Enable EXYNOS5420 SoC support diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 8930b66b4ab..58fe9e6f542 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -14,7 +14,7 @@ obj- := obj-$(CONFIG_ARCH_EXYNOS) += common.o -obj-$(CONFIG_S5P_PM) += pm.o +obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 025fd8215ca..e98ddadc5f7 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -315,6 +315,7 @@ void __init exynos_init_late(void) return; pm_genpd_poweroff_unused(); + exynos_pm_init(); } static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname, diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index f76967b1c55..aba6a2ad7d1 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -27,6 +27,20 @@ void exynos_init_late(void); void exynos_firmware_init(void); +#ifdef CONFIG_PINCTRL_EXYNOS +extern u32 exynos_get_eint_wake_mask(void); +#else +static inline u32 exynos_get_eint_wake_mask(void) { return 0xffffffff; } +#endif + +#ifdef CONFIG_PM_SLEEP +extern void __init exynos_pm_init(void); +#else +static inline void exynos_pm_init(void) {} +#endif + +extern void exynos_cpu_resume(void); + extern struct smp_operations exynos_smp_ops; extern void exynos_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c index f57cb91f02a..b530231130a 100644 --- a/arch/arm/mach-exynos/cpuidle.c +++ b/arch/arm/mach-exynos/cpuidle.c @@ -26,7 +26,6 @@ #include <plat/cpu.h> #include <plat/pm.h> -#include <mach/pm-core.h> #include <mach/map.h> #include "common.h" @@ -127,7 +126,7 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, /* Set value of power down register for aftr mode */ exynos_sys_powerdown_conf(SYS_AFTR); - __raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR); + __raw_writel(virt_to_phys(exynos_cpu_resume), REG_DIRECTGO_ADDR); __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); save_cpu_arch_register(); diff --git a/arch/arm/mach-exynos/include/mach/pm-core.h b/arch/arm/mach-exynos/include/mach/pm-core.h deleted file mode 100644 index dc0697c2fa9..00000000000 --- a/arch/arm/mach-exynos/include/mach/pm-core.h +++ /dev/null @@ -1,75 +0,0 @@ -/* linux/arch/arm/mach-exynos4/include/mach/pm-core.h - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Based on arch/arm/mach-s3c2410/include/mach/pm-core.h, - * Copyright 2008 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> - * http://armlinux.simtec.co.uk/ - * - * EXYNOS4210 - PM core support for arch/arm/plat-s5p/pm.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_PM_CORE_H -#define __ASM_ARCH_PM_CORE_H __FILE__ - -#include <linux/of.h> -#include <mach/map.h> - -#define S5P_EINT_WAKEUP_MASK (S5P_VA_PMU + 0x0604) -#define S5P_WAKEUP_MASK (S5P_VA_PMU + 0x0608) - -#ifdef CONFIG_PINCTRL_EXYNOS -extern u32 exynos_get_eint_wake_mask(void); -#else -static inline u32 exynos_get_eint_wake_mask(void) { return 0xffffffff; } -#endif - -static inline void s3c_pm_debug_init_uart(void) -{ - /* nothing here yet */ -} - -static inline void s3c_pm_arch_prepare_irqs(void) -{ - __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK); - __raw_writel(s3c_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK); -} - -static inline void s3c_pm_arch_stop_clocks(void) -{ - /* nothing here yet */ -} - -static inline void s3c_pm_arch_show_resume_irqs(void) -{ - /* nothing here yet */ -} - -static inline void s3c_pm_arch_update_uart(void __iomem *regs, - struct pm_uart_save *save) -{ - /* nothing here yet */ -} - -static inline void s3c_pm_restored_gpios(void) -{ - /* nothing here yet */ -} - -static inline void samsung_pm_saved_gpios(void) -{ - /* nothing here yet */ -} - -/* Compatibility definitions to make plat-samsung/pm.c compile */ -#define IRQ_EINT_BIT(x) 1 -#define s3c_irqwake_intallow 0 -#define s3c_irqwake_eintallow 0 - -#endif /* __ASM_ARCH_PM_CORE_H */ diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index ba18214c9ac..15af0ceb0a6 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -17,24 +17,35 @@ #include <linux/suspend.h> #include <linux/syscore_ops.h> #include <linux/io.h> +#include <linux/irqchip/arm-gic.h> #include <linux/err.h> #include <linux/clk.h> #include <asm/cacheflush.h> #include <asm/hardware/cache-l2x0.h> #include <asm/smp_scu.h> +#include <asm/suspend.h> #include <plat/cpu.h> -#include <plat/pm.h> +#include <plat/pm-common.h> #include <plat/pll.h> #include <plat/regs-srom.h> #include <mach/map.h> -#include <mach/pm-core.h> #include "common.h" #include "regs-pmu.h" +/** + * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping + * @hwirq: Hardware IRQ signal of the GIC + * @mask: Mask in PMU wake-up mask register + */ +struct exynos_wkup_irq { + unsigned int hwirq; + u32 mask; +}; + static struct sleep_save exynos5_sys_save[] = { SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), }; @@ -48,6 +59,46 @@ static struct sleep_save exynos_core_save[] = { SAVE_ITEM(S5P_SROM_BC3), }; +/* + * GIC wake-up support + */ + +static u32 exynos_irqwake_intmask = 0xffffffff; + +static const struct exynos_wkup_irq exynos4_wkup_irq[] = { + { 76, BIT(1) }, /* RTC alarm */ + { 77, BIT(2) }, /* RTC tick */ + { /* sentinel */ }, +}; + +static const struct exynos_wkup_irq exynos5250_wkup_irq[] = { + { 75, BIT(1) }, /* RTC alarm */ + { 76, BIT(2) }, /* RTC tick */ + { /* sentinel */ }, +}; + +static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) +{ + const struct exynos_wkup_irq *wkup_irq; + + if (soc_is_exynos5250()) + wkup_irq = exynos5250_wkup_irq; + else + wkup_irq = exynos4_wkup_irq; + + while (wkup_irq->mask) { + if (wkup_irq->hwirq == data->hwirq) { + if (!state) + exynos_irqwake_intmask |= wkup_irq->mask; + else + exynos_irqwake_intmask &= ~wkup_irq->mask; + return 0; + } + ++wkup_irq; + } + + return -ENOENT; +} /* For Cortex-A9 Diagnostic and Power control register */ static unsigned int save_arm_register[2]; @@ -72,6 +123,10 @@ static void exynos_pm_prepare(void) { unsigned int tmp; + /* Set wake-up mask registers */ + __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK); + __raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK); + s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); if (soc_is_exynos5250()) { @@ -89,41 +144,8 @@ static void exynos_pm_prepare(void) /* ensure at least INFORM0 has the resume address */ - __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); -} - -static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) -{ - pm_cpu_prep = exynos_pm_prepare; - pm_cpu_sleep = exynos_cpu_suspend; - - return 0; -} - -static struct subsys_interface exynos_pm_interface = { - .name = "exynos_pm", - .subsys = &exynos_subsys, - .add_dev = exynos_pm_add, -}; - -static __init int exynos_pm_drvinit(void) -{ - unsigned int tmp; - - if (soc_is_exynos5440()) - return 0; - - s3c_pm_init(); - - /* All wakeup disable */ - - tmp = __raw_readl(S5P_WAKEUP_MASK); - tmp |= ((0xFF << 8) | (0x1F << 1)); - __raw_writel(tmp, S5P_WAKEUP_MASK); - - return subsys_interface_register(&exynos_pm_interface); + __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); } -arch_initcall(exynos_pm_drvinit); static int exynos_pm_suspend(void) { @@ -220,12 +242,80 @@ static struct syscore_ops exynos_pm_syscore_ops = { .resume = exynos_pm_resume, }; -static __init int exynos_pm_syscore_init(void) +/* + * Suspend Ops + */ + +static int exynos_suspend_enter(suspend_state_t state) { - if (soc_is_exynos5440()) - return 0; + int ret; + + s3c_pm_debug_init(); + + S3C_PMDBG("%s: suspending the system...\n", __func__); + + S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__, + exynos_irqwake_intmask, exynos_get_eint_wake_mask()); + + if (exynos_irqwake_intmask == -1U + && exynos_get_eint_wake_mask() == -1U) { + pr_err("%s: No wake-up sources!\n", __func__); + pr_err("%s: Aborting sleep\n", __func__); + return -EINVAL; + } + + s3c_pm_save_uarts(); + exynos_pm_prepare(); + flush_cache_all(); + s3c_pm_check_store(); + + ret = cpu_suspend(0, exynos_cpu_suspend); + if (ret) + return ret; + + s3c_pm_restore_uarts(); + + S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, + __raw_readl(S5P_WAKEUP_STAT)); + + s3c_pm_check_restore(); + + S3C_PMDBG("%s: resuming the system...\n", __func__); - register_syscore_ops(&exynos_pm_syscore_ops); return 0; } -arch_initcall(exynos_pm_syscore_init); + +static int exynos_suspend_prepare(void) +{ + s3c_pm_check_prepare(); + + return 0; +} + +static void exynos_suspend_finish(void) +{ + s3c_pm_check_cleanup(); +} + +static const struct platform_suspend_ops exynos_suspend_ops = { + .enter = exynos_suspend_enter, + .prepare = exynos_suspend_prepare, + .finish = exynos_suspend_finish, + .valid = suspend_valid_only_mem, +}; + +void __init exynos_pm_init(void) +{ + u32 tmp; + + /* Platform-specific GIC callback */ + gic_arch_extn.irq_set_wake = exynos_irq_set_wake; + + /* All wakeup disable */ + tmp = __raw_readl(S5P_WAKEUP_MASK); + tmp |= ((0xFF << 8) | (0x1F << 1)); + __raw_writel(tmp, S5P_WAKEUP_MASK); + + register_syscore_ops(&exynos_pm_syscore_ops); + suspend_set_ops(&exynos_suspend_ops); +} diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h index 7c029ce2771..2c15a8fbcb5 100644 --- a/arch/arm/mach-exynos/regs-pmu.h +++ b/arch/arm/mach-exynos/regs-pmu.h @@ -31,6 +31,8 @@ #define EXYNOS5440_SWRESET S5P_PMUREG(0x00C4) #define S5P_WAKEUP_STAT S5P_PMUREG(0x0600) +#define S5P_EINT_WAKEUP_MASK S5P_PMUREG(0x0604) +#define S5P_WAKEUP_MASK S5P_PMUREG(0x0608) #define S5P_INFORM0 S5P_PMUREG(0x0800) #define S5P_INFORM1 S5P_PMUREG(0x0804) diff --git a/arch/arm/mach-exynos/sleep.S b/arch/arm/mach-exynos/sleep.S new file mode 100644 index 00000000000..a2613e944e1 --- /dev/null +++ b/arch/arm/mach-exynos/sleep.S @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Exynos low-level resume code + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/hardware/cache-l2x0.h> + +#define CPU_MASK 0xff0ffff0 +#define CPU_CORTEX_A9 0x410fc090 + + /* + * The following code is located into the .data section. This is to + * allow l2x0_regs_phys to be accessed with a relative load while we + * can't rely on any MMU translation. We could have put l2x0_regs_phys + * in the .text section as well, but some setups might insist on it to + * be truly read-only. (Reference from: arch/arm/kernel/sleep.S) + */ + .data + .align + + /* + * sleep magic, to allow the bootloader to check for an valid + * image to resume to. Must be the first word before the + * exynos_cpu_resume entry. + */ + + .word 0x2bedf00d + + /* + * exynos_cpu_resume + * + * resume code entry for bootloader to call + */ + +ENTRY(exynos_cpu_resume) +#ifdef CONFIG_CACHE_L2X0 + mrc p15, 0, r0, c0, c0, 0 + ldr r1, =CPU_MASK + and r0, r0, r1 + ldr r1, =CPU_CORTEX_A9 + cmp r0, r1 + bne skip_l2_resume + adr r0, l2x0_regs_phys + ldr r0, [r0] + cmp r0, #0 + beq skip_l2_resume + ldr r1, [r0, #L2X0_R_PHY_BASE] + ldr r2, [r1, #L2X0_CTRL] + tst r2, #0x1 + bne skip_l2_resume + ldr r2, [r0, #L2X0_R_AUX_CTRL] + str r2, [r1, #L2X0_AUX_CTRL] + ldr r2, [r0, #L2X0_R_TAG_LATENCY] + str r2, [r1, #L2X0_TAG_LATENCY_CTRL] + ldr r2, [r0, #L2X0_R_DATA_LATENCY] + str r2, [r1, #L2X0_DATA_LATENCY_CTRL] + ldr r2, [r0, #L2X0_R_PREFETCH_CTRL] + str r2, [r1, #L2X0_PREFETCH_CTRL] + ldr r2, [r0, #L2X0_R_PWR_CTRL] + str r2, [r1, #L2X0_POWER_CTRL] + mov r2, #1 + str r2, [r1, #L2X0_CTRL] +skip_l2_resume: +#endif + b cpu_resume +ENDPROC(exynos_cpu_resume) +#ifdef CONFIG_CACHE_L2X0 + .globl l2x0_regs_phys +l2x0_regs_phys: + .long 0 +#endif diff --git a/arch/arm/mach-s3c64xx/include/mach/pm-core.h b/arch/arm/mach-s3c64xx/include/mach/pm-core.h index c0537f40a3d..a30a1e3ffc6 100644 --- a/arch/arm/mach-s3c64xx/include/mach/pm-core.h +++ b/arch/arm/mach-s3c64xx/include/mach/pm-core.h @@ -15,6 +15,8 @@ #ifndef __MACH_S3C64XX_PM_CORE_H #define __MACH_S3C64XX_PM_CORE_H __FILE__ +#include <linux/serial_s3c.h> + #include <mach/regs-gpio.h> static inline void s3c_pm_debug_init_uart(void) diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c index b5a66986a52..6b37694fa33 100644 --- a/arch/arm/mach-s3c64xx/pm.c +++ b/arch/arm/mach-s3c64xx/pm.c @@ -332,7 +332,6 @@ static __init int s3c64xx_pm_initcall(void) { pm_cpu_prep = s3c64xx_pm_prepare; pm_cpu_sleep = s3c64xx_cpu_suspend; - pm_uart_udivslot = 1; #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK gpio_request(S3C64XX_GPN(12), "DEBUG_LED0"); diff --git a/arch/arm/mach-s5p64x0/include/mach/pm-core.h b/arch/arm/mach-s5p64x0/include/mach/pm-core.h index e52f7545d3a..1e0eb65b2b8 100644 --- a/arch/arm/mach-s5p64x0/include/mach/pm-core.h +++ b/arch/arm/mach-s5p64x0/include/mach/pm-core.h @@ -12,6 +12,8 @@ * published by the Free Software Foundation. */ +#include <linux/serial_s3c.h> + #include <mach/regs-gpio.h> static inline void s3c_pm_debug_init_uart(void) diff --git a/arch/arm/mach-s5p64x0/pm.c b/arch/arm/mach-s5p64x0/pm.c index 861e15cea69..ec8229cee71 100644 --- a/arch/arm/mach-s5p64x0/pm.c +++ b/arch/arm/mach-s5p64x0/pm.c @@ -161,7 +161,6 @@ static int s5p64x0_pm_add(struct device *dev, struct subsys_interface *sif) { pm_cpu_prep = s5p64x0_pm_prepare; pm_cpu_sleep = s5p64x0_cpu_suspend; - pm_uart_udivslot = 1; return 0; } diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 9267d29549b..25c826ed3b6 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -47,9 +47,11 @@ obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o # PM support +obj-$(CONFIG_PM_SLEEP) += pm-common.o obj-$(CONFIG_SAMSUNG_PM) += pm.o obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o +obj-$(CONFIG_SAMSUNG_PM_DEBUG) += pm-debug.o obj-$(CONFIG_SAMSUNG_WAKEMASK) += wakeup-mask.o obj-$(CONFIG_SAMSUNG_WDT_RESET) += watchdog-reset.o diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h index 31164b34d4c..d762533b856 100644 --- a/arch/arm/plat-samsung/include/plat/cpu.h +++ b/arch/arm/plat-samsung/include/plat/cpu.h @@ -20,6 +20,9 @@ extern unsigned long samsung_cpu_id; +#define S3C2410_CPU_ID 0x32410000 +#define S3C2410_CPU_MASK 0xFFFFFFFF + #define S3C24XX_CPU_ID 0x32400000 #define S3C24XX_CPU_MASK 0xFFF00000 @@ -56,6 +59,7 @@ static inline int is_samsung_##name(void) \ return ((samsung_cpu_id & mask) == (id & mask)); \ } +IS_SAMSUNG_CPU(s3c2410, S3C2410_CPU_ID, S3C2410_CPU_MASK) IS_SAMSUNG_CPU(s3c24xx, S3C24XX_CPU_ID, S3C24XX_CPU_MASK) IS_SAMSUNG_CPU(s3c2412, S3C2412_CPU_ID, S3C2412_CPU_MASK) IS_SAMSUNG_CPU(s3c6400, S3C6400_CPU_ID, S3C64XX_CPU_MASK) @@ -76,8 +80,10 @@ IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK) defined(CONFIG_CPU_S3C2442) || defined(CONFIG_CPU_S3C244X) || \ defined(CONFIG_CPU_S3C2443) # define soc_is_s3c24xx() is_samsung_s3c24xx() +# define soc_is_s3c2410() is_samsung_s3c2410() #else # define soc_is_s3c24xx() 0 +# define soc_is_s3c2410() 0 #endif #if defined(CONFIG_CPU_S3C2412) diff --git a/arch/arm/plat-samsung/include/plat/pm-common.h b/arch/arm/plat-samsung/include/plat/pm-common.h new file mode 100644 index 00000000000..8705f9e0e28 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/pm-common.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Tomasz Figa <t.figa@samsung.com> + * Copyright (c) 2004 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Written by Ben Dooks, <ben@simtec.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef __PLAT_SAMSUNG_PM_COMMON_H +#define __PLAT_SAMSUNG_PM_COMMON_H __FILE__ + +#include <linux/irq.h> + +/* sleep save info */ + +/** + * struct sleep_save - save information for shared peripherals. + * @reg: Pointer to the register to save. + * @val: Holder for the value saved from reg. + * + * This describes a list of registers which is used by the pm core and + * other subsystem to save and restore register values over suspend. + */ +struct sleep_save { + void __iomem *reg; + unsigned long val; +}; + +#define SAVE_ITEM(x) \ + { .reg = (x) } + +/* helper functions to save/restore lists of registers. */ + +extern void s3c_pm_do_save(struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count); +extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count); + +/* PM debug functions */ + +/** + * struct pm_uart_save - save block for core UART + * @ulcon: Save value for S3C2410_ULCON + * @ucon: Save value for S3C2410_UCON + * @ufcon: Save value for S3C2410_UFCON + * @umcon: Save value for S3C2410_UMCON + * @ubrdiv: Save value for S3C2410_UBRDIV + * + * Save block for UART registers to be held over sleep and restored if they + * are needed (say by debug). +*/ +struct pm_uart_save { + u32 ulcon; + u32 ucon; + u32 ufcon; + u32 umcon; + u32 ubrdiv; + u32 udivslot; +}; + +#ifdef CONFIG_SAMSUNG_PM_DEBUG +/** + * s3c_pm_dbg() - low level debug function for use in suspend/resume. + * @msg: The message to print. + * + * This function is used mainly to debug the resume process before the system + * can rely on printk/console output. It uses the low-level debugging output + * routine printascii() to do its work. + */ +extern void s3c_pm_dbg(const char *msg, ...); + +/** + * s3c_pm_debug_init() - suspend/resume low level debug initialization. + * @base: Virtual base of UART to use for suspend/resume debugging. + * + * This function needs to be called before S3C_PMDBG() can be used, to set up + * UART port base address and configuration. + */ +extern void s3c_pm_debug_init(void); + +#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt) + +extern void s3c_pm_save_uarts(void); +extern void s3c_pm_restore_uarts(void); +#else +#define S3C_PMDBG(fmt...) pr_debug(fmt) +#define s3c_pm_debug_init() do { } while (0) + +static inline void s3c_pm_save_uarts(void) { } +static inline void s3c_pm_restore_uarts(void) { } +#endif + +/* suspend memory checking */ + +#ifdef CONFIG_SAMSUNG_PM_CHECK +extern void s3c_pm_check_prepare(void); +extern void s3c_pm_check_restore(void); +extern void s3c_pm_check_cleanup(void); +extern void s3c_pm_check_store(void); +#else +#define s3c_pm_check_prepare() do { } while (0) +#define s3c_pm_check_restore() do { } while (0) +#define s3c_pm_check_cleanup() do { } while (0) +#define s3c_pm_check_store() do { } while (0) +#endif + +#endif diff --git a/arch/arm/plat-samsung/include/plat/pm.h b/arch/arm/plat-samsung/include/plat/pm.h index ff6063f0d5e..e17d871b934 100644 --- a/arch/arm/plat-samsung/include/plat/pm.h +++ b/arch/arm/plat-samsung/include/plat/pm.h @@ -15,7 +15,7 @@ * management */ -#include <linux/irq.h> +#include <plat/pm-common.h> struct device; @@ -54,56 +54,10 @@ extern int (*pm_cpu_sleep)(unsigned long); extern unsigned long s3c_pm_flags; -extern unsigned char pm_uart_udivslot; /* true to save UART UDIVSLOT */ - /* from sleep.S */ extern int s3c2410_cpu_suspend(unsigned long); -/* sleep save info */ - -/** - * struct sleep_save - save information for shared peripherals. - * @reg: Pointer to the register to save. - * @val: Holder for the value saved from reg. - * - * This describes a list of registers which is used by the pm core and - * other subsystem to save and restore register values over suspend. - */ -struct sleep_save { - void __iomem *reg; - unsigned long val; -}; - -#define SAVE_ITEM(x) \ - { .reg = (x) } - -/** - * struct pm_uart_save - save block for core UART - * @ulcon: Save value for S3C2410_ULCON - * @ucon: Save value for S3C2410_UCON - * @ufcon: Save value for S3C2410_UFCON - * @umcon: Save value for S3C2410_UMCON - * @ubrdiv: Save value for S3C2410_UBRDIV - * - * Save block for UART registers to be held over sleep and restored if they - * are needed (say by debug). -*/ -struct pm_uart_save { - u32 ulcon; - u32 ucon; - u32 ufcon; - u32 umcon; - u32 ubrdiv; - u32 udivslot; -}; - -/* helper functions to save/restore lists of registers. */ - -extern void s3c_pm_do_save(struct sleep_save *ptr, int count); -extern void s3c_pm_do_restore(const struct sleep_save *ptr, int count); -extern void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count); - #ifdef CONFIG_SAMSUNG_PM extern int s3c_irq_wake(struct irq_data *data, unsigned int state); extern int s3c_irqext_wake(struct irq_data *data, unsigned int state); @@ -114,24 +68,6 @@ extern void s3c_cpu_resume(void); #define s3c_cpu_resume NULL #endif -/* PM debug functions */ - -#ifdef CONFIG_SAMSUNG_PM_DEBUG -/** - * s3c_pm_dbg() - low level debug function for use in suspend/resume. - * @msg: The message to print. - * - * This function is used mainly to debug the resume process before the system - * can rely on printk/console output. It uses the low-level debugging output - * routine printascii() to do its work. - */ -extern void s3c_pm_dbg(const char *msg, ...); - -#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt) -#else -#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) -#endif - #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK /** * s3c_pm_debug_smdkled() - Debug PM suspend/resume via SMDK Board LEDs @@ -144,20 +80,6 @@ extern void s3c_pm_debug_smdkled(u32 set, u32 clear); static inline void s3c_pm_debug_smdkled(u32 set, u32 clear) { } #endif /* CONFIG_S3C_PM_DEBUG_LED_SMDK */ -/* suspend memory checking */ - -#ifdef CONFIG_SAMSUNG_PM_CHECK -extern void s3c_pm_check_prepare(void); -extern void s3c_pm_check_restore(void); -extern void s3c_pm_check_cleanup(void); -extern void s3c_pm_check_store(void); -#else -#define s3c_pm_check_prepare() do { } while(0) -#define s3c_pm_check_restore() do { } while(0) -#define s3c_pm_check_cleanup() do { } while(0) -#define s3c_pm_check_store() do { } while(0) -#endif - /** * s3c_pm_configure_extint() - ensure pins are correctly set for IRQ * diff --git a/arch/arm/plat-samsung/pm-check.c b/arch/arm/plat-samsung/pm-check.c index 3cbd62666b1..04aff2c31b4 100644 --- a/arch/arm/plat-samsung/pm-check.c +++ b/arch/arm/plat-samsung/pm-check.c @@ -19,7 +19,7 @@ #include <linux/ioport.h> #include <linux/slab.h> -#include <plat/pm.h> +#include <plat/pm-common.h> #if CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE < 1 #error CONFIG_SAMSUNG_PM_CHECK_CHUNKSIZE must be a positive non-zero value diff --git a/arch/arm/plat-samsung/pm-common.c b/arch/arm/plat-samsung/pm-common.c new file mode 100644 index 00000000000..515cd53372b --- /dev/null +++ b/arch/arm/plat-samsung/pm-common.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Tomasz Figa <t.figa@samsung.com> + * Copyright (C) 2008 Openmoko, Inc. + * Copyright (C) 2004-2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Samsung common power management helper functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/io.h> +#include <linux/kernel.h> + +#include <plat/pm-common.h> + +/* helper functions to save and restore register state */ + +/** + * s3c_pm_do_save() - save a set of registers for restoration on resume. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Run through the list of registers given, saving their contents in the + * array for later restoration when we wakeup. + */ +void s3c_pm_do_save(struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + ptr->val = __raw_readl(ptr->reg); + S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); + } +} + +/** + * s3c_pm_do_restore() - restore register values from the save list. + * @ptr: Pointer to an array of registers. + * @count: Size of the ptr array. + * + * Restore the register values saved from s3c_pm_do_save(). + * + * Note, we do not use S3C_PMDBG() in here, as the system may not have + * restore the UARTs state yet +*/ + +void s3c_pm_do_restore(const struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) { + pr_debug("restore %p (restore %08lx, was %08x)\n", + ptr->reg, ptr->val, __raw_readl(ptr->reg)); + + __raw_writel(ptr->val, ptr->reg); + } +} + +/** + * s3c_pm_do_restore_core() - early restore register values from save list. + * + * This is similar to s3c_pm_do_restore() except we try and minimise the + * side effects of the function in case registers that hardware might need + * to work has been restored. + * + * WARNING: Do not put any debug in here that may effect memory or use + * peripherals, as things may be changing! +*/ + +void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count) +{ + for (; count > 0; count--, ptr++) + __raw_writel(ptr->val, ptr->reg); +} diff --git a/arch/arm/plat-samsung/pm-debug.c b/arch/arm/plat-samsung/pm-debug.c new file mode 100644 index 00000000000..8f19f66388d --- /dev/null +++ b/arch/arm/plat-samsung/pm-debug.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Tomasz Figa <t.figa@samsung.com> + * Copyright (C) 2008 Openmoko, Inc. + * Copyright (C) 2004-2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * Samsung common power management (suspend to RAM) debug support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/serial_core.h> +#include <linux/io.h> + +#include <asm/mach/map.h> + +#include <plat/cpu.h> +#include <plat/pm-common.h> + +#ifdef CONFIG_SAMSUNG_ATAGS +#include <mach/pm-core.h> +#else +static inline void s3c_pm_debug_init_uart(void) {} +static inline void s3c_pm_arch_update_uart(void __iomem *regs, + struct pm_uart_save *save) {} +#endif + +static struct pm_uart_save uart_save; + +extern void printascii(const char *); + +void s3c_pm_dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsnprintf(buff, sizeof(buff), fmt, va); + va_end(va); + + printascii(buff); +} + +void s3c_pm_debug_init(void) +{ + /* restart uart clocks so we can use them to output */ + s3c_pm_debug_init_uart(); +} + +static inline void __iomem *s3c_pm_uart_base(void) +{ + unsigned long paddr; + unsigned long vaddr; + + debug_ll_addr(&paddr, &vaddr); + + return (void __iomem *)vaddr; +} + +void s3c_pm_save_uarts(void) +{ + void __iomem *regs = s3c_pm_uart_base(); + struct pm_uart_save *save = &uart_save; + + save->ulcon = __raw_readl(regs + S3C2410_ULCON); + save->ucon = __raw_readl(regs + S3C2410_UCON); + save->ufcon = __raw_readl(regs + S3C2410_UFCON); + save->umcon = __raw_readl(regs + S3C2410_UMCON); + save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV); + + if (!soc_is_s3c2410()) + save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); + + S3C_PMDBG("UART[%p]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", + regs, save->ulcon, save->ucon, save->ufcon, save->ubrdiv); +} + +void s3c_pm_restore_uarts(void) +{ + void __iomem *regs = s3c_pm_uart_base(); + struct pm_uart_save *save = &uart_save; + + s3c_pm_arch_update_uart(regs, save); + + __raw_writel(save->ulcon, regs + S3C2410_ULCON); + __raw_writel(save->ucon, regs + S3C2410_UCON); + __raw_writel(save->ufcon, regs + S3C2410_UFCON); + __raw_writel(save->umcon, regs + S3C2410_UMCON); + __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV); + + if (!soc_is_s3c2410()) + __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); +} diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c index d438e77fbf7..f8c0f9797dc 100644 --- a/arch/arm/plat-samsung/pm.c +++ b/arch/arm/plat-samsung/pm.c @@ -17,7 +17,6 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/serial_core.h> #include <linux/serial_s3c.h> #include <linux/io.h> @@ -42,93 +41,6 @@ unsigned long s3c_pm_flags; -/* Debug code: - * - * This code supports debug output to the low level UARTs for use on - * resume before the console layer is available. -*/ - -#ifdef CONFIG_SAMSUNG_PM_DEBUG -extern void printascii(const char *); - -void s3c_pm_dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsnprintf(buff, sizeof(buff), fmt, va); - va_end(va); - - printascii(buff); -} - -static inline void s3c_pm_debug_init(void) -{ - /* restart uart clocks so we can use them to output */ - s3c_pm_debug_init_uart(); -} - -#else -#define s3c_pm_debug_init() do { } while(0) - -#endif /* CONFIG_SAMSUNG_PM_DEBUG */ - -/* Save the UART configurations if we are configured for debug. */ - -unsigned char pm_uart_udivslot; - -#ifdef CONFIG_SAMSUNG_PM_DEBUG - -static struct pm_uart_save uart_save; - -static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save) -{ - void __iomem *regs = S3C_VA_UARTx(uart); - - save->ulcon = __raw_readl(regs + S3C2410_ULCON); - save->ucon = __raw_readl(regs + S3C2410_UCON); - save->ufcon = __raw_readl(regs + S3C2410_UFCON); - save->umcon = __raw_readl(regs + S3C2410_UMCON); - save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV); - - if (pm_uart_udivslot) - save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); - - S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", - uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv); -} - -static void s3c_pm_save_uarts(void) -{ - s3c_pm_save_uart(CONFIG_DEBUG_S3C_UART, &uart_save); -} - -static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save) -{ - void __iomem *regs = S3C_VA_UARTx(uart); - - s3c_pm_arch_update_uart(regs, save); - - __raw_writel(save->ulcon, regs + S3C2410_ULCON); - __raw_writel(save->ucon, regs + S3C2410_UCON); - __raw_writel(save->ufcon, regs + S3C2410_UFCON); - __raw_writel(save->umcon, regs + S3C2410_UMCON); - __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV); - - if (pm_uart_udivslot) - __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); -} - -static void s3c_pm_restore_uarts(void) -{ - s3c_pm_restore_uart(CONFIG_DEBUG_S3C_UART, &uart_save); -} -#else -static void s3c_pm_save_uarts(void) { } -static void s3c_pm_restore_uarts(void) { } -#endif - /* The IRQ ext-int code goes here, it is too small to currently bother * with its own file. */ @@ -153,62 +65,6 @@ int s3c_irqext_wake(struct irq_data *data, unsigned int state) return 0; } -/* helper functions to save and restore register state */ - -/** - * s3c_pm_do_save() - save a set of registers for restoration on resume. - * @ptr: Pointer to an array of registers. - * @count: Size of the ptr array. - * - * Run through the list of registers given, saving their contents in the - * array for later restoration when we wakeup. - */ -void s3c_pm_do_save(struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - ptr->val = __raw_readl(ptr->reg); - S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); - } -} - -/** - * s3c_pm_do_restore() - restore register values from the save list. - * @ptr: Pointer to an array of registers. - * @count: Size of the ptr array. - * - * Restore the register values saved from s3c_pm_do_save(). - * - * Note, we do not use S3C_PMDBG() in here, as the system may not have - * restore the UARTs state yet -*/ - -void s3c_pm_do_restore(const struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) { - printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", - ptr->reg, ptr->val, __raw_readl(ptr->reg)); - - __raw_writel(ptr->val, ptr->reg); - } -} - -/** - * s3c_pm_do_restore_core() - early restore register values from save list. - * - * This is similar to s3c_pm_do_restore() except we try and minimise the - * side effects of the function in case registers that hardware might need - * to work has been restored. - * - * WARNING: Do not put any debug in here that may effect memory or use - * peripherals, as things may be changing! -*/ - -void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count) -{ - for (; count > 0; count--, ptr++) - __raw_writel(ptr->val, ptr->reg); -} - /* s3c2410_pm_show_resume_irqs * * print any IRQs asserted at resume time (ie, we woke from) diff --git a/arch/arm/plat-samsung/s5p-sleep.S b/arch/arm/plat-samsung/s5p-sleep.S index a030e7301da..c5001659bdf 100644 --- a/arch/arm/plat-samsung/s5p-sleep.S +++ b/arch/arm/plat-samsung/s5p-sleep.S @@ -23,18 +23,7 @@ #include <linux/linkage.h> #include <asm/asm-offsets.h> -#include <asm/hardware/cache-l2x0.h> -#define CPU_MASK 0xff0ffff0 -#define CPU_CORTEX_A9 0x410fc090 - -/* - * The following code is located into the .data section. This is to - * allow l2x0_regs_phys to be accessed with a relative load while we - * can't rely on any MMU translation. We could have put l2x0_regs_phys - * in the .text section as well, but some setups might insist on it to - * be truly read-only. (Reference from: arch/arm/kernel/sleep.S) - */ .data .align @@ -53,37 +42,5 @@ */ ENTRY(s3c_cpu_resume) -#ifdef CONFIG_CACHE_L2X0 - mrc p15, 0, r0, c0, c0, 0 - ldr r1, =CPU_MASK - and r0, r0, r1 - ldr r1, =CPU_CORTEX_A9 - cmp r0, r1 - bne resume_l2on - adr r0, l2x0_regs_phys - ldr r0, [r0] - ldr r1, [r0, #L2X0_R_PHY_BASE] - ldr r2, [r1, #L2X0_CTRL] - tst r2, #0x1 - bne resume_l2on - ldr r2, [r0, #L2X0_R_AUX_CTRL] - str r2, [r1, #L2X0_AUX_CTRL] - ldr r2, [r0, #L2X0_R_TAG_LATENCY] - str r2, [r1, #L2X0_TAG_LATENCY_CTRL] - ldr r2, [r0, #L2X0_R_DATA_LATENCY] - str r2, [r1, #L2X0_DATA_LATENCY_CTRL] - ldr r2, [r0, #L2X0_R_PREFETCH_CTRL] - str r2, [r1, #L2X0_PREFETCH_CTRL] - ldr r2, [r0, #L2X0_R_PWR_CTRL] - str r2, [r1, #L2X0_POWER_CTRL] - mov r2, #1 - str r2, [r1, #L2X0_CTRL] -resume_l2on: -#endif b cpu_resume ENDPROC(s3c_cpu_resume) -#ifdef CONFIG_CACHE_L2X0 - .globl l2x0_regs_phys -l2x0_regs_phys: - .long 0 -#endif |