These patch adds support for samsung exynos4 platform cpuidle driver to use the common cpuidle framework submitted earlier by Lorenzo Pieralisi. Basically a single interface cpu_enter_idle takes care of saving and restoring single cpu or whole cluster information. The link to the framework patchset is, http://www.spinics.net/lists/arm-kernel/msg132044.html
Description of patches in this list: 1)Patch (ARM: kernel: Some fixes in save/restore common code) adds some essential fixes for the common save/restore to work on exynos4 platform.
2)Patch (ARM: EXYNOS4: Fix to work with origen boards) adds a fix for extracting chip revision number.
3)Patch (ARM: EXYNOS4: Add support AFTR mode cpuidle state on EXYNOS4210) rebases the L2 retention C state cpuidle driver submitted earlier by Jaecheol Lee.
4)Patch (Modify cpuidle code to adapt to save/restore common code) uses the generic infrastructure to enter and exit from the retention idle state.
All these codes have been tested on Samsung ORIGEN board against the tree git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git for-next.
Amit Daniel Kachhap (4): ARM: kernel: Some fixes in save/restore common code. ARM: EXYNOS4: Fix to work with origen boards. ARM: EXYNOS4: Add support AFTR mode cpuidle state on EXYNOS4210 ARM: EXYNOS4: Modify cpuidle code to adapt to save/restore common code
arch/arm/Kconfig | 1 + arch/arm/common/gic.c | 1 - arch/arm/kernel/sr_helpers.h | 2 +- arch/arm/mach-exynos4/cpu.c | 10 ++ arch/arm/mach-exynos4/cpuidle.c | 150 +++++++++++++++++++++++++++++- arch/arm/mach-exynos4/include/mach/pmu.h | 1 - arch/arm/mm/cache-l2x0.c | 10 ++- arch/arm/plat-s5p/include/plat/exynos4.h | 1 + 8 files changed, 169 insertions(+), 7 deletions(-)
These changes are necessary to make the retention cpuidle state work. This adds support for saving more L2 registers.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- arch/arm/common/gic.c | 1 - arch/arm/kernel/sr_helpers.h | 2 +- arch/arm/mm/cache-l2x0.c | 10 +++++++++- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index d845b75..124e122 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -385,7 +385,6 @@ static void gic_dist_save(unsigned int gic_nr) gic_data[gic_nr].saved_spi_enable[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
- writel_relaxed(0, dist_base + GIC_DIST_CTRL); }
/* diff --git a/arch/arm/kernel/sr_helpers.h b/arch/arm/kernel/sr_helpers.h index 1ae3a9a..5a30461 100644 --- a/arch/arm/kernel/sr_helpers.h +++ b/arch/arm/kernel/sr_helpers.h @@ -44,7 +44,7 @@ static inline void exit_coherency(void) "mcr p15, 0, %0, c1, c0, 1\n" : "=&r" (v) : "Ir" (0x40) - : ); + : "cc"); } #else static inline void exit_coherency(void) { } diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 4c99d41..f23696b 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -285,7 +285,11 @@ static void l2x0_save_context(void *data, bool dormant, unsigned long end) *l2x0_regs = readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; *l2x0_regs = readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL); - + l2x0_regs++; + *l2x0_regs = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); + l2x0_regs++; + *l2x0_regs = readl_relaxed(l2x0_base + L2X0_POWER_CTRL); + dormant = 0; if (!dormant) { /* clean entire L2 before disabling it*/ writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); @@ -325,6 +329,10 @@ static void l2x0_restore_context(void *data, bool dormant) writel_relaxed(*l2x0_regs, l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; writel_relaxed(*l2x0_regs, l2x0_base + L2X0_DATA_LATENCY_CTRL); + l2x0_regs++; + writel_relaxed(*l2x0_regs, l2x0_base + L2X0_PREFETCH_CTRL); + l2x0_regs++; + writel_relaxed(*l2x0_regs, l2x0_base + L2X0_POWER_CTRL); /* * If L2 is retained do not invalidate */
This patch is redundant with Lorenzo's new series[1]. Adding him to cc. I suggest you rebase on top of his latest series.
On 11 Aug 19, Amit Daniel Kachhap wrote:
These changes are necessary to make the retention cpuidle state work. This adds support for saving more L2 registers.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org
arch/arm/common/gic.c | 1 - arch/arm/kernel/sr_helpers.h | 2 +- arch/arm/mm/cache-l2x0.c | 10 +++++++++- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index d845b75..124e122 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -385,7 +385,6 @@ static void gic_dist_save(unsigned int gic_nr) gic_data[gic_nr].saved_spi_enable[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
- writel_relaxed(0, dist_base + GIC_DIST_CTRL);
}
He already has this bit in his tree[1]
/* diff --git a/arch/arm/kernel/sr_helpers.h b/arch/arm/kernel/sr_helpers.h index 1ae3a9a..5a30461 100644 --- a/arch/arm/kernel/sr_helpers.h +++ b/arch/arm/kernel/sr_helpers.h @@ -44,7 +44,7 @@ static inline void exit_coherency(void) "mcr p15, 0, %0, c1, c0, 1\n" : "=&r" (v) : "Ir" (0x40)
: );
: "cc");
He has a different fix for this.
} #else static inline void exit_coherency(void) { } diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 4c99d41..f23696b 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -285,7 +285,11 @@ static void l2x0_save_context(void *data, bool dormant, unsigned long end) *l2x0_regs = readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; *l2x0_regs = readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
- l2x0_regs++;
- *l2x0_regs = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
- l2x0_regs++;
- *l2x0_regs = readl_relaxed(l2x0_base + L2X0_POWER_CTRL);
- dormant = 0; if (!dormant) { /* clean entire L2 before disabling it*/ writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
@@ -325,6 +329,10 @@ static void l2x0_restore_context(void *data, bool dormant) writel_relaxed(*l2x0_regs, l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; writel_relaxed(*l2x0_regs, l2x0_base + L2X0_DATA_LATENCY_CTRL);
l2x0_regs++;
writel_relaxed(*l2x0_regs, l2x0_base + L2X0_PREFETCH_CTRL);
l2x0_regs++;
/*writel_relaxed(*l2x0_regs, l2x0_base + L2X0_POWER_CTRL);
*/
- If L2 is retained do not invalidate
-- 1.7.1
Already fixed in his tree.
[1] git://linux-arm.org/linux-2.6-lp.git sr-test-for-linaro
On Fri, Aug 19, 2011 at 03:33:45PM +0100, Amit Kucheria wrote:
This patch is redundant with Lorenzo's new series[1]. Adding him to cc. I suggest you rebase on top of his latest series.
Yes, thanks for pointing that out.
On 11 Aug 19, Amit Daniel Kachhap wrote:
These changes are necessary to make the retention cpuidle state work. This adds support for saving more L2 registers.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org
arch/arm/common/gic.c | 1 - arch/arm/kernel/sr_helpers.h | 2 +- arch/arm/mm/cache-l2x0.c | 10 +++++++++- 3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index d845b75..124e122 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -385,7 +385,6 @@ static void gic_dist_save(unsigned int gic_nr) gic_data[gic_nr].saved_spi_enable[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
- writel_relaxed(0, dist_base + GIC_DIST_CTRL);
}
He already has this bit in his tree[1]
/* diff --git a/arch/arm/kernel/sr_helpers.h b/arch/arm/kernel/sr_helpers.h index 1ae3a9a..5a30461 100644 --- a/arch/arm/kernel/sr_helpers.h +++ b/arch/arm/kernel/sr_helpers.h @@ -44,7 +44,7 @@ static inline void exit_coherency(void) "mcr p15, 0, %0, c1, c0, 1\n" : "=&r" (v) : "Ir" (0x40)
: );
: "cc");
He has a different fix for this.
We have to rework it anyway it since it does not belong in generic code.
} #else static inline void exit_coherency(void) { } diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 4c99d41..f23696b 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -285,7 +285,11 @@ static void l2x0_save_context(void *data, bool dormant, unsigned long end) *l2x0_regs = readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; *l2x0_regs = readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
- l2x0_regs++;
- *l2x0_regs = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL);
- l2x0_regs++;
- *l2x0_regs = readl_relaxed(l2x0_base + L2X0_POWER_CTRL);
- dormant = 0;
This is not right, dormant mode depends on cluster state and L2 retention capabilities, it must not be forced. I fixed L2 stack cleaning when L2 is in retention, please fetch [1] below.
if (!dormant) { /* clean entire L2 before disabling it*/ writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); @@ -325,6 +329,10 @@ static void l2x0_restore_context(void *data, bool dormant) writel_relaxed(*l2x0_regs, l2x0_base + L2X0_TAG_LATENCY_CTRL); l2x0_regs++; writel_relaxed(*l2x0_regs, l2x0_base + L2X0_DATA_LATENCY_CTRL);
l2x0_regs++;
writel_relaxed(*l2x0_regs, l2x0_base + L2X0_PREFETCH_CTRL);
l2x0_regs++;
/*writel_relaxed(*l2x0_regs, l2x0_base + L2X0_POWER_CTRL);
*/
- If L2 is retained do not invalidate
-- 1.7.1
Already fixed in his tree.
[1] git://linux-arm.org/linux-2.6-lp.git sr-test-for-linaro
Thanks, Lorenzo
This adds a function to get the revision id.
Signed-off-by: Jaecheol Lee jc.lee@samsung.com Signed-off-by: Changhwan Youn chaos.youn@samsung.com --- arch/arm/mach-exynos4/cpu.c | 10 ++++++++++ arch/arm/plat-s5p/include/plat/exynos4.h | 1 + 2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c index 2d8a40c..8b106b8 100644 --- a/arch/arm/mach-exynos4/cpu.c +++ b/arch/arm/mach-exynos4/cpu.c @@ -234,6 +234,16 @@ static int __init exynos4_l2x0_cache_init(void) early_initcall(exynos4_l2x0_cache_init); #endif
+int exynos4_subrev(void) +{ + static int subrev = -1; + + if (unlikely(subrev < 0)) + subrev = readl(S5P_VA_CHIPID) & 0xf; + + return subrev; +} + int __init exynos4_init(void) { printk(KERN_INFO "EXYNOS4: Initializing architecture\n"); diff --git a/arch/arm/plat-s5p/include/plat/exynos4.h b/arch/arm/plat-s5p/include/plat/exynos4.h index 907caab..d62f7f7 100644 --- a/arch/arm/plat-s5p/include/plat/exynos4.h +++ b/arch/arm/plat-s5p/include/plat/exynos4.h @@ -15,6 +15,7 @@ extern void exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void exynos4_register_clocks(void); extern void exynos4_setup_clocks(void); +extern int exynos4_subrev(void);
#ifdef CONFIG_CPU_EXYNOS4210
On Fri, Aug 19, 2011 at 10:09 PM, Amit Daniel Kachhap amit.kachhap@linaro.org wrote:
This adds a function to get the revision id.
Signed-off-by: Jaecheol Lee jc.lee@samsung.com Signed-off-by: Changhwan Youn chaos.youn@samsung.com
arch/arm/mach-exynos4/cpu.c | 10 ++++++++++ arch/arm/plat-s5p/include/plat/exynos4.h | 1 + 2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c index 2d8a40c..8b106b8 100644 --- a/arch/arm/mach-exynos4/cpu.c +++ b/arch/arm/mach-exynos4/cpu.c @@ -234,6 +234,16 @@ static int __init exynos4_l2x0_cache_init(void) early_initcall(exynos4_l2x0_cache_init); #endif
+int exynos4_subrev(void) +{
- static int subrev = -1;
- if (unlikely(subrev < 0))
- subrev = readl(S5P_VA_CHIPID) & 0xf;
How about to add the clock control here? 1. Register chipid clk 2. Get the chipid clk 3. Read CHPIID, 4. Put tht chipid clk.
Then you can save some power.
Thank you, Kyungmin Park
- return subrev;
+}
int __init exynos4_init(void) { printk(KERN_INFO "EXYNOS4: Initializing architecture\n"); diff --git a/arch/arm/plat-s5p/include/plat/exynos4.h b/arch/arm/plat-s5p/include/plat/exynos4.h index 907caab..d62f7f7 100644 --- a/arch/arm/plat-s5p/include/plat/exynos4.h +++ b/arch/arm/plat-s5p/include/plat/exynos4.h @@ -15,6 +15,7 @@ extern void exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void exynos4_register_clocks(void); extern void exynos4_setup_clocks(void); +extern int exynos4_subrev(void);
#ifdef CONFIG_CPU_EXYNOS4210
-- 1.7.1
-- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 19 August 2011 19:29, Kyungmin Park kmpark@infradead.org wrote:
On Fri, Aug 19, 2011 at 10:09 PM, Amit Daniel Kachhap amit.kachhap@linaro.org wrote:
This adds a function to get the revision id.
Signed-off-by: Jaecheol Lee jc.lee@samsung.com Signed-off-by: Changhwan Youn chaos.youn@samsung.com
arch/arm/mach-exynos4/cpu.c | 10 ++++++++++ arch/arm/plat-s5p/include/plat/exynos4.h | 1 + 2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c index 2d8a40c..8b106b8 100644 --- a/arch/arm/mach-exynos4/cpu.c +++ b/arch/arm/mach-exynos4/cpu.c @@ -234,6 +234,16 @@ static int __init exynos4_l2x0_cache_init(void) early_initcall(exynos4_l2x0_cache_init); #endif
+int exynos4_subrev(void) +{
- static int subrev = -1;
- if (unlikely(subrev < 0))
- subrev = readl(S5P_VA_CHIPID) & 0xf;
How about to add the clock control here?
- Register chipid clk
- Get the chipid clk
- Read CHPIID,
- Put tht chipid clk.
Nice suggestion. Will do it in the next patch version. Actually the main focus of this patch series was to have a proof of concept of the new cpuidle common code.
Thanks, Amit Daniel
Then you can save some power.
Thank you, Kyungmin Park
- return subrev;
+}
int __init exynos4_init(void) { printk(KERN_INFO "EXYNOS4: Initializing architecture\n"); diff --git a/arch/arm/plat-s5p/include/plat/exynos4.h b/arch/arm/plat-s5p/include/plat/exynos4.h index 907caab..d62f7f7 100644 --- a/arch/arm/plat-s5p/include/plat/exynos4.h +++ b/arch/arm/plat-s5p/include/plat/exynos4.h @@ -15,6 +15,7 @@ extern void exynos4_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void exynos4_register_clocks(void); extern void exynos4_setup_clocks(void); +extern int exynos4_subrev(void);
#ifdef CONFIG_CPU_EXYNOS4210
-- 1.7.1
-- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
This patch adds support AFTR(ARM OFF TOP RUNNING) mode in cpuidle driver. L2 cache keeps their data in this mode.
Signed-off-by: Jaecheol Lee jc.lee@samsung.com Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- arch/arm/mach-exynos4/Makefile | 2 +- arch/arm/mach-exynos4/cpuidle.c | 131 +++++++++++++++++++++++- arch/arm/mach-exynos4/idle.S | 165 ++++++++++++++++++++++++++++++ arch/arm/mach-exynos4/include/mach/pmu.h | 5 +- 4 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-exynos4/idle.S
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index 2e3a407..12568b0 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_CPU_EXYNOS4210) += cpu.o init.o clock.o irq-combiner.o obj-$(CONFIG_CPU_EXYNOS4210) += setup-i2c0.o irq-eint.o dma.o pmu.o obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o -obj-$(CONFIG_CPU_IDLE) += cpuidle.o +obj-$(CONFIG_CPU_IDLE) += cpuidle.o idle.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c index bf7e96f..1164945 100644 --- a/arch/arm/mach-exynos4/cpuidle.c +++ b/arch/arm/mach-exynos4/cpuidle.c @@ -12,12 +12,24 @@ #include <linux/init.h> #include <linux/cpuidle.h> #include <linux/io.h> +#include <linux/suspend.h>
#include <asm/proc-fns.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/cacheflush.h> + +#include <mach/regs-pmu.h> +#include <mach/pmu.h> + +#define REG_DIRECTGO_ADDR (S5P_VA_SYSRAM + 0x24) +#define REG_DIRECTGO_FLAG (S5P_VA_SYSRAM + 0x20)
static int exynos4_enter_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
+static int exynos4_enter_lowpower(struct cpuidle_device *dev, + struct cpuidle_state *state); + static struct cpuidle_state exynos4_cpuidle_set[] = { [0] = { .enter = exynos4_enter_idle, @@ -27,6 +39,14 @@ static struct cpuidle_state exynos4_cpuidle_set[] = { .name = "IDLE", .desc = "ARM clock gating(WFI)", }, + [1] = { + .enter = exynos4_enter_lowpower, + .exit_latency = 300, + .target_residency = 100000, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "LOW_POWER", + .desc = "ARM power down", + }, };
static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); @@ -36,6 +56,80 @@ static struct cpuidle_driver exynos4_idle_driver = { .owner = THIS_MODULE, };
+void exynos4_cpu_lp(void *stack_addr) +{ + /* + * Refer to v7 cpu_suspend function. + * From saveblk to stack_addr + (4 * 3) + (4 * 9) + * 4byte * (v:p offset, virt sp, phy resume fn) + * cpu_suspend_size = 4 * 9 (from proc-v7.S) + * Min L2 cache clean size = 36 + 12 + 36 = 84 + */ + + outer_clean_range(virt_to_phys(stack_addr), 84); + + /* To clean sleep_save_sp area */ + + outer_clean_range(virt_to_phys(cpu_resume), 64); + + cpu_do_idle(); +} + +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ +static void exynos4_set_wakeupmask(void) +{ + __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); +} + +static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct timeval before, after; + int idle_time; + unsigned long tmp; + + local_irq_disable(); + do_gettimeofday(&before); + + exynos4_set_wakeupmask(); + + __raw_writel(virt_to_phys(exynos4_idle_resume), REG_DIRECTGO_ADDR); + __raw_writel(0xfcba0d10, REG_DIRECTGO_FLAG); + + /* Set value of power down register for aftr mode */ + exynos4_sys_powerdown_conf(SYS_AFTR); + + /* 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); + + exynos4_enter_lp(0, PLAT_PHYS_OFFSET - PAGE_OFFSET); + + /* + * 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); + } + cpu_init(); + /* Clear wakeup state register */ + __raw_writel(0x0, S5P_WAKEUP_STAT); + + do_gettimeofday(&after); + + local_irq_enable(); + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC + + (after.tv_usec - before.tv_usec); + + return idle_time; +} + static int exynos4_enter_idle(struct cpuidle_device *dev, struct cpuidle_state *state) { @@ -55,6 +149,26 @@ static int exynos4_enter_idle(struct cpuidle_device *dev, return idle_time; }
+static int exynos4_enter_lowpower(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct cpuidle_state *new_state = state; + + /* This mode only can be entered when Core1 is offline */ + if (cpu_online(1)) { + BUG_ON(!dev->safe_state); + new_state = dev->safe_state; + } + dev->last_state = new_state; + + if (new_state == &dev->states[0]) + return exynos4_enter_idle(dev, new_state); + else + return exynos4_enter_core0_aftr(dev, new_state); + + return exynos4_enter_idle(dev, new_state); +} + static int __init exynos4_init_cpuidle(void) { int i, max_cpuidle_state, cpu_id; @@ -66,8 +180,11 @@ static int __init exynos4_init_cpuidle(void) device = &per_cpu(exynos4_cpuidle_device, cpu_id); device->cpu = cpu_id;
- device->state_count = (sizeof(exynos4_cpuidle_set) / + if (cpu_id == 0) + device->state_count = (sizeof(exynos4_cpuidle_set) / sizeof(struct cpuidle_state)); + else + device->state_count = 1; /* Support IDLE only */
max_cpuidle_state = device->state_count;
@@ -76,11 +193,23 @@ static int __init exynos4_init_cpuidle(void) sizeof(struct cpuidle_state)); }
+ device->safe_state = &device->states[0]; + if (cpuidle_register_device(device)) { printk(KERN_ERR "CPUidle register device failed\n,"); return -EIO; } } + + l2cc_save[0] = __raw_readl(S5P_VA_L2CC + L2X0_PREFETCH_CTRL); + l2cc_save[1] = __raw_readl(S5P_VA_L2CC + L2X0_POWER_CTRL); + l2cc_save[2] = __raw_readl(S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL); + l2cc_save[3] = __raw_readl(S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL); + l2cc_save[4] = __raw_readl(S5P_VA_L2CC + L2X0_AUX_CTRL); + + clean_dcache_area(&l2cc_save[0], 5 * sizeof(unsigned long)); + outer_clean_range(virt_to_phys(&l2cc_save[0]), + virt_to_phys(&l2cc_save[4] + sizeof(unsigned long))); return 0; } device_initcall(exynos4_init_cpuidle); diff --git a/arch/arm/mach-exynos4/idle.S b/arch/arm/mach-exynos4/idle.S new file mode 100644 index 0000000..5a3cd41 --- /dev/null +++ b/arch/arm/mach-exynos4/idle.S @@ -0,0 +1,165 @@ +/* linux/arch/arm/mach-exynos4/idle.S + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS4210 AFTR/LPA idle 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/linkage.h> + +#include <asm/assembler.h> +#include <asm/memory.h> +#include <asm/hardware/cache-l2x0.h> + +#include <mach/map.h> + + .text + + /* + * exynos4_enter_lp + * + * entry: + * r1 = v:p offset + */ + +ENTRY(exynos4_enter_lp) + stmfd sp!, { r3 - r12, lr } + + adr r0, sleep_save_misc + + mrc p15, 0, r2, c15, c0, 0 @ read power control register + str r2, [r0], #4 + + mrc p15, 0, r2, c15, c0, 1 @ read diagnostic register + str r2, [r0], #4 + + ldr r3, =resume_with_mmu + bl cpu_suspend + + mov r0, sp + bl exynos4_cpu_lp + + /* Restore original sp */ + mov r0, sp + add r0, r0, #4 + ldr sp, [r0] + + mov r0, #0 + b early_wakeup + +resume_with_mmu: + + adr r0, sleep_save_misc + + ldr r1, [r0], #4 + mcr p15, 0, r1, c15, c0, 0 @ write power control register + + ldr r1, [r0], #4 + mcr p15, 0, r1, c15, c0, 1 @ write diagnostic register + + mov r0, #1 +early_wakeup: + + ldmfd sp!, { r3 - r12, pc } + + .ltorg + + /* + * sleep magic, to allow the bootloader to check for an valid + * image to resume to. Must be the first word before the + * s3c_cpu_resume entry. + */ + + .word 0x2bedf00d + +sleep_save_misc: + .long 0 + .long 0 + + /* + * exynos4_idle_resume + * + * resume code entry for IROM to call + * + * we must put this code here in the data segment as we have no + * other way of restoring the stack pointer after sleep, and we + * must not write to the code segment (code is read-only) + */ + .data + .align +ENTRY(exynos4_idle_resume) + ldr r0, scu_pa_addr @ load physica address of SCU + ldr r1, [r0] + orr r1, r1, #1 + orr r1, r1, #(1 << 5) + str r1, [r0] @ enable SCU + + ldr r0, l2cc_pa_addr @ load physical address of L2CC + + ldr r1, l2cc_tag_latency_ctrl @ tag latency register offset + add r1, r0, r1 + ldr r2, l2cc_tag_data @ load saved tag latency register + str r2, [r1] @ store saved value to register + + ldr r1, l2cc_data_latency_ctrl @ data latency register offset + add r1, r0, r1 + ldr r2, l2cc_data_data @ load saved data latency register + str r2, [r1] @ store saved value to register + + ldr r1, l2cc_prefetch_ctrl @ prefetch control register offset + add r1, r0, r1 + ldr r2, l2cc_prefetch_data @ load saved prefetch control register + str r2, [r1] @ store saved value to register + + ldr r1, l2cc_pwr_ctrl @ power control register offset + add r1, r0, r1 + ldr r2, l2cc_pwr_data @ load saved power control register + str r2, [r1] @ store saved value to register + + ldr r1, l2cc_aux_ctrl @ aux control register offset + add r1, r0, r1 + ldr r2, l2cc_aux_data @ load saved aux control register + str r2, [r1] @ store saved value to register + + ldr r1, l2cc_ctrl @ control register offset + add r1, r0, r1 + mov r2, #1 @ enable L2CC + str r2, [r1] + + b cpu_resume +ENDPROC(exynos4_idle_resume) + + .global l2cc_save + +scu_pa_addr: + .word EXYNOS4_PA_COREPERI +l2cc_pa_addr: + .word EXYNOS4_PA_L2CC +l2cc_prefetch_ctrl: + .word L2X0_PREFETCH_CTRL +l2cc_pwr_ctrl: + .word L2X0_POWER_CTRL +l2cc_tag_latency_ctrl: + .word L2X0_TAG_LATENCY_CTRL +l2cc_data_latency_ctrl: + .word L2X0_DATA_LATENCY_CTRL +l2cc_aux_ctrl: + .word L2X0_AUX_CTRL +l2cc_ctrl: + .word L2X0_CTRL +l2cc_save: +l2cc_prefetch_data: + .long 0 +l2cc_pwr_data: + .long 0 +l2cc_tag_data: + .long 0 +l2cc_data_data: + .long 0 +l2cc_aux_data: + .long 0 diff --git a/arch/arm/mach-exynos4/include/mach/pmu.h b/arch/arm/mach-exynos4/include/mach/pmu.h index a952904..960456f 100644 --- a/arch/arm/mach-exynos4/include/mach/pmu.h +++ b/arch/arm/mach-exynos4/include/mach/pmu.h @@ -21,5 +21,8 @@ enum sys_powerdown { };
extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode); - +extern void exynos4_idle_resume(void); +extern void exynos4_enter_lp(unsigned long arg, long offset); +/* Keep following save sequence prefetch, power, tag, data, aux */ +extern unsigned long l2cc_save[5]; #endif /* __ASM_ARCH_PMU_H */
On Fri, Aug 19, 2011 at 06:39:59PM +0530, Amit Daniel Kachhap wrote:
+ENTRY(exynos4_enter_lp)
- stmfd sp!, { r3 - r12, lr }
- adr r0, sleep_save_misc
- mrc p15, 0, r2, c15, c0, 0 @ read power control register
- str r2, [r0], #4
- mrc p15, 0, r2, c15, c0, 1 @ read diagnostic register
- str r2, [r0], #4
- ldr r3, =resume_with_mmu
- bl cpu_suspend
- mov r0, sp
- bl exynos4_cpu_lp
- /* Restore original sp */
- mov r0, sp
- add r0, r0, #4
- ldr sp, [r0]
- mov r0, #0
- b early_wakeup
This is based upon old kernel code. Clearly hasn't been tested with anything later than 3.0.
On 21 August 2011 22:48, Russell King - ARM Linux linux@arm.linux.org.uk wrote:
On Fri, Aug 19, 2011 at 06:39:59PM +0530, Amit Daniel Kachhap wrote:
+ENTRY(exynos4_enter_lp)
- stmfd sp!, { r3 - r12, lr }
- adr r0, sleep_save_misc
- mrc p15, 0, r2, c15, c0, 0 @ read power control register
- str r2, [r0], #4
- mrc p15, 0, r2, c15, c0, 1 @ read diagnostic register
- str r2, [r0], #4
- ldr r3, =resume_with_mmu
- bl cpu_suspend
- mov r0, sp
- bl exynos4_cpu_lp
- /* Restore original sp */
- mov r0, sp
- add r0, r0, #4
- ldr sp, [r0]
- mov r0, #0
- b early_wakeup
This is based upon old kernel code. Clearly hasn't been tested with anything later than 3.0.
You are right. I will rebase it to the latest tree in the next version. The main intention of the patch series (mostly patch 4) is to collect information or issues on samsung platform with the new common cpuidle framework.
The L2 retention cpuidle state is modified to use the interfaces exposed by the lorenzo's cpuidle consolidation code. As can be seen lot of code is reduced in the machine directory.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- arch/arm/Kconfig | 1 + arch/arm/mach-exynos4/Makefile | 2 +- arch/arm/mach-exynos4/cpuidle.c | 85 +++++++++------- arch/arm/mach-exynos4/idle.S | 165 ------------------------------ arch/arm/mach-exynos4/include/mach/pmu.h | 4 - 5 files changed, 52 insertions(+), 205 deletions(-) delete mode 100644 arch/arm/mach-exynos4/idle.S
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a4435d6..4f97bb5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -786,6 +786,7 @@ config ARCH_EXYNOS4 select CLKDEV_LOOKUP select ARCH_HAS_CPUFREQ select GENERIC_CLOCKEVENTS + select ARCH_USES_CPU_PM select HAVE_S3C_RTC if RTC_CLASS select HAVE_S3C2410_I2C if I2C select HAVE_S3C2410_WATCHDOG if WATCHDOG diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index 12568b0..2e3a407 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_CPU_EXYNOS4210) += cpu.o init.o clock.o irq-combiner.o obj-$(CONFIG_CPU_EXYNOS4210) += setup-i2c0.o irq-eint.o dma.o pmu.o obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o -obj-$(CONFIG_CPU_IDLE) += cpuidle.o idle.o +obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c index 1164945..a15f038 100644 --- a/arch/arm/mach-exynos4/cpuidle.c +++ b/arch/arm/mach-exynos4/cpuidle.c @@ -13,16 +13,23 @@ #include <linux/cpuidle.h> #include <linux/io.h> #include <linux/suspend.h> - +#include <linux/slab.h> +#include <linux/err.h> #include <asm/proc-fns.h> #include <asm/hardware/cache-l2x0.h> #include <asm/cacheflush.h> - +#include <asm/unified.h> +#include <asm/sr_platform_api.h> #include <mach/regs-pmu.h> #include <mach/pmu.h>
-#define REG_DIRECTGO_ADDR (S5P_VA_SYSRAM + 0x24) -#define REG_DIRECTGO_FLAG (S5P_VA_SYSRAM + 0x20) +#include <plat/exynos4.h> + +#define REG_DIRECTGO_ADDR (exynos4_subrev() == 0 ?\ + (S5P_VA_SYSRAM + 0x24) : S5P_INFORM7) +#define REG_DIRECTGO_FLAG (exynos4_subrev() == 0 ?\ + (S5P_VA_SYSRAM + 0x20) : S5P_INFORM6) +
static int exynos4_enter_idle(struct cpuidle_device *dev, struct cpuidle_state *state); @@ -56,29 +63,30 @@ static struct cpuidle_driver exynos4_idle_driver = { .owner = THIS_MODULE, };
-void exynos4_cpu_lp(void *stack_addr) +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ +static void exynos4_set_wakeupmask(void) { - /* - * Refer to v7 cpu_suspend function. - * From saveblk to stack_addr + (4 * 3) + (4 * 9) - * 4byte * (v:p offset, virt sp, phy resume fn) - * cpu_suspend_size = 4 * 9 (from proc-v7.S) - * Min L2 cache clean size = 36 + 12 + 36 = 84 - */ - - outer_clean_range(virt_to_phys(stack_addr), 84); - - /* To clean sleep_save_sp area */ + __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); +}
- outer_clean_range(virt_to_phys(cpu_resume), 64); +unsigned int g_pwr_ctrl, g_diag_reg;
- cpu_do_idle(); +static void save_cpu_arch_register(void) +{ + /*read power control register*/ + asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc"); + /*read diagnostic register*/ + asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); + return; }
-/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ -static void exynos4_set_wakeupmask(void) +static void restore_cpu_arch_register(void) { - __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); + /*write power control register*/ + asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc"); + /*write diagnostic register*/ + asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); + return; }
static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, @@ -93,18 +101,24 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
exynos4_set_wakeupmask();
- __raw_writel(virt_to_phys(exynos4_idle_resume), REG_DIRECTGO_ADDR); + __raw_writel(BSYM(virt_to_phys(arch_reset_handler())), + REG_DIRECTGO_ADDR); __raw_writel(0xfcba0d10, REG_DIRECTGO_FLAG);
/* Set value of power down register for aftr mode */ exynos4_sys_powerdown_conf(SYS_AFTR);
+ save_cpu_arch_register(); + /* 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);
- exynos4_enter_lp(0, PLAT_PHYS_OFFSET - PAGE_OFFSET); + /*cstate=SHUTDOWN, cluster=RETENTION, save=ALL*/ + cpu_enter_idle(3, 2, SR_SAVE_ALL); + + restore_cpu_arch_register();
/* * If PMU failed while entering sleep mode, WFI will be @@ -117,7 +131,6 @@ static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, tmp |= S5P_CENTRAL_LOWPWR_CFG; __raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); } - cpu_init(); /* Clear wakeup state register */ __raw_writel(0x0, S5P_WAKEUP_STAT);
@@ -139,7 +152,8 @@ static int exynos4_enter_idle(struct cpuidle_device *dev, local_irq_disable(); do_gettimeofday(&before);
- cpu_do_idle(); + /*cstate=STANDBY, cluster=RUN, save=no flags*/ + cpu_enter_idle(1, 0, 0);
do_gettimeofday(&after); local_irq_enable(); @@ -169,6 +183,17 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev, return exynos4_enter_idle(dev, new_state); }
+void *platform_context_pointer(unsigned int size) +{ + void *memory; + memory = kmalloc(size, GFP_KERNEL); + if (memory == NULL) { + printk(KERN_ERR "Memory size = %u not available", size); + return ERR_PTR(-ENOMEM); + } + return virt_to_phys(memory); +} + static int __init exynos4_init_cpuidle(void) { int i, max_cpuidle_state, cpu_id; @@ -200,16 +225,6 @@ static int __init exynos4_init_cpuidle(void) return -EIO; } } - - l2cc_save[0] = __raw_readl(S5P_VA_L2CC + L2X0_PREFETCH_CTRL); - l2cc_save[1] = __raw_readl(S5P_VA_L2CC + L2X0_POWER_CTRL); - l2cc_save[2] = __raw_readl(S5P_VA_L2CC + L2X0_TAG_LATENCY_CTRL); - l2cc_save[3] = __raw_readl(S5P_VA_L2CC + L2X0_DATA_LATENCY_CTRL); - l2cc_save[4] = __raw_readl(S5P_VA_L2CC + L2X0_AUX_CTRL); - - clean_dcache_area(&l2cc_save[0], 5 * sizeof(unsigned long)); - outer_clean_range(virt_to_phys(&l2cc_save[0]), - virt_to_phys(&l2cc_save[4] + sizeof(unsigned long))); return 0; } device_initcall(exynos4_init_cpuidle); diff --git a/arch/arm/mach-exynos4/idle.S b/arch/arm/mach-exynos4/idle.S deleted file mode 100644 index 5a3cd41..0000000 --- a/arch/arm/mach-exynos4/idle.S +++ /dev/null @@ -1,165 +0,0 @@ -/* linux/arch/arm/mach-exynos4/idle.S - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4210 AFTR/LPA idle 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/linkage.h> - -#include <asm/assembler.h> -#include <asm/memory.h> -#include <asm/hardware/cache-l2x0.h> - -#include <mach/map.h> - - .text - - /* - * exynos4_enter_lp - * - * entry: - * r1 = v:p offset - */ - -ENTRY(exynos4_enter_lp) - stmfd sp!, { r3 - r12, lr } - - adr r0, sleep_save_misc - - mrc p15, 0, r2, c15, c0, 0 @ read power control register - str r2, [r0], #4 - - mrc p15, 0, r2, c15, c0, 1 @ read diagnostic register - str r2, [r0], #4 - - ldr r3, =resume_with_mmu - bl cpu_suspend - - mov r0, sp - bl exynos4_cpu_lp - - /* Restore original sp */ - mov r0, sp - add r0, r0, #4 - ldr sp, [r0] - - mov r0, #0 - b early_wakeup - -resume_with_mmu: - - adr r0, sleep_save_misc - - ldr r1, [r0], #4 - mcr p15, 0, r1, c15, c0, 0 @ write power control register - - ldr r1, [r0], #4 - mcr p15, 0, r1, c15, c0, 1 @ write diagnostic register - - mov r0, #1 -early_wakeup: - - ldmfd sp!, { r3 - r12, pc } - - .ltorg - - /* - * sleep magic, to allow the bootloader to check for an valid - * image to resume to. Must be the first word before the - * s3c_cpu_resume entry. - */ - - .word 0x2bedf00d - -sleep_save_misc: - .long 0 - .long 0 - - /* - * exynos4_idle_resume - * - * resume code entry for IROM to call - * - * we must put this code here in the data segment as we have no - * other way of restoring the stack pointer after sleep, and we - * must not write to the code segment (code is read-only) - */ - .data - .align -ENTRY(exynos4_idle_resume) - ldr r0, scu_pa_addr @ load physica address of SCU - ldr r1, [r0] - orr r1, r1, #1 - orr r1, r1, #(1 << 5) - str r1, [r0] @ enable SCU - - ldr r0, l2cc_pa_addr @ load physical address of L2CC - - ldr r1, l2cc_tag_latency_ctrl @ tag latency register offset - add r1, r0, r1 - ldr r2, l2cc_tag_data @ load saved tag latency register - str r2, [r1] @ store saved value to register - - ldr r1, l2cc_data_latency_ctrl @ data latency register offset - add r1, r0, r1 - ldr r2, l2cc_data_data @ load saved data latency register - str r2, [r1] @ store saved value to register - - ldr r1, l2cc_prefetch_ctrl @ prefetch control register offset - add r1, r0, r1 - ldr r2, l2cc_prefetch_data @ load saved prefetch control register - str r2, [r1] @ store saved value to register - - ldr r1, l2cc_pwr_ctrl @ power control register offset - add r1, r0, r1 - ldr r2, l2cc_pwr_data @ load saved power control register - str r2, [r1] @ store saved value to register - - ldr r1, l2cc_aux_ctrl @ aux control register offset - add r1, r0, r1 - ldr r2, l2cc_aux_data @ load saved aux control register - str r2, [r1] @ store saved value to register - - ldr r1, l2cc_ctrl @ control register offset - add r1, r0, r1 - mov r2, #1 @ enable L2CC - str r2, [r1] - - b cpu_resume -ENDPROC(exynos4_idle_resume) - - .global l2cc_save - -scu_pa_addr: - .word EXYNOS4_PA_COREPERI -l2cc_pa_addr: - .word EXYNOS4_PA_L2CC -l2cc_prefetch_ctrl: - .word L2X0_PREFETCH_CTRL -l2cc_pwr_ctrl: - .word L2X0_POWER_CTRL -l2cc_tag_latency_ctrl: - .word L2X0_TAG_LATENCY_CTRL -l2cc_data_latency_ctrl: - .word L2X0_DATA_LATENCY_CTRL -l2cc_aux_ctrl: - .word L2X0_AUX_CTRL -l2cc_ctrl: - .word L2X0_CTRL -l2cc_save: -l2cc_prefetch_data: - .long 0 -l2cc_pwr_data: - .long 0 -l2cc_tag_data: - .long 0 -l2cc_data_data: - .long 0 -l2cc_aux_data: - .long 0 diff --git a/arch/arm/mach-exynos4/include/mach/pmu.h b/arch/arm/mach-exynos4/include/mach/pmu.h index 960456f..d23194d 100644 --- a/arch/arm/mach-exynos4/include/mach/pmu.h +++ b/arch/arm/mach-exynos4/include/mach/pmu.h @@ -21,8 +21,4 @@ enum sys_powerdown { };
extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode); -extern void exynos4_idle_resume(void); -extern void exynos4_enter_lp(unsigned long arg, long offset); -/* Keep following save sequence prefetch, power, tag, data, aux */ -extern unsigned long l2cc_save[5]; #endif /* __ASM_ARCH_PMU_H */