[PATCH V3 1/6] ARM: exynos: Add AFTR mode cpuidle state on EXYNOS4210
amit kachhap
amit.kachhap at gmail.com
Thu Nov 24 22:40:29 EST 2011
On Thu, Nov 24, 2011 at 9:37 AM, Inderpal Singh
<inderpal.singh at linaro.org> wrote:
> Hi Amit,
>
> On 22 November 2011 15:05, Amit Daniel Kachhap <amit.kachhap at linaro.org> wrote:
>>
>> This patch adds support AFTR(ARM OFF TOP RUNNING) mode in
>> cpuidle driver. L2 cache keeps their data in this mode.
>> This patch ports the code to the latest interfaces to
>> save/restore CPU state inclusive of CPU PM notifiers, l2
>> resume and cpu_suspend/resume.
>>
>> Signed-off-by: Jaecheol Lee <jc.lee at samsung.com>
>> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi at arm.com>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap at linaro.org>
>> ---
>> arch/arm/mach-exynos/cpuidle.c | 152 ++++++++++++++++++++++++++++++-
>> arch/arm/mach-exynos/include/mach/pmu.h | 2 +
>> 2 files changed, 151 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/mach-exynos/cpuidle.c b/arch/arm/mach-exynos/cpuidle.c
>> index 21403c8..89afa93 100644
>> --- a/arch/arm/mach-exynos/cpuidle.c
>> +++ b/arch/arm/mach-exynos/cpuidle.c
>> @@ -12,15 +12,37 @@
>> #include <linux/module.h>
>> #include <linux/init.h>
>> #include <linux/cpuidle.h>
>> +#include <linux/cpu_pm.h>
>> #include <linux/io.h>
>> #include <linux/export.h>
>> #include <linux/time.h>
>>
>> #include <asm/proc-fns.h>
>> +#include <asm/smp_scu.h>
>> +#include <asm/suspend.h>
>> +#include <asm/unified.h>
>> +#include <mach/regs-pmu.h>
>> +#include <mach/pmu.h>
>> +
>> +#include <plat/exynos4.h>
>> +#include <plat/cpu.h>
>> +
>> +#define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \
>> + S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
>> + (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0))
>> +#define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \
>> + S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
>> + (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1))
>> +
>> +#define S5P_CHECK_AFTR (samsung_rev() == EXYNOS4210_REV_1_0 ? \
>> + 0xBAD00000 : 0xFCBA0D10)
>>
>> static int exynos4_enter_idle(struct cpuidle_device *dev,
>> struct cpuidle_driver *drv,
>> int index);
>> +static int exynos4_enter_lowpower(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index);
>>
>> static struct cpuidle_state exynos4_cpuidle_set[] = {
>> [0] = {
>> @@ -28,9 +50,17 @@ static struct cpuidle_state exynos4_cpuidle_set[] = {
>> .exit_latency = 1,
>> .target_residency = 100000,
>> .flags = CPUIDLE_FLAG_TIME_VALID,
>> - .name = "IDLE",
>> + .name = "C0",
>> .desc = "ARM clock gating(WFI)",
>> },
>> + [1] = {
>> + .enter = exynos4_enter_lowpower,
>> + .exit_latency = 300,
>> + .target_residency = 100000,
>> + .flags = CPUIDLE_FLAG_TIME_VALID,
>> + .name = "C1",
>> + .desc = "ARM power down",
>> + },
>> };
>>
>> static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device);
>> @@ -40,9 +70,101 @@ static struct cpuidle_driver exynos4_idle_driver = {
>> .owner = THIS_MODULE,
>> };
>>
>> +/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
>> +static void exynos4_set_wakeupmask(void)
>> +{
>> + __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
>> +}
>> +
>> +static unsigned int g_pwr_ctrl, g_diag_reg;
>> +
>> +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;
>> +}
>> +
>> +static void restore_cpu_arch_register(void)
>> +{
>> + /*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 idle_finisher(unsigned long flags)
>> +{
>> + cpu_do_idle();
>> + return 1;
>> +}
>> +
>> +static int exynos4_enter_core0_aftr(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index)
>> +{
>> + struct timeval before, after;
>> + int idle_time;
>> + unsigned long tmp;
>> +
>> + local_irq_disable();
>> + do_gettimeofday(&before);
>> +
>> + exynos4_set_wakeupmask();
>> +
>> + /* Set value of power down register for aftr mode */
>> + exynos4_sys_powerdown_conf(SYS_AFTR);
>> +
>> + __raw_writel(BSYM(virt_to_phys(s3c_cpu_resume)),
>> + REG_DIRECTGO_ADDR);
>> + __raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG);
>> +
>> + 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);
>> +
>> + cpu_pm_enter();
>> + cpu_suspend(0, idle_finisher);
>> +
>> + scu_enable(S5P_VA_SCU);
>> + cpu_pm_exit();
>> +
>> + restore_cpu_arch_register();
>> +
>> + /*
>> + * 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 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);
>> +
>> + dev->last_residency = idle_time;
>> + return index;
>> +}
>> +
>> static int exynos4_enter_idle(struct cpuidle_device *dev,
>> struct cpuidle_driver *drv,
>> - int index)
>> + int index)
>> {
>> struct timeval before, after;
>> int idle_time;
>> @@ -61,6 +183,24 @@ static int exynos4_enter_idle(struct cpuidle_device *dev,
>> return index;
>> }
>>
>> +static int exynos4_enter_lowpower(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index)
>> +{
>> + int new_index = index;
>> +
>> + /* This mode only can be entered when other core's are offline */
>> + if (num_online_cpus() > 1) {
>> + new_index = drv->safe_state_index;
>> + BUG_ON(new_index > drv->state_count);
>
> drv->state_count is not being initialized anywhere.
> Moreover, do we need BUG_ON? As we now return index to the cpuidle framework,
> can we not enter C0 and framework will know that we entered C0 instead of C1 ?
drv->state_count is initialised. Actually it is not visible in this
patch diff. The BUG_ON
will never enter as we always initialize the safe_state_index.
>
>> + }
>> +
>> + if (new_index == 0)
>> + return exynos4_enter_idle(dev, drv, new_index);
>> + else
>> + return exynos4_enter_core0_aftr(dev, drv, new_index);
>> +}
>> +
>> static int __init exynos4_init_cpuidle(void)
>> {
>> int i, max_cpuidle_state, cpu_id;
>> @@ -75,19 +215,25 @@ static int __init exynos4_init_cpuidle(void)
>> memcpy(&drv->states[i], &exynos4_cpuidle_set[i],
>> sizeof(struct cpuidle_state));
>> }
>> + drv->safe_state_index = 0;
>> cpuidle_register_driver(&exynos4_idle_driver);
>>
>> for_each_cpu(cpu_id, cpu_online_mask) {
>> device = &per_cpu(exynos4_cpuidle_device, cpu_id);
>> device->cpu = cpu_id;
>>
>> - device->state_count = drv->state_count;
>> + if (cpu_id == 0)
>> + device->state_count = (sizeof(exynos4_cpuidle_set) /
>> + sizeof(struct cpuidle_state));
>> + else
>> + device->state_count = 1; /* Support IDLE only */
>>
>> if (cpuidle_register_device(device)) {
>> printk(KERN_ERR "CPUidle register device failed\n,");
>> return -EIO;
>> }
>> }
>> +
>> return 0;
>> }
>> device_initcall(exynos4_init_cpuidle);
>> diff --git a/arch/arm/mach-exynos/include/mach/pmu.h b/arch/arm/mach-exynos/include/mach/pmu.h
>> index 632dd56..e76b7fa 100644
>> --- a/arch/arm/mach-exynos/include/mach/pmu.h
>> +++ b/arch/arm/mach-exynos/include/mach/pmu.h
>> @@ -22,11 +22,13 @@ enum sys_powerdown {
>> NUM_SYS_POWERDOWN,
>> };
>>
>> +extern unsigned long l2x0_regs_phys;
>> struct exynos4_pmu_conf {
>> void __iomem *reg;
>> unsigned int val[NUM_SYS_POWERDOWN];
>> };
>>
>> extern void exynos4_sys_powerdown_conf(enum sys_powerdown mode);
>> +extern void s3c_cpu_resume(void);
>>
>> #endif /* __ASM_ARCH_PMU_H */
>> --
>> 1.7.1
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
More information about the linux-arm-kernel
mailing list