diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle-psci.c')
-rw-r--r-- | drivers/cpuidle/cpuidle-psci.c | 105 |
1 files changed, 66 insertions, 39 deletions
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index b46a83f5ffe4..b19bc60cc627 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -16,7 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/platform_device.h> +#include <linux/device/faux.h> #include <linux/psci.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> @@ -36,19 +36,29 @@ 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 +68,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 +82,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 +97,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; } @@ -108,8 +123,12 @@ static int psci_idle_cpuhp_up(unsigned int cpu) { struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); - if (pd_dev) - pm_runtime_get_sync(pd_dev); + if (pd_dev) { + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + pm_runtime_get_sync(pd_dev); + else + dev_pm_genpd_resume(pd_dev); + } return 0; } @@ -119,9 +138,13 @@ static int psci_idle_cpuhp_down(unsigned int cpu) struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); if (pd_dev) { - pm_runtime_put_sync(pd_dev); + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) + pm_runtime_put_sync(pd_dev); + else + dev_pm_genpd_suspend(pd_dev); + /* Clear domain state to start fresh at next online. */ - psci_set_domain_state(0); + psci_clear_domain_state(); } return 0; @@ -147,7 +170,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; } } @@ -180,9 +203,6 @@ static void psci_idle_init_cpuhp(void) { int err; - if (!psci_cpuidle_use_cpuhp) - return; - err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING, "cpuidle/psci:online", psci_idle_cpuhp_up, @@ -243,10 +263,8 @@ static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv, * s2ram and s2idle. */ drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state; - if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) drv->states[state_count - 1].enter = psci_enter_domain_idle_state; - psci_cpuidle_use_cpuhp = true; - } return 0; } @@ -323,7 +341,6 @@ static void psci_cpu_deinit_idle(int cpu) dt_idle_detach_cpu(data->dev); psci_cpuidle_use_syscore = false; - psci_cpuidle_use_cpuhp = false; } static int psci_idle_init_cpu(struct device *dev, int cpu) @@ -407,14 +424,14 @@ deinit: * to register cpuidle driver then rollback to cancel all CPUs * registration. */ -static int psci_cpuidle_probe(struct platform_device *pdev) +static int psci_cpuidle_probe(struct faux_device *fdev) { int cpu, ret; struct cpuidle_driver *drv; struct cpuidle_device *dev; for_each_present_cpu(cpu) { - ret = psci_idle_init_cpu(&pdev->dev, cpu); + ret = psci_idle_init_cpu(&fdev->dev, cpu); if (ret) goto out_fail; } @@ -434,26 +451,36 @@ out_fail: return ret; } -static struct platform_driver psci_cpuidle_driver = { +static struct faux_device_ops psci_cpuidle_ops = { .probe = psci_cpuidle_probe, - .driver = { - .name = "psci-cpuidle", - }, }; +static bool __init dt_idle_state_present(void) +{ + struct device_node *cpu_node __free(device_node) = + of_cpu_device_node_get(cpumask_first(cpu_possible_mask)); + if (!cpu_node) + return false; + + struct device_node *state_node __free(device_node) = + of_get_cpu_state_node(cpu_node, 0); + if (!state_node) + return false; + + return !!of_match_node(psci_idle_state_match, state_node); +} + static int __init psci_idle_init(void) { - struct platform_device *pdev; - int ret; + struct faux_device *fdev; - ret = platform_driver_register(&psci_cpuidle_driver); - if (ret) - return ret; + if (!dt_idle_state_present()) + return 0; - pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0); - if (IS_ERR(pdev)) { - platform_driver_unregister(&psci_cpuidle_driver); - return PTR_ERR(pdev); + fdev = faux_device_create("psci-cpuidle", NULL, &psci_cpuidle_ops); + if (!fdev) { + pr_err("Failed to create psci-cpuidle device\n"); + return -ENODEV; } return 0; |