Subject: ARM AM335x: add cpuidle driver From: Carsten Emde <C.Emde@osadl.org> Date: Sat, 21 Jun 2014 17:00:55 +0100 Forward port of the cpuidle driver that is part of the TI vendor kernel for AM335x. Note that the "DDR Self Refresh" sleep state is never reached - same behavior as in the original kernel. Signed-off-by: Carsten Emde <C.Emde@osadl.org> --- arch/arm/mach-omap2/Makefile | 1 arch/arm/mach-omap2/am33xx.h | 2 arch/arm/mach-omap2/cpuidle33xx.c | 127 +++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/io.c | 2 arch/arm/mach-omap2/pm.h | 6 + arch/arm/plat-omap/include/plat/emif.h | 41 ++++++++++ 6 files changed, 179 insertions(+) Index: linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile =================================================================== @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:105 @ AFLAGS_sleep34xx.o :=-Wa,-march=armv7- endif ifeq ($(CONFIG_CPU_IDLE),y) +obj-$(CONFIG_SOC_AM33XX) += cpuidle33xx.o obj-$(CONFIG_ARCH_OMAP3) += cpuidle34xx.o obj-$(CONFIG_ARCH_OMAP4) += cpuidle44xx.o endif Index: linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/am33xx.h =================================================================== --- linux-3.12.31-rt45-r8s8.orig/arch/arm/mach-omap2/am33xx.h +++ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/am33xx.h @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:21 @ #define L4_SLOW_AM33XX_BASE 0x48000000 +#define AM33XX_EMIF0_BASE 0x4C000000 + #define AM33XX_SCM_BASE 0x44E10000 #define AM33XX_CTRL_BASE AM33XX_SCM_BASE #define AM33XX_PRCM_BASE 0x44E00000 Index: linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/cpuidle33xx.c =================================================================== --- /dev/null +++ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/cpuidle33xx.c @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:4 @ +/* + * CPU idle for AM33XX SoCs + * + * Copyright (C) 2011 Texas Instruments Incorporated. http://www.ti.com/ + * + * Derived from Davinci CPU idle code + * (arch/arm/mach-davinci/cpuidle.c) + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/cpuidle.h> +#include <linux/sched.h> +#include <asm/proc-fns.h> + +#include <plat/emif.h> + +#include "am33xx.h" + +#define AM33XX_CPUIDLE_MAX_STATES 2 + +/* fields in am33xx_ops.flags */ +#define AM33XX_CPUIDLE_FLAGS_DDR2_PWDN BIT(0) + +static void __iomem *emif_base; + +static void __iomem * __init am33xx_get_mem_ctlr(void) +{ + void __iomem *am33xx_emif_base = ioremap(AM33XX_EMIF0_BASE, SZ_32K); + + if (!am33xx_emif_base) + pr_warning("%s: Unable to map DDR3 controller", __func__); + + return am33xx_emif_base; +} + +static void am33xx_save_ddr_power(int enter, bool pdown) +{ + u32 val; + + val = __raw_readl(emif_base + EMIF4_0_SDRAM_MGMT_CTRL); + + /* TODO: Choose the mode based on memory type */ + if (enter) + val = SELF_REFRESH_ENABLE(64); + else + val = SELF_REFRESH_DISABLE; + + __raw_writel(val, emif_base + EMIF4_0_SDRAM_MGMT_CTRL); +} + +static void am33xx_c2state_enter(u32 flags) +{ + am33xx_save_ddr_power(1, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN)); +} + +static void am33xx_c2state_exit(u32 flags) +{ + am33xx_save_ddr_power(0, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN)); +} + +/** + * @dev: cpuidle device + * @drv: cpuidle driver + * @index: array index of target state to be programmed + */ +static int am33xx_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + local_irq_disable(); + + if (index == 1 && emif_base) + am33xx_c2state_enter(drv->states->flags); + + /* Wait for interrupt state */ + cpu_do_idle(); + + if (index == 1 && emif_base) + am33xx_c2state_exit(drv->states->flags); + + local_irq_enable(); + + return 0; +} + +static struct cpuidle_driver am33xx_idle_driver = { + .name = "cpuidle-am33xx", + .owner = THIS_MODULE, + .states = { + { + .enter = am33xx_enter_idle, + .exit_latency = 1, + .target_residency = 10000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "WFI", + .desc = "Wait for interrupt", + }, + { + .enter = am33xx_enter_idle, + .exit_latency = 100, + .target_residency = 10000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "DDR SR", + .desc = "WFI and DDR Self Refresh", + }, + }, + .state_count = AM33XX_CPUIDLE_MAX_STATES, + .safe_state_index = 0, +}; + +int __init am33xx_idle_init(void) +{ + emif_base = am33xx_get_mem_ctlr(); + return cpuidle_register(&am33xx_idle_driver, NULL); +} Index: linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/io.c =================================================================== --- linux-3.12.31-rt45-r8s8.orig/arch/arm/mach-omap2/io.c +++ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/io.c @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:48 @ #include "sram.h" #include "cm2xxx.h" #include "cm3xxx.h" +#include "pm.h" #include "prm.h" #include "cm.h" #include "prcm_mpu44xx.h" @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:591 @ void __init am33xx_init_early(void) void __init am33xx_init_late(void) { omap_common_late_init(); + am33xx_idle_init(); } #endif Index: linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/pm.h =================================================================== --- linux-3.12.31-rt45-r8s8.orig/arch/arm/mach-omap2/pm.h +++ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/pm.h @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:19 @ #include "powerdomain.h" #ifdef CONFIG_CPU_IDLE +extern int __init am33xx_idle_init(void); extern int __init omap3_idle_init(void); extern int __init omap4_idle_init(void); #else +static inline int am33xx_idle_init(void) +{ + return 0; +} + static inline int omap3_idle_init(void) { return 0; Index: linux-3.12.31-rt45-r8s8/arch/arm/plat-omap/include/plat/emif.h =================================================================== --- /dev/null +++ linux-3.12.31-rt45-r8s8/arch/arm/plat-omap/include/plat/emif.h @ linux-3.12.31-rt45-r8s8/arch/arm/mach-omap2/Makefile:4 @ +/* + * EMIF register definitions for TI81xx and AM33xx + * + * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __EMIF_H +#define __EMIF_H + +#define EMIF_MOD_ID_REV (0x0) +#define EMIF4_0_SDRAM_STATUS (0x04) +#define EMIF4_0_SDRAM_CONFIG (0x08) +#define EMIF4_0_SDRAM_CONFIG2 (0x0C) +#define EMIF4_0_SDRAM_REF_CTRL (0x10) +#define EMIF4_0_SDRAM_REF_CTRL_SHADOW (0x14) +#define EMIF4_0_SDRAM_TIM_1 (0x18) +#define EMIF4_0_SDRAM_TIM_1_SHADOW (0x1C) +#define EMIF4_0_SDRAM_TIM_2 (0x20) +#define EMIF4_0_SDRAM_TIM_2_SHADOW (0x24) +#define EMIF4_0_SDRAM_TIM_3 (0x28) +#define EMIF4_0_SDRAM_TIM_3_SHADOW (0x2C) +#define EMIF4_0_SDRAM_MGMT_CTRL (0x38) +#define EMIF4_0_SDRAM_MGMT_CTRL_SHD (0x3C) +#define EMIF4_0_DDR_PHY_CTRL_1 (0xE4) +#define EMIF4_0_DDR_PHY_CTRL_1_SHADOW (0xE8) +#define EMIF4_0_DDR_PHY_CTRL_2 (0xEC) +#define EMIF4_0_IODFT_TLGC (0x60) + +#define SELF_REFRESH_ENABLE(m) (0x2 << 8 | (m << 4)) +#define SELF_REFRESH_DISABLE (0x0 << 8) + +#endif /* __EMIF_H */