[PATCH 14/15] ARM: OMAP4+: CPUidle: Add OMAP5 idle driver support
Nishanth Menon
nm at ti.com
Fri Mar 1 19:25:42 EST 2013
On 17:41-20130301, Santosh Shilimkar wrote:
> The OMAP5 idle driver can re-use OMAP4 CPUidle driver implementation thanks
> to compatible MPUSS design.
>
> Though unlike OMAP4, on OMAP5 devices, MPUSS CSWR (Close Switch Retention)
> power states can be achieved with respective power states on CPU0 and CPU1
> power domain. This mode was broken on OMAP4 devices because of hardware
> limitation. Also there is no CPU low power entry order requirement like
> master CPU etc for CSWR C-state, which is icing on the cake. Code makes
> use of voting scheme for cluster low power state to support MPUSS CSWR
> C-state.
>
> Supported OMAP5 CPUidle C-states:
> C1 - CPU0 ON(WFI) + CPU1 ON(WFI) + MPUSS ON
> C2 - CPU0 CSWR + CPU1 CSWR + MPUSS CSWR
> C3 - CPU0 OFF + CPU1 Force OFF + MPUSS OSWR
think you meant CPU1 OFF?
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar at ti.com>
> ---
> arch/arm/mach-omap2/Kconfig | 1 +
> arch/arm/mach-omap2/Makefile | 4 +-
> .../{cpuidle44xx.c => cpuidle_omap4plus.c} | 103 +++++++++++++++++++-
> arch/arm/mach-omap2/omap-mpuss-lowpower.c | 7 +-
> arch/arm/mach-omap2/pm_omap4plus.c | 2 +-
> 5 files changed, 110 insertions(+), 7 deletions(-)
> rename arch/arm/mach-omap2/{cpuidle44xx.c => cpuidle_omap4plus.c} (70%)
>
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 41b581f..2df91dc 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -82,6 +82,7 @@ config SOC_OMAP5
> select CPU_V7
> select HAVE_SMP
> select COMMON_CLK
> + select ARCH_NEEDS_CPU_IDLE_COUPLED
>
> comment "OMAP Core Type"
> depends on ARCH_OMAP2
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 7c3c6b6..f6ff88f 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -97,8 +97,10 @@ endif
> endif
>
> ifeq ($(CONFIG_CPU_IDLE),y)
> +omap4plus-common-idle = cpuidle_omap4plus.o
> obj-$(CONFIG_ARCH_OMAP3) += cpuidle34xx.o
> -obj-$(CONFIG_ARCH_OMAP4) += cpuidle44xx.o
> +obj-$(CONFIG_ARCH_OMAP4) += $(omap4plus-common-idle)
> +obj-$(CONFIG_SOC_OMAP5) += $(omap4plus-common-idle)
nit pick: simpler to use cpuidle_omap4plus.o or do we expect more objs -
like moving the idle_data to DTS or so?
> endif
>
> # PRCM
> diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle_omap4plus.c
> similarity index 70%
> rename from arch/arm/mach-omap2/cpuidle44xx.c
> rename to arch/arm/mach-omap2/cpuidle_omap4plus.c
> index 23286c1..8681fa6 100644
> --- a/arch/arm/mach-omap2/cpuidle44xx.c
> +++ b/arch/arm/mach-omap2/cpuidle_omap4plus.c
> @@ -29,6 +29,7 @@ struct idle_statedata {
> u32 cpu_state;
> u32 mpu_logic_state;
> u32 mpu_state;
> + u32 mpu_state_vote;
> };
>
> static struct idle_statedata omap4_idle_data[] = {
> @@ -49,17 +50,36 @@ static struct idle_statedata omap4_idle_data[] = {
> },
> };
>
> +static struct idle_statedata omap5_idle_data[] = {
> + {
> + .cpu_state = PWRDM_POWER_ON,
> + .mpu_state = PWRDM_POWER_ON,
> + .mpu_logic_state = PWRDM_POWER_RET,
> + },
> + {
> + .cpu_state = PWRDM_POWER_RET,
CSWR I assume -> Documenting it helps? or we should introdce
cpu_logic_state to be explicit?
> + .mpu_state = PWRDM_POWER_RET,
> + .mpu_logic_state = PWRDM_POWER_RET,
> + },
> + {
> + .cpu_state = PWRDM_POWER_OFF,
CSWR I assume -> Documenting it helps?
> + .mpu_state = PWRDM_POWER_RET,
> + .mpu_logic_state = PWRDM_POWER_OFF,
> + },
> +};
> +
> static struct powerdomain *mpu_pd, *cpu_pd[NR_CPUS];
> static struct clockdomain *cpu_clkdm[NR_CPUS];
>
> static atomic_t abort_barrier;
> static bool cpu_done[NR_CPUS];
> static struct idle_statedata *state_ptr;
> +static DEFINE_RAW_SPINLOCK(mpu_lock);
>
> /* Private functions */
>
> /**
> - * omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions
> + * omap_enter_idle_[simple/smp/coupled] - OMAP4PLUS cpuidle entry functions
> * @dev: cpuidle device
> * @drv: cpuidle driver
> * @index: the index of state to be entered
> @@ -132,6 +152,8 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
>
> /* Wakeup CPU1 only if it is not offlined */
> if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
> + /* Restore MPU PD state post idle */
> + omap_set_pwrdm_state(mpu_pd, PWRDM_POWER_ON);
> clkdm_wakeup(cpu_clkdm[1]);
> omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON);
> clkdm_allow_idle(cpu_clkdm[1]);
> @@ -160,6 +182,37 @@ fail:
> return index;
> }
>
> +static int omap_enter_idle_smp(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv,
> + int index)
> +{
> + struct idle_statedata *cx = state_ptr + index;
> + int cpu_id = smp_processor_id();
> + unsigned long flag;
> +
> + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
> +
> + raw_spin_lock_irqsave(&mpu_lock, flag);
> + cx->mpu_state_vote++;
> + if (cx->mpu_state_vote == num_online_cpus()) {
> + pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
> + omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
> + }
> + raw_spin_unlock_irqrestore(&mpu_lock, flag);
> +
> + omap4_enter_lowpower(dev->cpu, cx->cpu_state);
> +
> + raw_spin_lock_irqsave(&mpu_lock, flag);
> + if (cx->mpu_state_vote == num_online_cpus())
> + omap_set_pwrdm_state(mpu_pd, PWRDM_POWER_ON);
> + cx->mpu_state_vote--;
> + raw_spin_unlock_irqrestore(&mpu_lock, flag);
> +
> + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
> +
> + return index;
> +}
> +
> /*
> * For each cpu, setup the broadcast timer because local timers
> * stops for the states above C1.
> @@ -209,6 +262,43 @@ static struct cpuidle_driver omap4_idle_driver = {
> .safe_state_index = 0,
> };
>
> +static struct cpuidle_driver omap5_idle_driver = {
> + .name = "omap5_idle",
> + .owner = THIS_MODULE,
> + .en_core_tk_irqen = 1,
> + .states = {
> + {
> + /* C1 - CPU0 ON + CPU1 ON + MPU ON */
we could move these to .desc
> + .exit_latency = 2 + 2,
> + .target_residency = 5,
the obvious question on how these latencies were arrived at..
> + .flags = CPUIDLE_FLAG_TIME_VALID,
> + .enter = omap_enter_idle_simple,
> + .name = "C1",
> + .desc = "MPUSS ON"
> + },
> + {
> + /* C2 - CPU0 CSWR + CPU1 CSWR + MPU CSWR */
we could move these to .desc
> + .exit_latency = 16 + 16,
> + .target_residency = 40,
> + .flags = CPUIDLE_FLAG_TIME_VALID,
> + .enter = omap_enter_idle_smp,
> + .name = "C2",
> + .desc = "MPUSS CSWR",
> + },
> + {
> + /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
we could move these to .desc
> + .exit_latency = 460 + 518,
> + .target_residency = 1100,
> + .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
> + .enter = omap_enter_idle_coupled,
> + .name = "C3",
> + .desc = "MPUSS OSWR",
> + },
> + },
> + .state_count = ARRAY_SIZE(omap5_idle_data),
> + .safe_state_index = 0,
> +};
> +
> /* Public functions */
>
> /**
> @@ -220,7 +310,7 @@ static struct cpuidle_driver omap4_idle_driver = {
> int __init omap4_idle_init(void)
> {
> struct cpuidle_device *dev;
> - unsigned int cpu_id = 0;
> + unsigned cpu_id = 0;
>
> /* Configure the broadcast timer on each cpu */
> mpu_pd = pwrdm_lookup("mpu_pwrdm");
> @@ -243,8 +333,13 @@ int __init omap4_idle_init(void)
> #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
> dev->coupled_cpus = *cpu_online_mask;
> #endif
> - state_ptr = &omap4_idle_data[0];
> - cpuidle_register_driver(&omap4_idle_driver);
> + if (cpu_is_omap44xx()) {
> + state_ptr = &omap4_idle_data[0];
> + cpuidle_register_driver(&omap4_idle_driver);
> + } else if (soc_is_omap54xx()) {
> + state_ptr = &omap5_idle_data[0];
> + cpuidle_register_driver(&omap5_idle_driver);
> + }
we'd be doing cpuidle_register_driver twice since it is within the
for_each_cpu loop - we dont want to do that.
>
> if (cpuidle_register_device(dev)) {
> pr_err("%s: CPUidle register failed\n", __func__);
> diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> index 5d32444..0ccb76e 100644
> --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
> @@ -87,7 +87,7 @@ extern void omap5_cpu_resume(void);
> static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
> static struct powerdomain *mpuss_pd;
> static void __iomem *sar_base;
> -static u32 cpu_context_offset;
> +static u32 cpu_context_offset, cpu_cswr_supported;
>
> static int default_finish_suspend(unsigned long cpu_state)
> {
> @@ -265,6 +265,10 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
> save_state = 1;
> break;
> case PWRDM_POWER_RET:
> + if (cpu_cswr_supported) {
> + save_state = 0;
> + break;
> + }
ok, I guess this is the best we can do under the current circumstance
where PWRDM_POWER_RET means either CSWR or OSWR!
> default:
> /*
> * CPUx CSWR is invalid hardware state. Also CPUx OSWR
> @@ -449,6 +453,7 @@ int __init omap4_mpuss_init(void)
> omap_pm_ops.resume = omap5_cpu_resume;
> cpu_context_offset = OMAP54XX_RM_CPU0_CPU0_CONTEXT_OFFSET;
> enable_mercury_retention_mode();
> + cpu_cswr_supported = 1;
> }
>
> if (cpu_is_omap446x())
> diff --git a/arch/arm/mach-omap2/pm_omap4plus.c b/arch/arm/mach-omap2/pm_omap4plus.c
> index 4c37c47..d62edf5 100644
> --- a/arch/arm/mach-omap2/pm_omap4plus.c
> +++ b/arch/arm/mach-omap2/pm_omap4plus.c
> @@ -241,7 +241,7 @@ int __init omap4_pm_init(void)
> /* Overwrite the default arch_idle() */
> arm_pm_idle = omap_default_idle;
>
> - if (cpu_is_omap44xx())
> + if (cpu_is_omap44xx() || soc_is_omap54xx())
> omap4_idle_init();
>
> err2:
otherwise, OK.
--
Regards,
Nishanth Menon
More information about the linux-arm-kernel
mailing list