[PATCH 14/18] drivers: firmware: psci: Manage runtime PM in the idle path for CPUs
From: Ulf Hansson
Date: Mon May 13 2019 - 15:31:41 EST
When the hierarchical CPU topology layout is used in DT, let's allow the
CPU to be power managed through its PM domain, via deploying runtime PM
support.
To know for which idle states runtime PM reference counting is needed,
let's store the index of deepest idle state for the CPU, in a per CPU
variable. This allows psci_cpu_suspend_enter() to compare this index with
the requested idle state index and then act accordingly.
Signed-off-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
---
Changes:
- Simplify the code by using the new per CPU struct, that stores the
needed struct device*.
---
drivers/firmware/psci/psci.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 54e23d4ed0ea..2c4157d3a616 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -20,6 +20,7 @@
#include <linux/linkage.h>
#include <linux/of.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
@@ -298,6 +299,7 @@ static int __init psci_features(u32 psci_func_id)
struct psci_cpuidle_data {
u32 *psci_states;
+ u32 rpm_state_id;
struct device *dev;
};
@@ -385,6 +387,7 @@ static int psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
goto free_mem;
data->dev = dev;
+ data->rpm_state_id = drv->state_count - 1;
}
/* Idle states parsed correctly, store them in the per-cpu struct. */
@@ -481,8 +484,11 @@ static int psci_suspend_finisher(unsigned long index)
int psci_cpu_suspend_enter(unsigned long index)
{
int ret;
- u32 *state = __this_cpu_read(psci_cpuidle_data.psci_states);
- u32 composite_state = state[index - 1] | psci_get_domain_state();
+ struct psci_cpuidle_data *data = this_cpu_ptr(&psci_cpuidle_data);
+ u32 *states = data->psci_states;
+ struct device *dev = data->dev;
+ bool runtime_pm = (dev && data->rpm_state_id == index);
+ u32 composite_state;
/*
* idle state index 0 corresponds to wfi, should never be called
@@ -491,11 +497,23 @@ int psci_cpu_suspend_enter(unsigned long index)
if (WARN_ON_ONCE(!index))
return -EINVAL;
+ /*
+ * Do runtime PM if we are using the hierarchical CPU toplogy, but only
+ * when cpuidle have selected the deepest idle state for the CPU.
+ */
+ if (runtime_pm)
+ pm_runtime_put_sync_suspend(dev);
+
+ composite_state = states[index - 1] | psci_get_domain_state();
+
if (!psci_power_state_loses_context(composite_state))
ret = psci_ops.cpu_suspend(composite_state, 0);
else
ret = cpu_suspend(index, psci_suspend_finisher);
+ if (runtime_pm)
+ pm_runtime_get_sync(dev);
+
/* Clear the domain state to start fresh when back from idle. */
psci_set_domain_state(0);
--
2.17.1