diff options
Diffstat (limited to 'arch/arm/mach-exynos')
| -rw-r--r-- | arch/arm/mach-exynos/Kconfig | 126 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/Makefile | 31 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/Makefile.boot | 2 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/common.h | 174 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/exynos-smc.S | 22 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/exynos.c | 361 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/firmware.c | 97 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/headsmp.S | 41 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/hotplug.c | 92 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/include/mach/dma.h | 26 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/include/mach/map.h | 73 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/include/mach/memory.h | 27 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/mcpm-exynos.c | 358 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/mfc.h | 16 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 262 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/pm.c | 483 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/pm_domains.c | 243 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/pmu.c | 423 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/regs-pmu.h | 326 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/sleep.S | 57 | ||||
| -rw-r--r-- | arch/arm/mach-exynos/smc.h | 31 |
21 files changed, 3271 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig new file mode 100644 index 00000000000..8f9b66c4ac7 --- /dev/null +++ b/arch/arm/mach-exynos/Kconfig @@ -0,0 +1,126 @@ +# arch/arm/mach-exynos/Kconfig +# +# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +# Configuration options for the EXYNOS4 + +menuconfig ARCH_EXYNOS + bool "Samsung EXYNOS" if ARCH_MULTI_V7 + select ARCH_HAS_BANDGAP + select ARCH_HAS_HOLES_MEMORYMODEL + select ARCH_REQUIRE_GPIOLIB + select ARM_AMBA + select ARM_GIC + select COMMON_CLK_SAMSUNG + select HAVE_ARM_SCU if SMP + select HAVE_S3C2410_I2C if I2C + select HAVE_S3C2410_WATCHDOG if WATCHDOG + select HAVE_S3C_RTC if RTC_CLASS + select PINCTRL + select PINCTRL_EXYNOS + select PM_GENERIC_DOMAINS if PM_RUNTIME + select S5P_DEV_MFC + select SRAM + help + Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5) + +if ARCH_EXYNOS + +config ARCH_EXYNOS3 + bool "SAMSUNG EXYNOS3" + select ARM_CPU_SUSPEND if PM + help + Samsung EXYNOS3 (Crotex-A7) SoC based systems + +config ARCH_EXYNOS4 + bool "SAMSUNG EXYNOS4" + default y + select ARM_CPU_SUSPEND if PM_SLEEP + select CLKSRC_SAMSUNG_PWM if CPU_EXYNOS4210 + select CPU_EXYNOS4210 + select GIC_NON_BANKED + select KEYBOARD_SAMSUNG if INPUT_KEYBOARD + select MIGHT_HAVE_CACHE_L2X0 + help + Samsung EXYNOS4 (Cortex-A9) SoC based systems + +config ARCH_EXYNOS5 + bool "SAMSUNG EXYNOS5" + default y + help + Samsung EXYNOS5 (Cortex-A15/A7) SoC based systems + +comment "EXYNOS SoCs" + +config SOC_EXYNOS3250 + bool "SAMSUNG EXYNOS3250" + default y + depends on ARCH_EXYNOS3 + +config CPU_EXYNOS4210 + bool "SAMSUNG EXYNOS4210" + default y + depends on ARCH_EXYNOS4 + +config SOC_EXYNOS4212 + bool "SAMSUNG EXYNOS4212" + default y + depends on ARCH_EXYNOS4 + +config SOC_EXYNOS4412 + bool "SAMSUNG EXYNOS4412" + default y + depends on ARCH_EXYNOS4 + +config SOC_EXYNOS5250 + bool "SAMSUNG EXYNOS5250" + default y + depends on ARCH_EXYNOS5 + +config SOC_EXYNOS5260 + bool "SAMSUNG EXYNOS5260" + default y + depends on ARCH_EXYNOS5 + +config SOC_EXYNOS5410 + bool "SAMSUNG EXYNOS5410" + default y + depends on ARCH_EXYNOS5 + +config SOC_EXYNOS5420 + bool "SAMSUNG EXYNOS5420" + default y + depends on ARCH_EXYNOS5 + +config SOC_EXYNOS5440 + bool "SAMSUNG EXYNOS5440" + default y + depends on ARCH_EXYNOS5 + select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE + select ARCH_HAS_OPP + select HAVE_ARM_ARCH_TIMER + select AUTO_ZRELADDR + select MIGHT_HAVE_PCI + select PCI_DOMAINS if PCI + select PINCTRL_EXYNOS5440 + select PM_OPP + help + Enable EXYNOS5440 SoC support + +config SOC_EXYNOS5800 + bool "SAMSUNG EXYNOS5800" + default y + depends on SOC_EXYNOS5420 + +config EXYNOS5420_MCPM + bool "Exynos5420 Multi-Cluster PM support" + depends on MCPM && SOC_EXYNOS5420 + select ARM_CCI + help + This is needed to provide CPU and cluster power management + on Exynos5420 implementing big.LITTLE. + +endif diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile new file mode 100644 index 00000000000..788f26d2114 --- /dev/null +++ b/arch/arm/mach-exynos/Makefile @@ -0,0 +1,31 @@ +# arch/arm/mach-exynos/Makefile +# +# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. +# http://www.samsung.com/ +# +# Licensed under GPLv2 + +ccflags-$(CONFIG_ARCH_MULTIPLATFORM) += -I$(srctree)/$(src)/include -I$(srctree)/arch/arm/plat-samsung/include + +obj-y := +obj-m := +obj-n := +obj- := + +# Core + +obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o + +obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o + +obj-$(CONFIG_SMP) += platsmp.o headsmp.o + +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +CFLAGS_hotplug.o += -march=armv7-a + +plus_sec := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) + +obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o +CFLAGS_mcpm-exynos.o += -march=armv7-a diff --git a/arch/arm/mach-exynos/Makefile.boot b/arch/arm/mach-exynos/Makefile.boot new file mode 100644 index 00000000000..b9862e22bf1 --- /dev/null +++ b/arch/arm/mach-exynos/Makefile.boot @@ -0,0 +1,2 @@ + zreladdr-y += 0x40008000 +params_phys-y := 0x40000100 diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h new file mode 100644 index 00000000000..1ee91763fa7 --- /dev/null +++ b/arch/arm/mach-exynos/common.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Common Header for EXYNOS machines + * + * 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 __ARCH_ARM_MACH_EXYNOS_COMMON_H +#define __ARCH_ARM_MACH_EXYNOS_COMMON_H + +#include <linux/reboot.h> +#include <linux/of.h> + +#define EXYNOS3250_SOC_ID 0xE3472000 +#define EXYNOS3_SOC_MASK 0xFFFFF000 + +#define EXYNOS4210_CPU_ID 0x43210000 +#define EXYNOS4212_CPU_ID 0x43220000 +#define EXYNOS4412_CPU_ID 0xE4412200 +#define EXYNOS4_CPU_MASK 0xFFFE0000 + +#define EXYNOS5250_SOC_ID 0x43520000 +#define EXYNOS5410_SOC_ID 0xE5410000 +#define EXYNOS5420_SOC_ID 0xE5420000 +#define EXYNOS5440_SOC_ID 0xE5440000 +#define EXYNOS5800_SOC_ID 0xE5422000 +#define EXYNOS5_SOC_MASK 0xFFFFF000 + +extern unsigned long samsung_cpu_id; + +#define IS_SAMSUNG_CPU(name, id, mask) \ +static inline int is_samsung_##name(void) \ +{ \ + return ((samsung_cpu_id & mask) == (id & mask)); \ +} + +IS_SAMSUNG_CPU(exynos3250, EXYNOS3250_SOC_ID, EXYNOS3_SOC_MASK) +IS_SAMSUNG_CPU(exynos4210, EXYNOS4210_CPU_ID, EXYNOS4_CPU_MASK) +IS_SAMSUNG_CPU(exynos4212, EXYNOS4212_CPU_ID, EXYNOS4_CPU_MASK) +IS_SAMSUNG_CPU(exynos4412, EXYNOS4412_CPU_ID, EXYNOS4_CPU_MASK) +IS_SAMSUNG_CPU(exynos5250, EXYNOS5250_SOC_ID, EXYNOS5_SOC_MASK) +IS_SAMSUNG_CPU(exynos5410, EXYNOS5410_SOC_ID, EXYNOS5_SOC_MASK) +IS_SAMSUNG_CPU(exynos5420, EXYNOS5420_SOC_ID, EXYNOS5_SOC_MASK) +IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK) +IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK) + +#if defined(CONFIG_SOC_EXYNOS3250) +# define soc_is_exynos3250() is_samsung_exynos3250() +#else +# define soc_is_exynos3250() 0 +#endif + +#if defined(CONFIG_CPU_EXYNOS4210) +# define soc_is_exynos4210() is_samsung_exynos4210() +#else +# define soc_is_exynos4210() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS4212) +# define soc_is_exynos4212() is_samsung_exynos4212() +#else +# define soc_is_exynos4212() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS4412) +# define soc_is_exynos4412() is_samsung_exynos4412() +#else +# define soc_is_exynos4412() 0 +#endif + +#define EXYNOS4210_REV_0 (0x0) +#define EXYNOS4210_REV_1_0 (0x10) +#define EXYNOS4210_REV_1_1 (0x11) + +#if defined(CONFIG_SOC_EXYNOS5250) +# define soc_is_exynos5250() is_samsung_exynos5250() +#else +# define soc_is_exynos5250() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS5410) +# define soc_is_exynos5410() is_samsung_exynos5410() +#else +# define soc_is_exynos5410() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS5420) +# define soc_is_exynos5420() is_samsung_exynos5420() +#else +# define soc_is_exynos5420() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS5440) +# define soc_is_exynos5440() is_samsung_exynos5440() +#else +# define soc_is_exynos5440() 0 +#endif + +#if defined(CONFIG_SOC_EXYNOS5800) +# define soc_is_exynos5800() is_samsung_exynos5800() +#else +# define soc_is_exynos5800() 0 +#endif + +#define soc_is_exynos4() (soc_is_exynos4210() || soc_is_exynos4212() || \ + soc_is_exynos4412()) +#define soc_is_exynos5() (soc_is_exynos5250() || soc_is_exynos5410() || \ + soc_is_exynos5420() || soc_is_exynos5800()) + +void mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1); + +struct map_desc; +extern void __iomem *sysram_ns_base_addr; +extern void __iomem *sysram_base_addr; +void exynos_init_io(void); +void exynos_restart(enum reboot_mode mode, const char *cmd); +void exynos_sysram_init(void); +void exynos_cpuidle_init(void); +void exynos_cpufreq_init(void); +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); + +/* PMU(Power Management Unit) support */ + +#define PMU_TABLE_END NULL + +enum sys_powerdown { + SYS_AFTR, + SYS_LPA, + SYS_SLEEP, + NUM_SYS_POWERDOWN, +}; + +struct exynos_pmu_conf { + void __iomem *reg; + unsigned int val[NUM_SYS_POWERDOWN]; +}; + +extern void exynos_sys_powerdown_conf(enum sys_powerdown mode); +extern void exynos_cpu_power_down(int cpu); +extern void exynos_cpu_power_up(int cpu); +extern int exynos_cpu_power_state(int cpu); +extern void exynos_cluster_power_down(int cluster); +extern void exynos_cluster_power_up(int cluster); +extern int exynos_cluster_power_state(int cluster); +extern void exynos_enter_aftr(void); + +extern void s5p_init_cpu(void __iomem *cpuid_addr); +extern unsigned int samsung_rev(void); + +#endif /* __ARCH_ARM_MACH_EXYNOS_COMMON_H */ diff --git a/arch/arm/mach-exynos/exynos-smc.S b/arch/arm/mach-exynos/exynos-smc.S new file mode 100644 index 00000000000..2e27aa3813f --- /dev/null +++ b/arch/arm/mach-exynos/exynos-smc.S @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * + * Copied from omap-smc.S Copyright (C) 2010 Texas Instruments, Inc. + * + * 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/linkage.h> + +/* + * Function signature: void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3) + */ + +ENTRY(exynos_smc) + stmfd sp!, {r4-r11, lr} + dsb + smc #0 + ldmfd sp!, {r4-r11, pc} +ENDPROC(exynos_smc) diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c new file mode 100644 index 00000000000..66c9b9614f3 --- /dev/null +++ b/arch/arm/mach-exynos/exynos.c @@ -0,0 +1,361 @@ +/* + * SAMSUNG EXYNOS Flattened Device Tree enabled machine + * + * Copyright (c) 2010-2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * 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/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/serial_s3c.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> + +#include <asm/cacheflush.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/memory.h> + +#include "common.h" +#include "mfc.h" +#include "regs-pmu.h" + +static struct map_desc exynos4_iodesc[] __initdata = { + { + .virtual = (unsigned long)S3C_VA_SYS, + .pfn = __phys_to_pfn(EXYNOS4_PA_SYSCON), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_TIMER, + .pfn = __phys_to_pfn(EXYNOS4_PA_TIMER), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_WATCHDOG, + .pfn = __phys_to_pfn(EXYNOS4_PA_WATCHDOG), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_SROMC, + .pfn = __phys_to_pfn(EXYNOS4_PA_SROMC), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_SYSTIMER, + .pfn = __phys_to_pfn(EXYNOS4_PA_SYSTIMER), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_PMU, + .pfn = __phys_to_pfn(EXYNOS4_PA_PMU), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_COMBINER_BASE, + .pfn = __phys_to_pfn(EXYNOS4_PA_COMBINER), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_GIC_CPU, + .pfn = __phys_to_pfn(EXYNOS4_PA_GIC_CPU), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_GIC_DIST, + .pfn = __phys_to_pfn(EXYNOS4_PA_GIC_DIST), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_CMU, + .pfn = __phys_to_pfn(EXYNOS4_PA_CMU), + .length = SZ_128K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_COREPERI_BASE, + .pfn = __phys_to_pfn(EXYNOS4_PA_COREPERI), + .length = SZ_8K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_L2CC, + .pfn = __phys_to_pfn(EXYNOS4_PA_L2CC), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_DMC0, + .pfn = __phys_to_pfn(EXYNOS4_PA_DMC0), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_DMC1, + .pfn = __phys_to_pfn(EXYNOS4_PA_DMC1), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_USB_HSPHY, + .pfn = __phys_to_pfn(EXYNOS4_PA_HSPHY), + .length = SZ_4K, + .type = MT_DEVICE, + }, +}; + +static struct map_desc exynos5_iodesc[] __initdata = { + { + .virtual = (unsigned long)S3C_VA_SYS, + .pfn = __phys_to_pfn(EXYNOS5_PA_SYSCON), + .length = SZ_64K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_TIMER, + .pfn = __phys_to_pfn(EXYNOS5_PA_TIMER), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S3C_VA_WATCHDOG, + .pfn = __phys_to_pfn(EXYNOS5_PA_WATCHDOG), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_SROMC, + .pfn = __phys_to_pfn(EXYNOS5_PA_SROMC), + .length = SZ_4K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_CMU, + .pfn = __phys_to_pfn(EXYNOS5_PA_CMU), + .length = 144 * SZ_1K, + .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_PMU, + .pfn = __phys_to_pfn(EXYNOS5_PA_PMU), + .length = SZ_64K, + .type = MT_DEVICE, + }, +}; + +void exynos_restart(enum reboot_mode mode, const char *cmd) +{ + struct device_node *np; + u32 val = 0x1; + void __iomem *addr = EXYNOS_SWRESET; + + if (of_machine_is_compatible("samsung,exynos5440")) { + u32 status; + np = of_find_compatible_node(NULL, NULL, "samsung,exynos5440-clock"); + + addr = of_iomap(np, 0) + 0xbc; + status = __raw_readl(addr); + + addr = of_iomap(np, 0) + 0xcc; + val = __raw_readl(addr); + + val = (val & 0xffff0000) | (status & 0xffff); + } + + __raw_writel(val, addr); +} + +static struct platform_device exynos_cpuidle = { + .name = "exynos_cpuidle", + .dev.platform_data = exynos_enter_aftr, + .id = -1, +}; + +void __init exynos_cpuidle_init(void) +{ + if (soc_is_exynos4210() || soc_is_exynos5250()) + platform_device_register(&exynos_cpuidle); +} + +void __init exynos_cpufreq_init(void) +{ + platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); +} + +void __iomem *sysram_base_addr; +void __iomem *sysram_ns_base_addr; + +void __init exynos_sysram_init(void) +{ + struct device_node *node; + + for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") { + if (!of_device_is_available(node)) + continue; + sysram_base_addr = of_iomap(node, 0); + break; + } + + for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") { + if (!of_device_is_available(node)) + continue; + sysram_ns_base_addr = of_iomap(node, 0); + break; + } +} + +void __init exynos_init_late(void) +{ + if (of_machine_is_compatible("samsung,exynos5440")) + /* to be supported later */ + return; + + pm_genpd_poweroff_unused(); + exynos_pm_init(); +} + +static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname, + int depth, void *data) +{ + struct map_desc iodesc; + const __be32 *reg; + int len; + + if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") && + !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock")) + return 0; + + reg = of_get_flat_dt_prop(node, "reg", &len); + if (reg == NULL || len != (sizeof(unsigned long) * 2)) + return 0; + + iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0])); + iodesc.length = be32_to_cpu(reg[1]) - 1; + iodesc.virtual = (unsigned long)S5P_VA_CHIPID; + iodesc.type = MT_DEVICE; + iotable_init(&iodesc, 1); + return 1; +} + +/* + * exynos_map_io + * + * register the standard cpu IO areas + */ +static void __init exynos_map_io(void) +{ + if (soc_is_exynos4()) + iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc)); + + if (soc_is_exynos5()) + iotable_init(exynos5_iodesc, ARRAY_SIZE(exynos5_iodesc)); +} + +void __init exynos_init_io(void) +{ + debug_ll_io_init(); + + of_scan_flat_dt(exynos_fdt_map_chipid, NULL); + + /* detect cpu id and rev. */ + s5p_init_cpu(S5P_VA_CHIPID); + + exynos_map_io(); +} + +static void __init exynos_dt_machine_init(void) +{ + struct device_node *i2c_np; + const char *i2c_compat = "samsung,s3c2440-i2c"; + unsigned int tmp; + int id; + + /* + * Exynos5's legacy i2c controller and new high speed i2c + * controller have muxed interrupt sources. By default the + * interrupts for 4-channel HS-I2C controller are enabled. + * If node for first four channels of legacy i2c controller + * are available then re-configure the interrupts via the + * system register. + */ + if (soc_is_exynos5()) { + for_each_compatible_node(i2c_np, NULL, i2c_compat) { + if (of_device_is_available(i2c_np)) { + id = of_alias_get_id(i2c_np, "i2c"); + if (id < 4) { + tmp = readl(EXYNOS5_SYS_I2C_CFG); + writel(tmp & ~(0x1 << id), + EXYNOS5_SYS_I2C_CFG); + } + } + } + } + + /* + * This is called from smp_prepare_cpus if we've built for SMP, but + * we still need to set it up for PM and firmware ops if not. + */ + if (!IS_ENABLED(CONFIG_SMP)) + exynos_sysram_init(); + + exynos_cpuidle_init(); + exynos_cpufreq_init(); + + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +} + +static char const *exynos_dt_compat[] __initconst = { + "samsung,exynos3", + "samsung,exynos3250", + "samsung,exynos4", + "samsung,exynos4210", + "samsung,exynos4212", + "samsung,exynos4412", + "samsung,exynos5", + "samsung,exynos5250", + "samsung,exynos5260", + "samsung,exynos5420", + "samsung,exynos5440", + NULL +}; + +static void __init exynos_reserve(void) +{ +#ifdef CONFIG_S5P_DEV_MFC + int i; + char *mfc_mem[] = { + "samsung,mfc-v5", + "samsung,mfc-v6", + "samsung,mfc-v7", + }; + + for (i = 0; i < ARRAY_SIZE(mfc_mem); i++) + if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i])) + break; +#endif +} + +static void __init exynos_dt_fixup(void) +{ + /* + * Some versions of uboot pass garbage entries in the memory node, + * use the old CONFIG_ARM_NR_BANKS + */ + of_fdt_limit_memory(8); +} + +DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") + /* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */ + /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */ + .l2c_aux_val = 0x3c400001, + .l2c_aux_mask = 0xc20fffff, + .smp = smp_ops(exynos_smp_ops), + .map_io = exynos_init_io, + .init_early = exynos_firmware_init, + .init_machine = exynos_dt_machine_init, + .init_late = exynos_init_late, + .dt_compat = exynos_dt_compat, + .restart = exynos_restart, + .reserve = exynos_reserve, + .dt_fixup = exynos_dt_fixup, +MACHINE_END diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c new file mode 100644 index 00000000000..e8797bb7887 --- /dev/null +++ b/arch/arm/mach-exynos/firmware.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Samsung Electronics. + * Kyungmin Park <kyungmin.park@samsung.com> + * Tomasz Figa <t.figa@samsung.com> + * + * 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/kernel.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <asm/firmware.h> + +#include <mach/map.h> + +#include "common.h" +#include "smc.h" + +static int exynos_do_idle(void) +{ + exynos_smc(SMC_CMD_SLEEP, 0, 0, 0); + return 0; +} + +static int exynos_cpu_boot(int cpu) +{ + /* + * Exynos3250 doesn't need to send smc command for secondary CPU boot + * because Exynos3250 removes WFE in secure mode. + */ + if (soc_is_exynos3250()) + return 0; + + /* + * The second parameter of SMC_CMD_CPU1BOOT command means CPU id. + * But, Exynos4212 has only one secondary CPU so second parameter + * isn't used for informing secure firmware about CPU id. + */ + if (soc_is_exynos4212()) + cpu = 0; + + exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0); + return 0; +} + +static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) +{ + void __iomem *boot_reg; + + if (!sysram_ns_base_addr) + return -ENODEV; + + boot_reg = sysram_ns_base_addr + 0x1c; + + /* + * Almost all Exynos-series of SoCs that run in secure mode don't need + * additional offset for every CPU, with Exynos4412 being the only + * exception. + */ + if (soc_is_exynos4412()) + boot_reg += 4 * cpu; + + __raw_writel(boot_addr, boot_reg); + return 0; +} + +static const struct firmware_ops exynos_firmware_ops = { + .do_idle = exynos_do_idle, + .set_cpu_boot_addr = exynos_set_cpu_boot_addr, + .cpu_boot = exynos_cpu_boot, +}; + +void __init exynos_firmware_init(void) +{ + struct device_node *nd; + const __be32 *addr; + + nd = of_find_compatible_node(NULL, NULL, + "samsung,secure-firmware"); + if (!nd) + return; + + addr = of_get_address(nd, 0, NULL, NULL); + if (!addr) { + pr_err("%s: No address specified.\n", __func__); + return; + } + + pr_info("Running under secure firmware.\n"); + + register_firmware_ops(&exynos_firmware_ops); +} diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S new file mode 100644 index 00000000000..cdd9d91e993 --- /dev/null +++ b/arch/arm/mach-exynos/headsmp.S @@ -0,0 +1,41 @@ +/* + * linux/arch/arm/mach-exynos4/headsmp.S + * + * Cloned from linux/arch/arm/mach-realview/headsmp.S + * + * Copyright (c) 2003 ARM Limited + * All Rights Reserved + * + * 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/linkage.h> +#include <linux/init.h> + +/* + * exynos4 specific entry point for secondary CPUs. This provides + * a "holding pen" into which all secondary cores are held until we're + * ready for them to initialise. + */ +ENTRY(exynos4_secondary_startup) + mrc p15, 0, r0, c0, c0, 5 + and r0, r0, #15 + adr r4, 1f + ldmia r4, {r5, r6} + sub r4, r4, r5 + add r6, r6, r4 +pen: ldr r7, [r6] + cmp r7, r0 + bne pen + + /* + * we've been released from the holding pen: secondary_stack + * should now contain the SVC stack for this core + */ + b secondary_startup +ENDPROC(exynos4_secondary_startup) + + .align 2 +1: .long . + .long pen_release diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c new file mode 100644 index 00000000000..920a4baa53c --- /dev/null +++ b/arch/arm/mach-exynos/hotplug.c @@ -0,0 +1,92 @@ +/* linux arch/arm/mach-exynos4/hotplug.c + * + * Cloned from linux/arch/arm/mach-realview/hotplug.c + * + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * + * 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/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> +#include <asm/cp15.h> +#include <asm/smp_plat.h> + +#include "common.h" +#include "regs-pmu.h" + +static inline void cpu_leave_lowpower(void) +{ + unsigned int v; + + asm volatile( + "mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); +} + +static inline void platform_do_lowpower(unsigned int cpu, int *spurious) +{ + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + + for (;;) { + + /* Turn the CPU off on next WFI instruction. */ + exynos_cpu_power_down(core_id); + + wfi(); + + if (pen_release == core_id) { + /* + * OK, proper wakeup, we're done + */ + break; + } + + /* + * Getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * Just note it happening - when we're woken, we can report + * its occurrence. + */ + (*spurious)++; + } +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void __ref exynos_cpu_die(unsigned int cpu) +{ + int spurious = 0; + + v7_exit_coherency_flush(louis); + + platform_do_lowpower(cpu, &spurious); + + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + cpu_leave_lowpower(); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +} diff --git a/arch/arm/mach-exynos/include/mach/dma.h b/arch/arm/mach-exynos/include/mach/dma.h new file mode 100644 index 00000000000..201842a3769 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/dma.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010 Samsung Electronics Co. Ltd. + * Jaswinder Singh <jassi.brar@samsung.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MACH_DMA_H +#define __MACH_DMA_H + +/* This platform uses the common DMA API driver for PL330 */ +#include <plat/dma-pl330.h> + +#endif /* __MACH_DMA_H */ diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h new file mode 100644 index 00000000000..548269a6063 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -0,0 +1,73 @@ +/* linux/arch/arm/mach-exynos/include/mach/map.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4 - Memory map definitions + * + * 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_MAP_H +#define __ASM_ARCH_MAP_H __FILE__ + +#include <plat/map-base.h> + +/* + * EXYNOS4 UART offset is 0x10000 but the older S5P SoCs are 0x400. + * So need to define it, and here is to avoid redefinition warning. + */ +#define S3C_UART_OFFSET (0x10000) + +#include <plat/map-s5p.h> + +#define EXYNOS_PA_CHIPID 0x10000000 + +#define EXYNOS4_PA_SYSCON 0x10010000 +#define EXYNOS5_PA_SYSCON 0x10050100 + +#define EXYNOS4_PA_PMU 0x10020000 +#define EXYNOS5_PA_PMU 0x10040000 + +#define EXYNOS4_PA_CMU 0x10030000 +#define EXYNOS5_PA_CMU 0x10010000 + +#define EXYNOS4_PA_SYSTIMER 0x10050000 + +#define EXYNOS4_PA_WATCHDOG 0x10060000 +#define EXYNOS5_PA_WATCHDOG 0x101D0000 + +#define EXYNOS4_PA_DMC0 0x10400000 +#define EXYNOS4_PA_DMC1 0x10410000 + +#define EXYNOS4_PA_COMBINER 0x10440000 +#define EXYNOS5_PA_COMBINER 0x10440000 + +#define EXYNOS4_PA_GIC_CPU 0x10480000 +#define EXYNOS4_PA_GIC_DIST 0x10490000 +#define EXYNOS5_PA_GIC_CPU 0x10482000 +#define EXYNOS5_PA_GIC_DIST 0x10481000 + +#define EXYNOS4_PA_COREPERI 0x10500000 +#define EXYNOS4_PA_L2CC 0x10502000 + +#define EXYNOS4_PA_SROMC 0x12570000 +#define EXYNOS5_PA_SROMC 0x12250000 + +#define EXYNOS4_PA_HSPHY 0x125B0000 + +#define EXYNOS4_PA_UART 0x13800000 +#define EXYNOS5_PA_UART 0x12C00000 + +#define EXYNOS4_PA_TIMER 0x139D0000 +#define EXYNOS5_PA_TIMER 0x12DD0000 + +/* Compatibility UART */ + +#define EXYNOS5440_PA_UART0 0x000B0000 + +#define S3C_VA_UARTx(x) (S3C_VA_UART + ((x) * S3C_UART_OFFSET)) + +#endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-exynos/include/mach/memory.h b/arch/arm/mach-exynos/include/mach/memory.h new file mode 100644 index 00000000000..2a4cdb7cb32 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/memory.h @@ -0,0 +1,27 @@ +/* linux/arch/arm/mach-exynos4/include/mach/memory.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS4 - Memory definitions + * + * 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_MEMORY_H +#define __ASM_ARCH_MEMORY_H __FILE__ + +#define PLAT_PHYS_OFFSET UL(0x40000000) + +#ifndef CONFIG_ARM_LPAE +/* Maximum of 256MiB in one bank */ +#define MAX_PHYSMEM_BITS 32 +#define SECTION_SIZE_BITS 28 +#else +#define MAX_PHYSMEM_BITS 36 +#define SECTION_SIZE_BITS 31 +#endif + +#endif /* __ASM_ARCH_MEMORY_H */ diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c new file mode 100644 index 00000000000..ace0ed61747 --- /dev/null +++ b/arch/arm/mach-exynos/mcpm-exynos.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * arch/arm/mach-exynos/mcpm-exynos.c + * + * Based on arch/arm/mach-vexpress/dcscb.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. + */ + +#include <linux/arm-cci.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of_address.h> + +#include <asm/cputype.h> +#include <asm/cp15.h> +#include <asm/mcpm.h> + +#include "regs-pmu.h" +#include "common.h" + +#define EXYNOS5420_CPUS_PER_CLUSTER 4 +#define EXYNOS5420_NR_CLUSTERS 2 + +/* + * The common v7_exit_coherency_flush API could not be used because of the + * Erratum 799270 workaround. This macro is the same as the common one (in + * arch/arm/include/asm/cacheflush.h) except for the erratum handling. + */ +#define exynos_v7_exit_coherency_flush(level) \ + asm volatile( \ + "stmfd sp!, {fp, ip}\n\t"\ + "mrc p15, 0, r0, c1, c0, 0 @ get SCTLR\n\t" \ + "bic r0, r0, #"__stringify(CR_C)"\n\t" \ + "mcr p15, 0, r0, c1, c0, 0 @ set SCTLR\n\t" \ + "isb\n\t"\ + "bl v7_flush_dcache_"__stringify(level)"\n\t" \ + "clrex\n\t"\ + "mrc p15, 0, r0, c1, c0, 1 @ get ACTLR\n\t" \ + "bic r0, r0, #(1 << 6) @ disable local coherency\n\t" \ + /* Dummy Load of a device register to avoid Erratum 799270 */ \ + "ldr r4, [%0]\n\t" \ + "and r4, r4, #0\n\t" \ + "orr r0, r0, r4\n\t" \ + "mcr p15, 0, r0, c1, c0, 1 @ set ACTLR\n\t" \ + "isb\n\t" \ + "dsb\n\t" \ + "ldmfd sp!, {fp, ip}" \ + : \ + : "Ir" (S5P_INFORM0) \ + : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "r9", "r10", "lr", "memory") + +/* + * We can't use regular spinlocks. In the switcher case, it is possible + * for an outbound CPU to call power_down() after its inbound counterpart + * is already live using the same logical CPU number which trips lockdep + * debugging. + */ +static arch_spinlock_t exynos_mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED; +static int +cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS]; + +#define exynos_cluster_usecnt(cluster) \ + (cpu_use_count[0][cluster] + \ + cpu_use_count[1][cluster] + \ + cpu_use_count[2][cluster] + \ + cpu_use_count[3][cluster]) + +#define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster) + +static int exynos_cluster_power_control(unsigned int cluster, int enable) +{ + unsigned int tries = 100; + unsigned int val; + + if (enable) { + exynos_cluster_power_up(cluster); + val = S5P_CORE_LOCAL_PWR_EN; + } else { + exynos_cluster_power_down(cluster); + val = 0; + } + + /* Wait until cluster power control is applied */ + while (tries--) { + if (exynos_cluster_power_state(cluster) == val) + return 0; + + cpu_relax(); + } + pr_debug("timed out waiting for cluster %u to power %s\n", cluster, + enable ? "on" : "off"); + + return -ETIMEDOUT; +} + +static int exynos_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); + int err = 0; + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER || + cluster >= EXYNOS5420_NR_CLUSTERS) + return -EINVAL; + + /* + * Since this is called with IRQs enabled, and no arch_spin_lock_irq + * variant exists, we need to disable IRQs manually here. + */ + local_irq_disable(); + arch_spin_lock(&exynos_mcpm_lock); + + cpu_use_count[cpu][cluster]++; + if (cpu_use_count[cpu][cluster] == 1) { + bool was_cluster_down = + (exynos_cluster_usecnt(cluster) == 1); + + /* + * Turn on the cluster (L2/COMMON) and then power on the + * cores. + */ + if (was_cluster_down) + err = exynos_cluster_power_control(cluster, 1); + + if (!err) + exynos_cpu_power_up(cpunr); + else + exynos_cluster_power_control(cluster, 0); + } else if (cpu_use_count[cpu][cluster] != 2) { + /* + * The only possible values are: + * 0 = CPU down + * 1 = CPU (still) up + * 2 = CPU requested to be up before it had a chance + * to actually make itself down. + * Any other value is a bug. + */ + BUG(); + } + + arch_spin_unlock(&exynos_mcpm_lock); + local_irq_enable(); + + return err; +} + +/* + * NOTE: This function requires the stack data to be visible through power down + * and can only be executed on processors like A15 and A7 that hit the cache + * with the C bit clear in the SCTLR register. + */ +static void exynos_power_down(void) +{ + unsigned int mpidr, cpu, cluster; + bool last_man = false, skip_wfi = false; + unsigned int cpunr; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || + cluster >= EXYNOS5420_NR_CLUSTERS); + + __mcpm_cpu_going_down(cpu, cluster); + + arch_spin_lock(&exynos_mcpm_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + cpu_use_count[cpu][cluster]--; + if (cpu_use_count[cpu][cluster] == 0) { + exynos_cpu_power_down(cpunr); + + if (exynos_cluster_unused(cluster)) + /* TODO: Turn off the cluster here to save power. */ + last_man = true; + } else if (cpu_use_count[cpu][cluster] == 1) { + /* + * A power_up request went ahead of us. + * Even if we do not want to shut this CPU down, + * the caller expects a certain state as if the WFI + * was aborted. So let's continue with cache cleaning. + */ + skip_wfi = true; + } else { + BUG(); + } + + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { + arch_spin_unlock(&exynos_mcpm_lock); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) { + /* + * On the Cortex-A15 we need to disable + * L2 prefetching before flushing the cache. + */ + asm volatile( + "mcr p15, 1, %0, c15, c0, 3\n\t" + "isb\n\t" + "dsb" + : : "r" (0x400)); + } + + /* Flush all cache levels for this cluster. */ + exynos_v7_exit_coherency_flush(all); + + /* + * Disable cluster-level coherency by masking + * incoming snoops and DVM messages: + */ + cci_disable_port_by_cpu(mpidr); + + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + } else { + arch_spin_unlock(&exynos_mcpm_lock); + + /* Disable and flush the local CPU cache. */ + exynos_v7_exit_coherency_flush(louis); + } + + __mcpm_cpu_down(cpu, cluster); + + /* Now we are prepared for power-down, do it: */ + if (!skip_wfi) + wfi(); + + /* Not dead at this point? Let our caller cope. */ +} + +static int exynos_wait_for_powerdown(unsigned int cpu, unsigned int cluster) +{ + unsigned int tries = 100; + unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || + cluster >= EXYNOS5420_NR_CLUSTERS); + + /* Wait for the core state to be OFF */ + while (tries--) { + if (ACCESS_ONCE(cpu_use_count[cpu][cluster]) == 0) { + if ((exynos_cpu_power_state(cpunr) == 0)) + return 0; /* success: the CPU is halted */ + } + + /* Otherwise, wait and retry: */ + msleep(1); + } + + return -ETIMEDOUT; /* timeout */ +} + +static const struct mcpm_platform_ops exynos_power_ops = { + .power_up = exynos_power_up, + .power_down = exynos_power_down, + .wait_for_powerdown = exynos_wait_for_powerdown, +}; + +static void __init exynos_mcpm_usage_count_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER || + cluster >= EXYNOS5420_NR_CLUSTERS); + + cpu_use_count[cpu][cluster] = 1; +} + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + */ +static void __naked exynos_pm_power_up_setup(unsigned int affinity_level) +{ + asm volatile ("\n" + "cmp r0, #1\n" + "bxne lr\n" + "b cci_enable_port_for_self"); +} + +static const struct of_device_id exynos_dt_mcpm_match[] = { + { .compatible = "samsung,exynos5420" }, + { .compatible = "samsung,exynos5800" }, + {}, +}; + +static int __init exynos_mcpm_init(void) +{ + struct device_node *node; + void __iomem *ns_sram_base_addr; + int ret; + + node = of_find_matching_node(NULL, exynos_dt_mcpm_match); + if (!node) + return -ENODEV; + of_node_put(node); + + if (!cci_probed()) + return -ENODEV; + + node = of_find_compatible_node(NULL, NULL, + "samsung,exynos4210-sysram-ns"); + if (!node) + return -ENODEV; + + ns_sram_base_addr = of_iomap(node, 0); + of_node_put(node); + if (!ns_sram_base_addr) { + pr_err("failed to map non-secure iRAM base address\n"); + return -ENOMEM; + } + + /* + * To increase the stability of KFC reset we need to program + * the PMU SPARE3 register + */ + __raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3); + + exynos_mcpm_usage_count_init(); + + ret = mcpm_platform_register(&exynos_power_ops); + if (!ret) + ret = mcpm_sync_init(exynos_pm_power_up_setup); + if (ret) { + iounmap(ns_sram_base_addr); + return ret; + } + + mcpm_smp_set_ops(); + + pr_info("Exynos MCPM support installed\n"); + + /* + * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr + * as part of secondary_cpu_start(). Let's redirect it to the + * mcpm_entry_point(). + */ + __raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */ + __raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */ + __raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8); + + iounmap(ns_sram_base_addr); + + return ret; +} + +early_initcall(exynos_mcpm_init); diff --git a/arch/arm/mach-exynos/mfc.h b/arch/arm/mach-exynos/mfc.h new file mode 100644 index 00000000000..dec93cd5b3c --- /dev/null +++ b/arch/arm/mach-exynos/mfc.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * + * 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. + */ + +#ifndef __MACH_EXYNOS_MFC_H +#define __MACH_EXYNOS_MFC_H __FILE__ + +int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, + int depth, void *data); + +#endif /* __MACH_EXYNOS_MFC_H */ diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c new file mode 100644 index 00000000000..50b9aad5e27 --- /dev/null +++ b/arch/arm/mach-exynos/platsmp.c @@ -0,0 +1,262 @@ +/* linux/arch/arm/mach-exynos4/platsmp.c + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Cloned from linux/arch/arm/mach-vexpress/platsmp.c + * + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * + * 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/init.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/jiffies.h> +#include <linux/smp.h> +#include <linux/io.h> +#include <linux/of_address.h> + +#include <asm/cacheflush.h> +#include <asm/smp_plat.h> +#include <asm/smp_scu.h> +#include <asm/firmware.h> + +#include "common.h" +#include "regs-pmu.h" + +extern void exynos4_secondary_startup(void); + +static inline void __iomem *cpu_boot_reg_base(void) +{ + if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1) + return S5P_INFORM5; + return sysram_base_addr; +} + +static inline void __iomem *cpu_boot_reg(int cpu) +{ + void __iomem *boot_reg; + + boot_reg = cpu_boot_reg_base(); + if (!boot_reg) + return ERR_PTR(-ENODEV); + if (soc_is_exynos4412()) + boot_reg += 4*cpu; + else if (soc_is_exynos5420() || soc_is_exynos5800()) + boot_reg += 4; + return boot_reg; +} + +/* + * Write pen_release in a way that is guaranteed to be visible to all + * observers, irrespective of whether they're taking part in coherency + * or not. This is necessary for the hotplug code to work reliably. + */ +static void write_pen_release(int val) +{ + pen_release = val; + smp_wmb(); + sync_cache_w(&pen_release); +} + +static void __iomem *scu_base_addr(void) +{ + return (void __iomem *)(S5P_VA_SCU); +} + +static DEFINE_SPINLOCK(boot_lock); + +static void exynos_secondary_init(unsigned int cpu) +{ + /* + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ + write_pen_release(-1); + + /* + * Synchronise with the boot thread. + */ + spin_lock(&boot_lock); + spin_unlock(&boot_lock); +} + +static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + unsigned long timeout; + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + int ret = -ENOSYS; + + /* + * Set synchronisation state between this boot processor + * and the secondary one + */ + spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from + * the holding pen - release it, then wait for it to flag + * that it has been released by resetting pen_release. + * + * Note that "pen_release" is the hardware CPU core ID, whereas + * "cpu" is Linux's internal ID. + */ + write_pen_release(core_id); + + if (!exynos_cpu_power_state(core_id)) { + exynos_cpu_power_up(core_id); + timeout = 10; + + /* wait max 10 ms until cpu1 is on */ + while (exynos_cpu_power_state(core_id) + != S5P_CORE_LOCAL_PWR_EN) { + if (timeout-- == 0) + break; + + mdelay(1); + } + + if (timeout == 0) { + printk(KERN_ERR "cpu1 power enable failed"); + spin_unlock(&boot_lock); + return -ETIMEDOUT; + } + } + /* + * Send the secondary CPU a soft interrupt, thereby causing + * the boot monitor to read the system wide flags register, + * and branch to the address found there. + */ + + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + unsigned long boot_addr; + + smp_rmb(); + + boot_addr = virt_to_phys(exynos4_secondary_startup); + + /* + * Try to set boot address using firmware first + * and fall back to boot register if it fails. + */ + ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); + if (ret && ret != -ENOSYS) + goto fail; + if (ret == -ENOSYS) { + void __iomem *boot_reg = cpu_boot_reg(core_id); + + if (IS_ERR(boot_reg)) { + ret = PTR_ERR(boot_reg); + goto fail; + } + __raw_writel(boot_addr, cpu_boot_reg(core_id)); + } + + call_firmware_op(cpu_boot, core_id); + + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + if (pen_release == -1) + break; + + udelay(10); + } + + /* + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +fail: + spin_unlock(&boot_lock); + + return pen_release != -1 ? ret : 0; +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ + +static void __init exynos_smp_init_cpus(void) +{ + void __iomem *scu_base = scu_base_addr(); + unsigned int i, ncores; + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + ncores = scu_base ? scu_get_core_count(scu_base) : 1; + else + /* + * CPU Nodes are passed thru DT and set_cpu_possible + * is set by "arm_dt_init_cpu_maps". + */ + return; + + /* sanity check */ + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); +} + +static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) +{ + int i; + + exynos_sysram_init(); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + scu_enable(scu_base_addr()); + + /* + * Write the address of secondary startup into the + * system-wide flags register. The boot monitor waits + * until it receives a soft interrupt, and then the + * secondary CPU branches to this address. + * + * Try using firmware operation first and fall back to + * boot register if it fails. + */ + for (i = 1; i < max_cpus; ++i) { + unsigned long boot_addr; + u32 mpidr; + u32 core_id; + int ret; + + mpidr = cpu_logical_map(i); + core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + boot_addr = virt_to_phys(exynos4_secondary_startup); + + ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); + if (ret && ret != -ENOSYS) + break; + if (ret == -ENOSYS) { + void __iomem *boot_reg = cpu_boot_reg(core_id); + + if (IS_ERR(boot_reg)) + break; + __raw_writel(boot_addr, cpu_boot_reg(core_id)); + } + } +} + +struct smp_operations exynos_smp_ops __initdata = { + .smp_init_cpus = exynos_smp_init_cpus, + .smp_prepare_cpus = exynos_smp_prepare_cpus, + .smp_secondary_init = exynos_secondary_init, + .smp_boot_secondary = exynos_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = exynos_cpu_die, +#endif +}; diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c new file mode 100644 index 00000000000..202ca73e49c --- /dev/null +++ b/arch/arm/mach-exynos/pm.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - Power Management support + * + * Based on arch/arm/mach-s3c2410/pm.c + * Copyright (c) 2006 Simtec Electronics + * 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. +*/ + +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/syscore_ops.h> +#include <linux/cpu_pm.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/pm-common.h> +#include <plat/pll.h> +#include <plat/regs-srom.h> + +#include <mach/map.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), +}; + +static struct sleep_save exynos_core_save[] = { + /* SROM side */ + SAVE_ITEM(S5P_SROM_BW), + SAVE_ITEM(S5P_SROM_BC0), + SAVE_ITEM(S5P_SROM_BC1), + SAVE_ITEM(S5P_SROM_BC2), + 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; +} + +/** + * exynos_core_power_down : power down the specified cpu + * @cpu : the cpu to power down + * + * Power down the specified cpu. The sequence must be finished by a + * call to cpu_do_idle() + * + */ +void exynos_cpu_power_down(int cpu) +{ + __raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); +} + +/** + * exynos_cpu_power_up : power up the specified cpu + * @cpu : the cpu to power up + * + * Power up the specified cpu + */ +void exynos_cpu_power_up(int cpu) +{ + __raw_writel(S5P_CORE_LOCAL_PWR_EN, + EXYNOS_ARM_CORE_CONFIGURATION(cpu)); +} + +/** + * exynos_cpu_power_state : returns the power state of the cpu + * @cpu : the cpu to retrieve the power state from + * + */ +int exynos_cpu_power_state(int cpu) +{ + return (__raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) & + S5P_CORE_LOCAL_PWR_EN); +} + +/** + * exynos_cluster_power_down : power down the specified cluster + * @cluster : the cluster to power down + */ +void exynos_cluster_power_down(int cluster) +{ + __raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster)); +} + +/** + * exynos_cluster_power_up : power up the specified cluster + * @cluster : the cluster to power up + */ +void exynos_cluster_power_up(int cluster) +{ + __raw_writel(S5P_CORE_LOCAL_PWR_EN, + EXYNOS_COMMON_CONFIGURATION(cluster)); +} + +/** + * exynos_cluster_power_state : returns the power state of the cluster + * @cluster : the cluster to retrieve the power state from + * + */ +int exynos_cluster_power_state(int cluster) +{ + return (__raw_readl(EXYNOS_COMMON_STATUS(cluster)) & + S5P_CORE_LOCAL_PWR_EN); +} + +#define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (sysram_base_addr + 0x24) : S5P_INFORM0)) +#define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ + S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ + (sysram_base_addr + 0x20) : S5P_INFORM1)) + +#define S5P_CHECK_AFTR 0xFCBA0D10 +#define S5P_CHECK_SLEEP 0x00000BAD + +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ +static void exynos_set_wakeupmask(long mask) +{ + __raw_writel(mask, S5P_WAKEUP_MASK); +} + +static void exynos_cpu_set_boot_vector(long flags) +{ + __raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR); + __raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG); +} + +void exynos_enter_aftr(void) +{ + exynos_set_wakeupmask(0x0000ff3e); + exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); + /* Set value of power down register for aftr mode */ + exynos_sys_powerdown_conf(SYS_AFTR); +} + +/* For Cortex-A9 Diagnostic and Power control register */ +static unsigned int save_arm_register[2]; + +static void exynos_cpu_save_register(void) +{ + unsigned long tmp; + + /* Save Power control register */ + asm ("mrc p15, 0, %0, c15, c0, 0" + : "=r" (tmp) : : "cc"); + + save_arm_register[0] = tmp; + + /* Save Diagnostic register */ + asm ("mrc p15, 0, %0, c15, c0, 1" + : "=r" (tmp) : : "cc"); + + save_arm_register[1] = tmp; +} + +static void exynos_cpu_restore_register(void) +{ + unsigned long tmp; + + /* Restore Power control register */ + tmp = save_arm_register[0]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 0" + : : "r" (tmp) + : "cc"); + + /* Restore Diagnostic register */ + tmp = save_arm_register[1]; + + asm volatile ("mcr p15, 0, %0, c15, c0, 1" + : : "r" (tmp) + : "cc"); +} + +static int exynos_cpu_suspend(unsigned long arg) +{ +#ifdef CONFIG_CACHE_L2X0 + outer_flush_all(); +#endif + + if (soc_is_exynos5250()) + flush_cache_all(); + + /* issue the standby signal into the pm unit. */ + cpu_do_idle(); + + pr_info("Failed to suspend the system\n"); + return 1; /* Aborting suspend */ +} + +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()) { + s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save)); + /* Disable USE_RETENTION of JPEG_MEM_OPTION */ + tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION); + tmp &= ~EXYNOS5_OPTION_USE_RETENTION; + __raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION); + } + + /* Set value of power down register for sleep mode */ + + exynos_sys_powerdown_conf(SYS_SLEEP); + __raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); + + /* ensure at least INFORM0 has the resume address */ + + __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); +} + +static void exynos_pm_central_suspend(void) +{ + unsigned long tmp; + + /* Setting Central Sequence Register for power down mode */ + tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); + tmp &= ~S5P_CENTRAL_LOWPWR_CFG; + __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); +} + +static int exynos_pm_suspend(void) +{ + unsigned long tmp; + + exynos_pm_central_suspend(); + + /* Setting SEQ_OPTION register */ + + tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0); + __raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + exynos_cpu_save_register(); + + return 0; +} + +static int exynos_pm_central_resume(void) +{ + unsigned long tmp; + + /* + * If PMU failed while entering sleep mode, WFI will be + * ignored by PMU and then exiting cpu_do_idle(). + * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically + * in this situation. + */ + tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); + if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { + tmp |= S5P_CENTRAL_LOWPWR_CFG; + __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); + /* clear the wakeup state register */ + __raw_writel(0x0, S5P_WAKEUP_STAT); + /* No need to perform below restore code */ + return -1; + } + + return 0; +} + +static void exynos_pm_resume(void) +{ + if (exynos_pm_central_resume()) + goto early_wakeup; + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + exynos_cpu_restore_register(); + + /* For release retention */ + + __raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION); + __raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION); + + if (soc_is_exynos5250()) + s3c_pm_do_restore(exynos5_sys_save, + ARRAY_SIZE(exynos5_sys_save)); + + s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save)); + + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + scu_enable(S5P_VA_SCU); + +early_wakeup: + + /* Clear SLEEP mode set in INFORM1 */ + __raw_writel(0x0, S5P_INFORM1); + + return; +} + +static struct syscore_ops exynos_pm_syscore_ops = { + .suspend = exynos_pm_suspend, + .resume = exynos_pm_resume, +}; + +/* + * Suspend Ops + */ + +static int exynos_suspend_enter(suspend_state_t state) +{ + 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__); + + return 0; +} + +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, +}; + +static int exynos_cpu_pm_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + int cpu = smp_processor_id(); + + switch (cmd) { + case CPU_PM_ENTER: + if (cpu == 0) { + exynos_pm_central_suspend(); + if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9) + exynos_cpu_save_register(); + } + break; + + case CPU_PM_EXIT: + if (cpu == 0) { + if (read_cpuid_part_number() == + ARM_CPU_PART_CORTEX_A9) { + scu_enable(S5P_VA_SCU); + exynos_cpu_restore_register(); + } + exynos_pm_central_resume(); + } + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos_cpu_pm_notifier_block = { + .notifier_call = exynos_cpu_pm_notifier, +}; + +void __init exynos_pm_init(void) +{ + u32 tmp; + + cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block); + + /* 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/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c new file mode 100644 index 00000000000..797cb134bff --- /dev/null +++ b/arch/arm/mach-exynos/pm_domains.c @@ -0,0 +1,243 @@ +/* + * Exynos Generic power domain support. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Implementation of Exynos specific power domain control which is used in + * conjunction with runtime-pm. Support for both device-tree and non-device-tree + * based power domain support is included. + * + * 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/err.h> +#include <linux/slab.h> +#include <linux/pm_domain.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/sched.h> + +#include "regs-pmu.h" + +#define MAX_CLK_PER_DOMAIN 4 + +/* + * Exynos specific wrapper around the generic power domain + */ +struct exynos_pm_domain { + void __iomem *base; + char const *name; + bool is_off; + struct generic_pm_domain pd; + struct clk *oscclk; + struct clk *clk[MAX_CLK_PER_DOMAIN]; + struct clk *pclk[MAX_CLK_PER_DOMAIN]; +}; + +static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct exynos_pm_domain *pd; + void __iomem *base; + u32 timeout, pwr; + char *op; + + pd = container_of(domain, struct exynos_pm_domain, pd); + base = pd->base; + + /* Set oscclk before powering off a domain*/ + if (!power_on) { + int i; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->clk[i])) + break; + if (clk_set_parent(pd->clk[i], pd->oscclk)) + pr_err("%s: error setting oscclk as parent to clock %d\n", + pd->name, i); + } + } + + pwr = power_on ? S5P_INT_LOCAL_PWR_EN : 0; + __raw_writel(pwr, base); + + /* Wait max 1ms */ + timeout = 10; + + while ((__raw_readl(base + 0x4) & S5P_INT_LOCAL_PWR_EN) != pwr) { + if (!timeout) { + op = (power_on) ? "enable" : "disable"; + pr_err("Power domain %s %s failed\n", domain->name, op); + return -ETIMEDOUT; + } + timeout--; + cpu_relax(); + usleep_range(80, 100); + } + + /* Restore clocks after powering on a domain*/ + if (power_on) { + int i; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->clk[i])) + break; + if (clk_set_parent(pd->clk[i], pd->pclk[i])) + pr_err("%s: error setting parent to clock%d\n", + pd->name, i); + } + } + + return 0; +} + +static int exynos_pd_power_on(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, true); +} + +static int exynos_pd_power_off(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, false); +} + +static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, + struct device *dev) +{ + int ret; + + dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); + + while (1) { + ret = pm_genpd_add_device(&pd->pd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } + + pm_genpd_dev_need_restore(dev, true); +} + +static void exynos_remove_device_from_domain(struct device *dev) +{ + struct generic_pm_domain *genpd = dev_to_genpd(dev); + int ret; + + dev_dbg(dev, "removing from power domain %s\n", genpd->name); + + while (1) { + ret = pm_genpd_remove_device(genpd, dev); + if (ret != -EAGAIN) + break; + cond_resched(); + } +} + +static void exynos_read_domain_from_dt(struct device *dev) +{ + struct platform_device *pd_pdev; + struct exynos_pm_domain *pd; + struct device_node *node; + + node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); + if (!node) + return; + pd_pdev = of_find_device_by_node(node); + if (!pd_pdev) + return; + pd = platform_get_drvdata(pd_pdev); + exynos_add_device_to_domain(pd, dev); +} + +static int exynos_pm_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct device *dev = data; + + switch (event) { + case BUS_NOTIFY_BIND_DRIVER: + if (dev->of_node) + exynos_read_domain_from_dt(dev); + + break; + + case BUS_NOTIFY_UNBOUND_DRIVER: + exynos_remove_device_from_domain(dev); + + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block platform_nb = { + .notifier_call = exynos_pm_notifier_call, +}; + +static __init int exynos4_pm_init_power_domain(void) +{ + struct platform_device *pdev; + struct device_node *np; + + for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct exynos_pm_domain *pd; + int on, i; + struct device *dev; + + pdev = of_find_device_by_node(np); + dev = &pdev->dev; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + pr_err("%s: failed to allocate memory for domain\n", + __func__); + return -ENOMEM; + } + + pd->pd.name = kstrdup(np->name, GFP_KERNEL); + pd->name = pd->pd.name; + pd->base = of_iomap(np, 0); + pd->pd.power_off = exynos_pd_power_off; + pd->pd.power_on = exynos_pd_power_on; + pd->pd.of_node = np; + + pd->oscclk = clk_get(dev, "oscclk"); + if (IS_ERR(pd->oscclk)) + goto no_clk; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + char clk_name[8]; + + snprintf(clk_name, sizeof(clk_name), "clk%d", i); + pd->clk[i] = clk_get(dev, clk_name); + if (IS_ERR(pd->clk[i])) + break; + snprintf(clk_name, sizeof(clk_name), "pclk%d", i); + pd->pclk[i] = clk_get(dev, clk_name); + if (IS_ERR(pd->pclk[i])) { + clk_put(pd->clk[i]); + pd->clk[i] = ERR_PTR(-EINVAL); + break; + } + } + + if (IS_ERR(pd->clk[0])) + clk_put(pd->oscclk); + +no_clk: + platform_set_drvdata(pdev, pd); + + on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; + + pm_genpd_init(&pd->pd, NULL, !on); + } + + bus_register_notifier(&platform_bus_type, &platform_nb); + + return 0; +} +arch_initcall(exynos4_pm_init_power_domain); diff --git a/arch/arm/mach-exynos/pmu.c b/arch/arm/mach-exynos/pmu.c new file mode 100644 index 00000000000..fb0deda3b3a --- /dev/null +++ b/arch/arm/mach-exynos/pmu.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - CPU PMU(Power Management Unit) 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/io.h> +#include <linux/kernel.h> +#include <linux/bug.h> + +#include "common.h" +#include "regs-pmu.h" + +static const struct exynos_pmu_conf *exynos_pmu_config; + +static const struct exynos_pmu_conf exynos4210_pmu_config[] = { + /* { .reg = address, .val = { AFTR, LPA, SLEEP } */ + { S5P_ARM_CORE0_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE0, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL0, { 0x0, 0x0, 0x0 } }, + { S5P_ARM_CORE1_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE1, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL1, { 0x0, 0x0, 0x0 } }, + { S5P_ARM_COMMON_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_L2_0_LOWPWR, { 0x2, 0x2, 0x3 } }, + { S5P_L2_1_LOWPWR, { 0x2, 0x2, 0x3 } }, + { S5P_CMU_ACLKSTOP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_SCLKSTOP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_APLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_MPLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_VPLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_EPLL_SYSCLK_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_GPSALIVE_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_CAM_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_TV_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_MFC_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_G3D_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_LCD0_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_LCD1_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_CLKSTOP_GPS_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_CAM_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_TV_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_MFC_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_G3D_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_LCD0_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_LCD1_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_GPS_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_TOP_BUS_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_TOP_RETENTION_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_TOP_PWR_LOWPWR, { 0x3, 0x0, 0x3 } }, + { S5P_LOGIC_RESET_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_ONENAND_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_MODIMIF_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_G2D_ACP_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_USBOTG_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_HSMMC_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_CSSYS_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_SECSS_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_PCIE_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_SATA_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_DRAM_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_PAD_RETENTION_GPIO_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_UART_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MMCA_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MMCB_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_EBIA_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_EBIB_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_ISOLATION_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_ALV_SEL_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_XUSBXTI_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_XXTI_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_EXT_REGULATOR_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_GPIO_MODE_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_GPIO_MODE_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CAM_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_TV_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_MFC_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_G3D_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_LCD0_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_LCD1_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_MAUDIO_LOWPWR, { 0x7, 0x7, 0x0 } }, + { S5P_GPS_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_GPS_ALIVE_LOWPWR, { 0x7, 0x0, 0x0 } }, + { PMU_TABLE_END,}, +}; + +static const struct exynos_pmu_conf exynos4x12_pmu_config[] = { + { S5P_ARM_CORE0_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE0, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL0, { 0x0, 0x0, 0x0 } }, + { S5P_ARM_CORE1_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE1, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL1, { 0x0, 0x0, 0x0 } }, + { S5P_ISP_ARM_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_DIS_IRQ_ISP_ARM_LOCAL_LOWPWR, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_ISP_ARM_CENTRAL_LOWPWR, { 0x0, 0x0, 0x0 } }, + { S5P_ARM_COMMON_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_L2_0_LOWPWR, { 0x0, 0x0, 0x3 } }, + /* XXX_OPTION register should be set other field */ + { S5P_ARM_L2_0_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_L2_1_LOWPWR, { 0x0, 0x0, 0x3 } }, + { S5P_ARM_L2_1_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_CMU_ACLKSTOP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_SCLKSTOP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_DRAM_FREQ_DOWN_LOWPWR, { 0x1, 0x1, 0x1 } }, + { S5P_DDRPHY_DLLOFF_LOWPWR, { 0x1, 0x1, 0x1 } }, + { S5P_LPDDR_PHY_DLL_LOCK_LOWPWR, { 0x1, 0x1, 0x1 } }, + { S5P_CMU_ACLKSTOP_COREBLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_SCLKSTOP_COREBLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_COREBLK_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_APLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_MPLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_VPLL_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_EPLL_SYSCLK_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_MPLLUSER_SYSCLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_GPSALIVE_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_CAM_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_TV_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_MFC_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_G3D_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_LCD0_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_ISP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_MAUDIO_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_CLKSTOP_GPS_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_CAM_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_TV_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_MFC_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_G3D_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_LCD0_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_ISP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_RESET_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_CMU_RESET_GPS_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_TOP_BUS_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_TOP_RETENTION_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_TOP_PWR_LOWPWR, { 0x3, 0x0, 0x3 } }, + { S5P_TOP_BUS_COREBLK_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_TOP_RETENTION_COREBLK_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_TOP_PWR_COREBLK_LOWPWR, { 0x3, 0x0, 0x3 } }, + { S5P_LOGIC_RESET_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_OSCCLK_GATE_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_LOGIC_RESET_COREBLK_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_OSCCLK_GATE_COREBLK_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_ONENAND_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_ONENAND_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_HSI_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_HSI_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_G2D_ACP_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_G2D_ACP_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_USBOTG_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_USBOTG_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_HSMMC_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_HSMMC_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_CSSYS_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_CSSYS_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_SECSS_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_SECSS_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_ROTATOR_MEM_LOWPWR, { 0x3, 0x0, 0x0 } }, + { S5P_ROTATOR_MEM_OPTION, { 0x10, 0x10, 0x0 } }, + { S5P_PAD_RETENTION_DRAM_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_PAD_RETENTION_GPIO_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_UART_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MMCA_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_MMCB_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_EBIA_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_EBIB_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_GPIO_COREBLK_LOWPWR,{ 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_ISOLATION_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_ISOLATION_COREBLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_PAD_RETENTION_ALV_SEL_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_XUSBXTI_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_XXTI_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_EXT_REGULATOR_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_GPIO_MODE_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_GPIO_MODE_COREBLK_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_GPIO_MODE_MAUDIO_LOWPWR, { 0x1, 0x1, 0x0 } }, + { S5P_TOP_ASB_RESET_LOWPWR, { 0x1, 0x1, 0x1 } }, + { S5P_TOP_ASB_ISOLATION_LOWPWR, { 0x1, 0x0, 0x1 } }, + { S5P_CAM_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_TV_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_MFC_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_G3D_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_LCD0_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_ISP_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_MAUDIO_LOWPWR, { 0x7, 0x7, 0x0 } }, + { S5P_GPS_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_GPS_ALIVE_LOWPWR, { 0x7, 0x0, 0x0 } }, + { S5P_CMU_SYSCLK_ISP_LOWPWR, { 0x1, 0x0, 0x0 } }, + { S5P_CMU_SYSCLK_GPS_LOWPWR, { 0x1, 0x0, 0x0 } }, + { PMU_TABLE_END,}, +}; + +static const struct exynos_pmu_conf exynos4412_pmu_config[] = { + { S5P_ARM_CORE2_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE2, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL2, { 0x0, 0x0, 0x0 } }, + { S5P_ARM_CORE3_LOWPWR, { 0x0, 0x0, 0x2 } }, + { S5P_DIS_IRQ_CORE3, { 0x0, 0x0, 0x0 } }, + { S5P_DIS_IRQ_CENTRAL3, { 0x0, 0x0, 0x0 } }, + { PMU_TABLE_END,}, +}; + +static const struct exynos_pmu_conf exynos5250_pmu_config[] = { + /* { .reg = address, .val = { AFTR, LPA, SLEEP } */ + { EXYNOS5_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, + { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_ARM_CORE1_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, + { EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_FSYS_ARM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_ISP_ARM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, + { EXYNOS5_ARM_COMMON_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, + { EXYNOS5_ARM_L2_SYS_PWR_REG, { 0x3, 0x3, 0x3} }, + { EXYNOS5_ARM_L2_OPTION, { 0x10, 0x10, 0x0 } }, + { EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_CMU_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_APLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_TOP_BUS_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_TOP_RETENTION_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_TOP_PWR_SYS_PWR_REG, { 0x3, 0x0, 0x3} }, + { EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG, { 0x3, 0x0, 0x3} }, + { EXYNOS5_LOGIC_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_OSCCLK_GATE_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_USBOTG_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_G2D_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_USBDRD_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_SDMMC_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_CSSYS_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_SECSS_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_ROTATOR_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_INTRAM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_INTROM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_JPEG_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_HSI_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_MCUIOP_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_SATA_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_ISOLATION_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_XUSBXTI_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_XXTI_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_EXT_REGULATOR_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_GPIO_MODE_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x1} }, + { EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG, { 0x1, 0x0, 0x1} }, + { EXYNOS5_GSCL_SYS_PWR_REG, { 0x7, 0x0, 0x0} }, + { EXYNOS5_ISP_SYS_PWR_REG, { 0x7, 0x0, 0x0} }, + { EXYNOS5_MFC_SYS_PWR_REG, { 0x7, 0x0, 0x0} }, + { EXYNOS5_G3D_SYS_PWR_REG, { 0x7, 0x0, 0x0} }, + { EXYNOS5_DISP1_SYS_PWR_REG, { 0x7, 0x0, 0x0} }, + { EXYNOS5_MAU_SYS_PWR_REG, { 0x7, 0x7, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG, { 0x1, 0x0, 0x0} }, + { EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} }, + { PMU_TABLE_END,}, +}; + +static void __iomem * const exynos5_list_both_cnt_feed[] = { + EXYNOS5_ARM_CORE0_OPTION, + EXYNOS5_ARM_CORE1_OPTION, + EXYNOS5_ARM_COMMON_OPTION, + EXYNOS5_GSCL_OPTION, + EXYNOS5_ISP_OPTION, + EXYNOS5_MFC_OPTION, + EXYNOS5_G3D_OPTION, + EXYNOS5_DISP1_OPTION, + EXYNOS5_MAU_OPTION, + EXYNOS5_TOP_PWR_OPTION, + EXYNOS5_TOP_PWR_SYSMEM_OPTION, +}; + +static void __iomem * const exynos5_list_diable_wfi_wfe[] = { + EXYNOS5_ARM_CORE1_OPTION, + EXYNOS5_FSYS_ARM_OPTION, + EXYNOS5_ISP_ARM_OPTION, +}; + +static void exynos5_init_pmu(void) +{ + unsigned int i; + unsigned int tmp; + + /* + * Enable both SC_FEEDBACK and SC_COUNTER + */ + for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) { + tmp = __raw_readl(exynos5_list_both_cnt_feed[i]); + tmp |= (EXYNOS5_USE_SC_FEEDBACK | + EXYNOS5_USE_SC_COUNTER); + __raw_writel(tmp, exynos5_list_both_cnt_feed[i]); + } + + /* + * SKIP_DEACTIVATE_ACEACP_IN_PWDN_BITFIELD Enable + */ + tmp = __raw_readl(EXYNOS5_ARM_COMMON_OPTION); + tmp |= EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN; + __raw_writel(tmp, EXYNOS5_ARM_COMMON_OPTION); + + /* + * Disable WFI/WFE on XXX_OPTION + */ + for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) { + tmp = __raw_readl(exynos5_list_diable_wfi_wfe[i]); + tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE | + EXYNOS5_OPTION_USE_STANDBYWFI); + __raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]); + } +} + +void exynos_sys_powerdown_conf(enum sys_powerdown mode) +{ + unsigned int i; + + if (soc_is_exynos5250()) + exynos5_init_pmu(); + + for (i = 0; (exynos_pmu_config[i].reg != PMU_TABLE_END) ; i++) + __raw_writel(exynos_pmu_config[i].val[mode], + exynos_pmu_config[i].reg); + + if (soc_is_exynos4412()) { + for (i = 0; exynos4412_pmu_config[i].reg != PMU_TABLE_END ; i++) + __raw_writel(exynos4412_pmu_config[i].val[mode], + exynos4412_pmu_config[i].reg); + } +} + +static int __init exynos_pmu_init(void) +{ + unsigned int value; + + exynos_pmu_config = exynos4210_pmu_config; + + if (soc_is_exynos4210()) { + exynos_pmu_config = exynos4210_pmu_config; + pr_info("EXYNOS4210 PMU Initialize\n"); + } else if (soc_is_exynos4212() || soc_is_exynos4412()) { + exynos_pmu_config = exynos4x12_pmu_config; + pr_info("EXYNOS4x12 PMU Initialize\n"); + } else if (soc_is_exynos5250()) { + /* + * When SYS_WDTRESET is set, watchdog timer reset request + * is ignored by power management unit. + */ + value = __raw_readl(EXYNOS5_AUTO_WDTRESET_DISABLE); + value &= ~EXYNOS5_SYS_WDTRESET; + __raw_writel(value, EXYNOS5_AUTO_WDTRESET_DISABLE); + + value = __raw_readl(EXYNOS5_MASK_WDTRESET_REQUEST); + value &= ~EXYNOS5_SYS_WDTRESET; + __raw_writel(value, EXYNOS5_MASK_WDTRESET_REQUEST); + + exynos_pmu_config = exynos5250_pmu_config; + pr_info("EXYNOS5250 PMU Initialize\n"); + } else { + pr_info("EXYNOS: PMU not supported\n"); + } + + return 0; +} +arch_initcall(exynos_pmu_init); diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h new file mode 100644 index 00000000000..1d13b08708f --- /dev/null +++ b/arch/arm/mach-exynos/regs-pmu.h @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - Power management unit definition + * + * 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_REGS_PMU_H +#define __ASM_ARCH_REGS_PMU_H __FILE__ + +#include <mach/map.h> + +#define S5P_PMUREG(x) (S5P_VA_PMU + (x)) +#define S5P_SYSREG(x) (S3C_VA_SYS + (x)) + +#define S5P_CENTRAL_SEQ_CONFIGURATION S5P_PMUREG(0x0200) + +#define S5P_CENTRAL_LOWPWR_CFG (1 << 16) + +#define S5P_CENTRAL_SEQ_OPTION S5P_PMUREG(0x0208) + +#define S5P_USE_STANDBY_WFI0 (1 << 16) +#define S5P_USE_STANDBY_WFE0 (1 << 24) + +#define EXYNOS_SWRESET S5P_PMUREG(0x0400) +#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) +#define S5P_INFORM5 S5P_PMUREG(0x0814) +#define S5P_INFORM6 S5P_PMUREG(0x0818) +#define S5P_INFORM7 S5P_PMUREG(0x081C) +#define S5P_PMU_SPARE3 S5P_PMUREG(0x090C) + +#define S5P_ARM_CORE0_LOWPWR S5P_PMUREG(0x1000) +#define S5P_DIS_IRQ_CORE0 S5P_PMUREG(0x1004) +#define S5P_DIS_IRQ_CENTRAL0 S5P_PMUREG(0x1008) +#define S5P_ARM_CORE1_LOWPWR S5P_PMUREG(0x1010) +#define S5P_DIS_IRQ_CORE1 S5P_PMUREG(0x1014) +#define S5P_DIS_IRQ_CENTRAL1 S5P_PMUREG(0x1018) +#define S5P_ARM_COMMON_LOWPWR S5P_PMUREG(0x1080) +#define S5P_L2_0_LOWPWR S5P_PMUREG(0x10C0) +#define S5P_L2_1_LOWPWR S5P_PMUREG(0x10C4) +#define S5P_CMU_ACLKSTOP_LOWPWR S5P_PMUREG(0x1100) +#define S5P_CMU_SCLKSTOP_LOWPWR S5P_PMUREG(0x1104) +#define S5P_CMU_RESET_LOWPWR S5P_PMUREG(0x110C) +#define S5P_APLL_SYSCLK_LOWPWR S5P_PMUREG(0x1120) +#define S5P_MPLL_SYSCLK_LOWPWR S5P_PMUREG(0x1124) +#define S5P_VPLL_SYSCLK_LOWPWR S5P_PMUREG(0x1128) +#define S5P_EPLL_SYSCLK_LOWPWR S5P_PMUREG(0x112C) +#define S5P_CMU_CLKSTOP_GPS_ALIVE_LOWPWR S5P_PMUREG(0x1138) +#define S5P_CMU_RESET_GPSALIVE_LOWPWR S5P_PMUREG(0x113C) +#define S5P_CMU_CLKSTOP_CAM_LOWPWR S5P_PMUREG(0x1140) +#define S5P_CMU_CLKSTOP_TV_LOWPWR S5P_PMUREG(0x1144) +#define S5P_CMU_CLKSTOP_MFC_LOWPWR S5P_PMUREG(0x1148) +#define S5P_CMU_CLKSTOP_G3D_LOWPWR S5P_PMUREG(0x114C) +#define S5P_CMU_CLKSTOP_LCD0_LOWPWR S5P_PMUREG(0x1150) +#define S5P_CMU_CLKSTOP_MAUDIO_LOWPWR S5P_PMUREG(0x1158) +#define S5P_CMU_CLKSTOP_GPS_LOWPWR S5P_PMUREG(0x115C) +#define S5P_CMU_RESET_CAM_LOWPWR S5P_PMUREG(0x1160) +#define S5P_CMU_RESET_TV_LOWPWR S5P_PMUREG(0x1164) +#define S5P_CMU_RESET_MFC_LOWPWR S5P_PMUREG(0x1168) +#define S5P_CMU_RESET_G3D_LOWPWR S5P_PMUREG(0x116C) +#define S5P_CMU_RESET_LCD0_LOWPWR S5P_PMUREG(0x1170) +#define S5P_CMU_RESET_MAUDIO_LOWPWR S5P_PMUREG(0x1178) +#define S5P_CMU_RESET_GPS_LOWPWR S5P_PMUREG(0x117C) +#define S5P_TOP_BUS_LOWPWR S5P_PMUREG(0x1180) +#define S5P_TOP_RETENTION_LOWPWR S5P_PMUREG(0x1184) +#define S5P_TOP_PWR_LOWPWR S5P_PMUREG(0x1188) +#define S5P_LOGIC_RESET_LOWPWR S5P_PMUREG(0x11A0) +#define S5P_ONENAND_MEM_LOWPWR S5P_PMUREG(0x11C0) +#define S5P_G2D_ACP_MEM_LOWPWR S5P_PMUREG(0x11C8) +#define S5P_USBOTG_MEM_LOWPWR S5P_PMUREG(0x11CC) +#define S5P_HSMMC_MEM_LOWPWR S5P_PMUREG(0x11D0) +#define S5P_CSSYS_MEM_LOWPWR S5P_PMUREG(0x11D4) +#define S5P_SECSS_MEM_LOWPWR S5P_PMUREG(0x11D8) +#define S5P_PAD_RETENTION_DRAM_LOWPWR S5P_PMUREG(0x1200) +#define S5P_PAD_RETENTION_MAUDIO_LOWPWR S5P_PMUREG(0x1204) +#define S5P_PAD_RETENTION_GPIO_LOWPWR S5P_PMUREG(0x1220) +#define S5P_PAD_RETENTION_UART_LOWPWR S5P_PMUREG(0x1224) +#define S5P_PAD_RETENTION_MMCA_LOWPWR S5P_PMUREG(0x1228) +#define S5P_PAD_RETENTION_MMCB_LOWPWR S5P_PMUREG(0x122C) +#define S5P_PAD_RETENTION_EBIA_LOWPWR S5P_PMUREG(0x1230) +#define S5P_PAD_RETENTION_EBIB_LOWPWR S5P_PMUREG(0x1234) +#define S5P_PAD_RETENTION_ISOLATION_LOWPWR S5P_PMUREG(0x1240) +#define S5P_PAD_RETENTION_ALV_SEL_LOWPWR S5P_PMUREG(0x1260) +#define S5P_XUSBXTI_LOWPWR S5P_PMUREG(0x1280) +#define S5P_XXTI_LOWPWR S5P_PMUREG(0x1284) +#define S5P_EXT_REGULATOR_LOWPWR S5P_PMUREG(0x12C0) +#define S5P_GPIO_MODE_LOWPWR S5P_PMUREG(0x1300) +#define S5P_GPIO_MODE_MAUDIO_LOWPWR S5P_PMUREG(0x1340) +#define S5P_CAM_LOWPWR S5P_PMUREG(0x1380) +#define S5P_TV_LOWPWR S5P_PMUREG(0x1384) +#define S5P_MFC_LOWPWR S5P_PMUREG(0x1388) +#define S5P_G3D_LOWPWR S5P_PMUREG(0x138C) +#define S5P_LCD0_LOWPWR S5P_PMUREG(0x1390) +#define S5P_MAUDIO_LOWPWR S5P_PMUREG(0x1398) +#define S5P_GPS_LOWPWR S5P_PMUREG(0x139C) +#define S5P_GPS_ALIVE_LOWPWR S5P_PMUREG(0x13A0) + +#define EXYNOS_ARM_CORE0_CONFIGURATION S5P_PMUREG(0x2000) +#define EXYNOS_ARM_CORE_CONFIGURATION(_nr) \ + (EXYNOS_ARM_CORE0_CONFIGURATION + (0x80 * (_nr))) +#define EXYNOS_ARM_CORE_STATUS(_nr) \ + (EXYNOS_ARM_CORE_CONFIGURATION(_nr) + 0x4) + +#define EXYNOS_ARM_COMMON_CONFIGURATION S5P_PMUREG(0x2500) +#define EXYNOS_COMMON_CONFIGURATION(_nr) \ + (EXYNOS_ARM_COMMON_CONFIGURATION + (0x80 * (_nr))) +#define EXYNOS_COMMON_STATUS(_nr) \ + (EXYNOS_COMMON_CONFIGURATION(_nr) + 0x4) + +#define S5P_PAD_RET_MAUDIO_OPTION S5P_PMUREG(0x3028) +#define S5P_PAD_RET_GPIO_OPTION S5P_PMUREG(0x3108) +#define S5P_PAD_RET_UART_OPTION S5P_PMUREG(0x3128) +#define S5P_PAD_RET_MMCA_OPTION S5P_PMUREG(0x3148) +#define S5P_PAD_RET_MMCB_OPTION S5P_PMUREG(0x3168) +#define S5P_PAD_RET_EBIA_OPTION S5P_PMUREG(0x3188) +#define S5P_PAD_RET_EBIB_OPTION S5P_PMUREG(0x31A8) + +#define S5P_CORE_LOCAL_PWR_EN 0x3 +#define S5P_INT_LOCAL_PWR_EN 0x7 + +/* Only for EXYNOS4210 */ +#define S5P_CMU_CLKSTOP_LCD1_LOWPWR S5P_PMUREG(0x1154) +#define S5P_CMU_RESET_LCD1_LOWPWR S5P_PMUREG(0x1174) +#define S5P_MODIMIF_MEM_LOWPWR S5P_PMUREG(0x11C4) +#define S5P_PCIE_MEM_LOWPWR S5P_PMUREG(0x11E0) +#define S5P_SATA_MEM_LOWPWR S5P_PMUREG(0x11E4) +#define S5P_LCD1_LOWPWR S5P_PMUREG(0x1394) + +/* Only for EXYNOS4x12 */ +#define S5P_ISP_ARM_LOWPWR S5P_PMUREG(0x1050) +#define S5P_DIS_IRQ_ISP_ARM_LOCAL_LOWPWR S5P_PMUREG(0x1054) +#define S5P_DIS_IRQ_ISP_ARM_CENTRAL_LOWPWR S5P_PMUREG(0x1058) +#define S5P_CMU_ACLKSTOP_COREBLK_LOWPWR S5P_PMUREG(0x1110) +#define S5P_CMU_SCLKSTOP_COREBLK_LOWPWR S5P_PMUREG(0x1114) +#define S5P_CMU_RESET_COREBLK_LOWPWR S5P_PMUREG(0x111C) +#define S5P_MPLLUSER_SYSCLK_LOWPWR S5P_PMUREG(0x1130) +#define S5P_CMU_CLKSTOP_ISP_LOWPWR S5P_PMUREG(0x1154) +#define S5P_CMU_RESET_ISP_LOWPWR S5P_PMUREG(0x1174) +#define S5P_TOP_BUS_COREBLK_LOWPWR S5P_PMUREG(0x1190) +#define S5P_TOP_RETENTION_COREBLK_LOWPWR S5P_PMUREG(0x1194) +#define S5P_TOP_PWR_COREBLK_LOWPWR S5P_PMUREG(0x1198) +#define S5P_OSCCLK_GATE_LOWPWR S5P_PMUREG(0x11A4) +#define S5P_LOGIC_RESET_COREBLK_LOWPWR S5P_PMUREG(0x11B0) +#define S5P_OSCCLK_GATE_COREBLK_LOWPWR S5P_PMUREG(0x11B4) +#define S5P_HSI_MEM_LOWPWR S5P_PMUREG(0x11C4) +#define S5P_ROTATOR_MEM_LOWPWR S5P_PMUREG(0x11DC) +#define S5P_PAD_RETENTION_GPIO_COREBLK_LOWPWR S5P_PMUREG(0x123C) +#define S5P_PAD_ISOLATION_COREBLK_LOWPWR S5P_PMUREG(0x1250) +#define S5P_GPIO_MODE_COREBLK_LOWPWR S5P_PMUREG(0x1320) +#define S5P_TOP_ASB_RESET_LOWPWR S5P_PMUREG(0x1344) +#define S5P_TOP_ASB_ISOLATION_LOWPWR S5P_PMUREG(0x1348) +#define S5P_ISP_LOWPWR S5P_PMUREG(0x1394) +#define S5P_DRAM_FREQ_DOWN_LOWPWR S5P_PMUREG(0x13B0) +#define S5P_DDRPHY_DLLOFF_LOWPWR S5P_PMUREG(0x13B4) +#define S5P_CMU_SYSCLK_ISP_LOWPWR S5P_PMUREG(0x13B8) +#define S5P_CMU_SYSCLK_GPS_LOWPWR S5P_PMUREG(0x13BC) +#define S5P_LPDDR_PHY_DLL_LOCK_LOWPWR S5P_PMUREG(0x13C0) + +#define S5P_ARM_L2_0_OPTION S5P_PMUREG(0x2608) +#define S5P_ARM_L2_1_OPTION S5P_PMUREG(0x2628) +#define S5P_ONENAND_MEM_OPTION S5P_PMUREG(0x2E08) +#define S5P_HSI_MEM_OPTION S5P_PMUREG(0x2E28) +#define S5P_G2D_ACP_MEM_OPTION S5P_PMUREG(0x2E48) +#define S5P_USBOTG_MEM_OPTION S5P_PMUREG(0x2E68) +#define S5P_HSMMC_MEM_OPTION S5P_PMUREG(0x2E88) +#define S5P_CSSYS_MEM_OPTION S5P_PMUREG(0x2EA8) +#define S5P_SECSS_MEM_OPTION S5P_PMUREG(0x2EC8) +#define S5P_ROTATOR_MEM_OPTION S5P_PMUREG(0x2F48) + +/* Only for EXYNOS4412 */ +#define S5P_ARM_CORE2_LOWPWR S5P_PMUREG(0x1020) +#define S5P_DIS_IRQ_CORE2 S5P_PMUREG(0x1024) +#define S5P_DIS_IRQ_CENTRAL2 S5P_PMUREG(0x1028) +#define S5P_ARM_CORE3_LOWPWR S5P_PMUREG(0x1030) +#define S5P_DIS_IRQ_CORE3 S5P_PMUREG(0x1034) +#define S5P_DIS_IRQ_CENTRAL3 S5P_PMUREG(0x1038) + +/* For EXYNOS5 */ + +#define EXYNOS5_SYS_I2C_CFG S5P_SYSREG(0x0234) + +#define EXYNOS5_AUTO_WDTRESET_DISABLE S5P_PMUREG(0x0408) +#define EXYNOS5_MASK_WDTRESET_REQUEST S5P_PMUREG(0x040C) + +#define EXYNOS5_SYS_WDTRESET (1 << 20) + +#define EXYNOS5_ARM_CORE0_SYS_PWR_REG S5P_PMUREG(0x1000) +#define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG S5P_PMUREG(0x1004) +#define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1008) +#define EXYNOS5_ARM_CORE1_SYS_PWR_REG S5P_PMUREG(0x1010) +#define EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG S5P_PMUREG(0x1014) +#define EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1018) +#define EXYNOS5_FSYS_ARM_SYS_PWR_REG S5P_PMUREG(0x1040) +#define EXYNOS5_DIS_IRQ_FSYS_ARM_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1048) +#define EXYNOS5_ISP_ARM_SYS_PWR_REG S5P_PMUREG(0x1050) +#define EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG S5P_PMUREG(0x1054) +#define EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1058) +#define EXYNOS5_ARM_COMMON_SYS_PWR_REG S5P_PMUREG(0x1080) +#define EXYNOS5_ARM_L2_SYS_PWR_REG S5P_PMUREG(0x10C0) +#define EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG S5P_PMUREG(0x1100) +#define EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG S5P_PMUREG(0x1104) +#define EXYNOS5_CMU_RESET_SYS_PWR_REG S5P_PMUREG(0x110C) +#define EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1120) +#define EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1124) +#define EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x112C) +#define EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG S5P_PMUREG(0x1130) +#define EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG S5P_PMUREG(0x1134) +#define EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG S5P_PMUREG(0x1138) +#define EXYNOS5_APLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1140) +#define EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1144) +#define EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1148) +#define EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x114C) +#define EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1150) +#define EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1154) +#define EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1164) +#define EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG S5P_PMUREG(0x1170) +#define EXYNOS5_TOP_BUS_SYS_PWR_REG S5P_PMUREG(0x1180) +#define EXYNOS5_TOP_RETENTION_SYS_PWR_REG S5P_PMUREG(0x1184) +#define EXYNOS5_TOP_PWR_SYS_PWR_REG S5P_PMUREG(0x1188) +#define EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1190) +#define EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1194) +#define EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1198) +#define EXYNOS5_LOGIC_RESET_SYS_PWR_REG S5P_PMUREG(0x11A0) +#define EXYNOS5_OSCCLK_GATE_SYS_PWR_REG S5P_PMUREG(0x11A4) +#define EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x11B0) +#define EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x11B4) +#define EXYNOS5_USBOTG_MEM_SYS_PWR_REG S5P_PMUREG(0x11C0) +#define EXYNOS5_G2D_MEM_SYS_PWR_REG S5P_PMUREG(0x11C8) +#define EXYNOS5_USBDRD_MEM_SYS_PWR_REG S5P_PMUREG(0x11CC) +#define EXYNOS5_SDMMC_MEM_SYS_PWR_REG S5P_PMUREG(0x11D0) +#define EXYNOS5_CSSYS_MEM_SYS_PWR_REG S5P_PMUREG(0x11D4) +#define EXYNOS5_SECSS_MEM_SYS_PWR_REG S5P_PMUREG(0x11D8) +#define EXYNOS5_ROTATOR_MEM_SYS_PWR_REG S5P_PMUREG(0x11DC) +#define EXYNOS5_INTRAM_MEM_SYS_PWR_REG S5P_PMUREG(0x11E0) +#define EXYNOS5_INTROM_MEM_SYS_PWR_REG S5P_PMUREG(0x11E4) +#define EXYNOS5_JPEG_MEM_SYS_PWR_REG S5P_PMUREG(0x11E8) +#define EXYNOS5_HSI_MEM_SYS_PWR_REG S5P_PMUREG(0x11EC) +#define EXYNOS5_MCUIOP_MEM_SYS_PWR_REG S5P_PMUREG(0x11F4) +#define EXYNOS5_SATA_MEM_SYS_PWR_REG S5P_PMUREG(0x11FC) +#define EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG S5P_PMUREG(0x1200) +#define EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG S5P_PMUREG(0x1204) +#define EXYNOS5_PAD_RETENTION_EFNAND_SYS_PWR_REG S5P_PMUREG(0x1208) +#define EXYNOS5_PAD_RETENTION_GPIO_SYS_PWR_REG S5P_PMUREG(0x1220) +#define EXYNOS5_PAD_RETENTION_UART_SYS_PWR_REG S5P_PMUREG(0x1224) +#define EXYNOS5_PAD_RETENTION_MMCA_SYS_PWR_REG S5P_PMUREG(0x1228) +#define EXYNOS5_PAD_RETENTION_MMCB_SYS_PWR_REG S5P_PMUREG(0x122C) +#define EXYNOS5_PAD_RETENTION_EBIA_SYS_PWR_REG S5P_PMUREG(0x1230) +#define EXYNOS5_PAD_RETENTION_EBIB_SYS_PWR_REG S5P_PMUREG(0x1234) +#define EXYNOS5_PAD_RETENTION_SPI_SYS_PWR_REG S5P_PMUREG(0x1238) +#define EXYNOS5_PAD_RETENTION_GPIO_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x123C) +#define EXYNOS5_PAD_ISOLATION_SYS_PWR_REG S5P_PMUREG(0x1240) +#define EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1250) +#define EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG S5P_PMUREG(0x1260) +#define EXYNOS5_XUSBXTI_SYS_PWR_REG S5P_PMUREG(0x1280) +#define EXYNOS5_XXTI_SYS_PWR_REG S5P_PMUREG(0x1284) +#define EXYNOS5_EXT_REGULATOR_SYS_PWR_REG S5P_PMUREG(0x12C0) +#define EXYNOS5_GPIO_MODE_SYS_PWR_REG S5P_PMUREG(0x1300) +#define EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG S5P_PMUREG(0x1320) +#define EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG S5P_PMUREG(0x1340) +#define EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG S5P_PMUREG(0x1344) +#define EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG S5P_PMUREG(0x1348) +#define EXYNOS5_GSCL_SYS_PWR_REG S5P_PMUREG(0x1400) +#define EXYNOS5_ISP_SYS_PWR_REG S5P_PMUREG(0x1404) +#define EXYNOS5_MFC_SYS_PWR_REG S5P_PMUREG(0x1408) +#define EXYNOS5_G3D_SYS_PWR_REG S5P_PMUREG(0x140C) +#define EXYNOS5_DISP1_SYS_PWR_REG S5P_PMUREG(0x1414) +#define EXYNOS5_MAU_SYS_PWR_REG S5P_PMUREG(0x1418) +#define EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG S5P_PMUREG(0x1480) +#define EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG S5P_PMUREG(0x1484) +#define EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG S5P_PMUREG(0x1488) +#define EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG S5P_PMUREG(0x148C) +#define EXYNOS5_CMU_CLKSTOP_DISP1_SYS_PWR_REG S5P_PMUREG(0x1494) +#define EXYNOS5_CMU_CLKSTOP_MAU_SYS_PWR_REG S5P_PMUREG(0x1498) +#define EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG S5P_PMUREG(0x14C0) +#define EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG S5P_PMUREG(0x14C4) +#define EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG S5P_PMUREG(0x14C8) +#define EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG S5P_PMUREG(0x14CC) +#define EXYNOS5_CMU_SYSCLK_DISP1_SYS_PWR_REG S5P_PMUREG(0x14D4) +#define EXYNOS5_CMU_SYSCLK_MAU_SYS_PWR_REG S5P_PMUREG(0x14D8) +#define EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG S5P_PMUREG(0x1580) +#define EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG S5P_PMUREG(0x1584) +#define EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG S5P_PMUREG(0x1588) +#define EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG S5P_PMUREG(0x158C) +#define EXYNOS5_CMU_RESET_DISP1_SYS_PWR_REG S5P_PMUREG(0x1594) +#define EXYNOS5_CMU_RESET_MAU_SYS_PWR_REG S5P_PMUREG(0x1598) + +#define EXYNOS5_ARM_CORE0_OPTION S5P_PMUREG(0x2008) +#define EXYNOS5_ARM_CORE1_OPTION S5P_PMUREG(0x2088) +#define EXYNOS5_FSYS_ARM_OPTION S5P_PMUREG(0x2208) +#define EXYNOS5_ISP_ARM_OPTION S5P_PMUREG(0x2288) +#define EXYNOS5_ARM_COMMON_OPTION S5P_PMUREG(0x2408) +#define EXYNOS5_ARM_L2_OPTION S5P_PMUREG(0x2608) +#define EXYNOS5_TOP_PWR_OPTION S5P_PMUREG(0x2C48) +#define EXYNOS5_TOP_PWR_SYSMEM_OPTION S5P_PMUREG(0x2CC8) +#define EXYNOS5_JPEG_MEM_OPTION S5P_PMUREG(0x2F48) +#define EXYNOS5_GSCL_OPTION S5P_PMUREG(0x4008) +#define EXYNOS5_ISP_OPTION S5P_PMUREG(0x4028) +#define EXYNOS5_MFC_OPTION S5P_PMUREG(0x4048) +#define EXYNOS5_G3D_OPTION S5P_PMUREG(0x4068) +#define EXYNOS5_DISP1_OPTION S5P_PMUREG(0x40A8) +#define EXYNOS5_MAU_OPTION S5P_PMUREG(0x40C8) + +#define EXYNOS5_USE_SC_FEEDBACK (1 << 1) +#define EXYNOS5_USE_SC_COUNTER (1 << 0) + +#define EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN (1 << 7) + +#define EXYNOS5_OPTION_USE_STANDBYWFE (1 << 24) +#define EXYNOS5_OPTION_USE_STANDBYWFI (1 << 16) + +#define EXYNOS5_OPTION_USE_RETENTION (1 << 4) + +#define EXYNOS5420_SWRESET_KFC_SEL 0x3 + +#endif /* __ASM_ARCH_REGS_PMU_H */ diff --git a/arch/arm/mach-exynos/sleep.S b/arch/arm/mach-exynos/sleep.S new file mode 100644 index 00000000000..108a45f4bb6 --- /dev/null +++ b/arch/arm/mach-exynos/sleep.S @@ -0,0 +1,57 @@ +/* + * 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> + +#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 + bleq l2c310_early_resume +#endif + b cpu_resume +ENDPROC(exynos_cpu_resume) diff --git a/arch/arm/mach-exynos/smc.h b/arch/arm/mach-exynos/smc.h new file mode 100644 index 00000000000..13a1dc8ecbf --- /dev/null +++ b/arch/arm/mach-exynos/smc.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 Samsung Electronics. + * + * EXYNOS - SMC Call + * + * 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_EXYNOS_SMC_H +#define __ASM_ARCH_EXYNOS_SMC_H + +#define SMC_CMD_INIT (-1) +#define SMC_CMD_INFO (-2) +/* For Power Management */ +#define SMC_CMD_SLEEP (-3) +#define SMC_CMD_CPU1BOOT (-4) +#define SMC_CMD_CPU0AFTR (-5) +/* For CP15 Access */ +#define SMC_CMD_C15RESUME (-11) +/* For L2 Cache Access */ +#define SMC_CMD_L2X0CTRL (-21) +#define SMC_CMD_L2X0SETUP1 (-22) +#define SMC_CMD_L2X0SETUP2 (-23) +#define SMC_CMD_L2X0INVALL (-24) +#define SMC_CMD_L2X0DEBUG (-25) + +extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3); + +#endif |
