diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle-psci.c')
-rw-r--r-- | drivers/cpuidle/cpuidle-psci.c | 40 |
1 files changed, 28 insertions, 12 deletions
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index 40f378c1dc9f..3c2756a539c4 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -36,19 +36,30 @@ struct psci_cpuidle_data { struct device *dev; }; +struct psci_cpuidle_domain_state { + struct generic_pm_domain *pd; + unsigned int state_idx; + u32 state; +}; + static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data); -static DEFINE_PER_CPU(u32, domain_state); +static DEFINE_PER_CPU(struct psci_cpuidle_domain_state, psci_domain_state); static bool psci_cpuidle_use_syscore; static bool psci_cpuidle_use_cpuhp; -void psci_set_domain_state(u32 state) +void psci_set_domain_state(struct generic_pm_domain *pd, unsigned int state_idx, + u32 state) { - __this_cpu_write(domain_state, state); + struct psci_cpuidle_domain_state *ds = this_cpu_ptr(&psci_domain_state); + + ds->pd = pd; + ds->state_idx = state_idx; + ds->state = state; } -static inline u32 psci_get_domain_state(void) +static inline void psci_clear_domain_state(void) { - return __this_cpu_read(domain_state); + __this_cpu_write(psci_domain_state.state, 0); } static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev, @@ -58,7 +69,8 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev, struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data); u32 *states = data->psci_states; struct device *pd_dev = data->dev; - u32 state; + struct psci_cpuidle_domain_state *ds; + u32 state = states[idx]; int ret; ret = cpu_pm_enter(); @@ -71,9 +83,9 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev, else pm_runtime_put_sync_suspend(pd_dev); - state = psci_get_domain_state(); - if (!state) - state = states[idx]; + ds = this_cpu_ptr(&psci_domain_state); + if (ds->state) + state = ds->state; trace_psci_domain_idle_enter(dev->cpu, state, s2idle); ret = psci_cpu_suspend_enter(state) ? -1 : idx; @@ -86,8 +98,12 @@ static __cpuidle int __psci_enter_domain_idle_state(struct cpuidle_device *dev, cpu_pm_exit(); + /* Correct domain-idlestate statistics if we failed to enter. */ + if (ret == -1 && ds->state) + pm_genpd_inc_rejected(ds->pd, ds->state_idx); + /* Clear the domain state to start fresh when back from idle. */ - psci_set_domain_state(0); + psci_clear_domain_state(); return ret; } @@ -121,7 +137,7 @@ static int psci_idle_cpuhp_down(unsigned int cpu) if (pd_dev) { pm_runtime_put_sync(pd_dev); /* Clear domain state to start fresh at next online. */ - psci_set_domain_state(0); + psci_clear_domain_state(); } return 0; @@ -147,7 +163,7 @@ static void psci_idle_syscore_switch(bool suspend) /* Clear domain state to re-start fresh. */ if (!cleared) { - psci_set_domain_state(0); + psci_clear_domain_state(); cleared = true; } } |