summaryrefslogtreecommitdiff
path: root/drivers/pmdomain/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pmdomain/core.c')
-rw-r--r--drivers/pmdomain/core.c379
1 files changed, 322 insertions, 57 deletions
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index d6c1ddb807b2..0006ab3d0789 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -27,6 +27,16 @@
/* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida);
+/* The bus for genpd_providers. */
+static const struct bus_type genpd_provider_bus_type = {
+ .name = "genpd_provider",
+};
+
+/* The parent for genpd_provider devices. */
+static struct device genpd_provider_bus = {
+ .init_name = "genpd_provider",
+};
+
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
@@ -176,6 +186,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW)
#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
+#define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd)
@@ -304,10 +315,40 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
genpd->accounting_time = now;
}
+
+static void genpd_reflect_residency(struct generic_pm_domain *genpd)
+{
+ struct genpd_governor_data *gd = genpd->gd;
+ struct genpd_power_state *state, *next_state;
+ unsigned int state_idx;
+ s64 sleep_ns, target_ns;
+
+ if (!gd || !gd->reflect_residency)
+ return;
+
+ sleep_ns = ktime_to_ns(ktime_sub(ktime_get(), gd->last_enter));
+ state_idx = genpd->state_idx;
+ state = &genpd->states[state_idx];
+ target_ns = state->power_off_latency_ns + state->residency_ns;
+
+ if (sleep_ns < target_ns) {
+ state->above++;
+ } else if (state_idx < (genpd->state_count -1)) {
+ next_state = &genpd->states[state_idx + 1];
+ target_ns = next_state->power_off_latency_ns +
+ next_state->residency_ns;
+
+ if (sleep_ns >= target_ns)
+ state->below++;
+ }
+
+ gd->reflect_residency = false;
+}
#else
static inline void genpd_debug_add(struct generic_pm_domain *genpd) {}
static inline void genpd_debug_remove(struct generic_pm_domain *genpd) {}
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
+static inline void genpd_reflect_residency(struct generic_pm_domain *genpd) {}
#endif
static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
@@ -728,6 +769,64 @@ int dev_pm_genpd_rpm_always_on(struct device *dev, bool on)
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_rpm_always_on);
+/**
+ * dev_pm_genpd_is_on() - Get device's current power domain status
+ *
+ * @dev: Device to get the current power status
+ *
+ * This function checks whether the generic power domain associated with the
+ * given device is on or not by verifying if genpd_status_on equals
+ * GENPD_STATE_ON.
+ *
+ * Note: this function returns the power status of the genpd at the time of the
+ * call. The power status may change after due to activity from other devices
+ * sharing the same genpd. Therefore, this information should not be relied for
+ * long-term decisions about the device power state.
+ *
+ * Return: 'true' if the device's power domain is on, 'false' otherwise.
+ */
+bool dev_pm_genpd_is_on(struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ bool is_on;
+
+ genpd = dev_to_genpd_safe(dev);
+ if (!genpd)
+ return false;
+
+ genpd_lock(genpd);
+ is_on = genpd_status_on(genpd);
+ genpd_unlock(genpd);
+
+ return is_on;
+}
+EXPORT_SYMBOL_GPL(dev_pm_genpd_is_on);
+
+/**
+ * pm_genpd_inc_rejected() - Adjust the rejected/usage counts for an idle-state.
+ *
+ * @genpd: The PM domain the idle-state belongs to.
+ * @state_idx: The index of the idle-state that failed.
+ *
+ * In some special cases the ->power_off() callback is asynchronously powering
+ * off the PM domain, leading to that it may return zero to indicate success,
+ * even though the actual power-off could fail. To account for this correctly in
+ * the rejected/usage counts for the idle-state statistics, users can call this
+ * function to adjust the values.
+ *
+ * It is assumed that the users guarantee that the genpd doesn't get removed
+ * while this routine is getting called.
+ */
+void pm_genpd_inc_rejected(struct generic_pm_domain *genpd,
+ unsigned int state_idx)
+{
+ genpd_lock(genpd);
+ genpd->states[genpd->state_idx].rejected++;
+ genpd->states[genpd->state_idx].usage--;
+ genpd_unlock(genpd);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_inc_rejected);
+
static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{
unsigned int state_idx = genpd->state_idx;
@@ -853,31 +952,25 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
* If all of the @genpd's devices have been suspended and all of its subdomains
* have been powered down, remove power from @genpd.
*/
-static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
- unsigned int depth)
+static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
+ unsigned int depth)
{
struct pm_domain_data *pdd;
struct gpd_link *link;
unsigned int not_suspended = 0;
- int ret;
/*
* Do not try to power off the domain in the following situations:
- * (1) The domain is already in the "power off" state.
- * (2) System suspend is in progress.
- */
- if (!genpd_status_on(genpd) || genpd->prepared_count > 0)
- return 0;
-
- /*
- * Abort power off for the PM domain in the following situations:
- * (1) The domain is configured as always on.
- * (2) When the domain has a subdomain being powered on.
+ * The domain is already in the "power off" state.
+ * System suspend is in progress.
+ * The domain is configured as always on.
+ * The domain was on at boot and still need to stay on.
+ * The domain has a subdomain being powered on.
*/
- if (genpd_is_always_on(genpd) ||
- genpd_is_rpm_always_on(genpd) ||
- atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
+ if (!genpd_status_on(genpd) || genpd->prepared_count > 0 ||
+ genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) ||
+ genpd->stay_on || atomic_read(&genpd->sd_count) > 0)
+ return;
/*
* The children must be in their deepest (powered-off) states to allow
@@ -888,7 +981,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
list_for_each_entry(link, &genpd->parent_links, parent_node) {
struct generic_pm_domain *child = link->child;
if (child->state_idx < child->state_count - 1)
- return -EBUSY;
+ return;
}
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
@@ -902,15 +995,15 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
/* The device may need its PM domain to stay powered on. */
if (to_gpd_data(pdd)->rpm_always_on)
- return -EBUSY;
+ return;
}
if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
- return -EBUSY;
+ return;
if (genpd->gov && genpd->gov->power_down_ok) {
if (!genpd->gov->power_down_ok(&genpd->domain))
- return -EAGAIN;
+ return;
}
/* Default to shallowest state. */
@@ -919,12 +1012,11 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
/* Don't power off, if a child domain is waiting to power on. */
if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
+ return;
- ret = _genpd_power_off(genpd, true);
- if (ret) {
+ if (_genpd_power_off(genpd, true)) {
genpd->states[genpd->state_idx].rejected++;
- return ret;
+ return;
}
genpd->status = GENPD_STATE_OFF;
@@ -937,8 +1029,6 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
genpd_power_off(link->parent, false, depth + 1);
genpd_unlock(link->parent);
}
-
- return 0;
}
/**
@@ -957,6 +1047,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
if (genpd_status_on(genpd))
return 0;
+ /* Reflect over the entered idle-states residency for debugfs. */
+ genpd_reflect_residency(genpd);
+
/*
* The list is guaranteed not to change while the loop below is being
* executed, unless one of the parents' .power_on() callbacks fiddles
@@ -1264,6 +1357,7 @@ err_poweroff:
return ret;
}
+#ifndef CONFIG_PM_GENERIC_DOMAINS_OF
static bool pd_ignore_unused;
static int __init pd_ignore_unused_setup(char *__unused)
{
@@ -1287,14 +1381,19 @@ static int __init genpd_power_off_unused(void)
pr_info("genpd: Disabling unused power domains\n");
mutex_lock(&gpd_list_lock);
- list_for_each_entry(genpd, &gpd_list, gpd_list_node)
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
+ genpd_lock(genpd);
+ genpd->stay_on = false;
+ genpd_unlock(genpd);
genpd_queue_power_off_work(genpd);
+ }
mutex_unlock(&gpd_list_lock);
return 0;
}
late_initcall_sync(genpd_power_off_unused);
+#endif
#ifdef CONFIG_PM_SLEEP
@@ -1450,7 +1549,7 @@ static int genpd_finish_suspend(struct device *dev,
if (ret)
return ret;
- if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
+ if (device_awake_path(dev) && genpd_is_active_wakeup(genpd))
return 0;
if (genpd->dev_ops.stop && genpd->dev_ops.start &&
@@ -1505,7 +1604,7 @@ static int genpd_finish_resume(struct device *dev,
if (IS_ERR(genpd))
return -EINVAL;
- if (device_wakeup_path(dev) && genpd_is_active_wakeup(genpd))
+ if (device_awake_path(dev) && genpd_is_active_wakeup(genpd))
return resume_noirq(dev);
genpd_lock(genpd);
@@ -2214,6 +2313,8 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd)
genpd->gd = gd;
device_initialize(&genpd->dev);
genpd->dev.release = genpd_provider_release;
+ genpd->dev.bus = &genpd_provider_bus_type;
+ genpd->dev.parent = &genpd_provider_bus;
if (!genpd_is_dev_name_fw(genpd)) {
dev_set_name(&genpd->dev, "%s", genpd->name);
@@ -2229,8 +2330,10 @@ static int genpd_alloc_data(struct generic_pm_domain *genpd)
return 0;
put:
put_device(&genpd->dev);
- if (genpd->free_states == genpd_free_default_power_state)
+ if (genpd->free_states == genpd_free_default_power_state) {
kfree(genpd->states);
+ genpd->states = NULL;
+ }
free:
if (genpd_is_cpu_domain(genpd))
free_cpumask_var(genpd->cpus);
@@ -2289,10 +2392,13 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
+ genpd->stay_on = !is_off;
+ genpd->sync_state = GENPD_SYNC_STATE_OFF;
genpd->device_count = 0;
genpd->provider = NULL;
genpd->device_id = -ENXIO;
genpd->has_provider = false;
+ genpd->opp_table = NULL;
genpd->accounting_time = ktime_get_mono_fast_ns();
genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = genpd_runtime_resume;
@@ -2440,6 +2546,8 @@ struct of_genpd_provider {
static LIST_HEAD(of_genpd_providers);
/* Mutex to protect the list above. */
static DEFINE_MUTEX(of_genpd_mutex);
+/* Used to prevent registering devices before the bus. */
+static bool genpd_bus_registered;
/**
* genpd_xlate_simple() - Xlate function for direct node-domain mapping
@@ -2506,7 +2614,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
cp->node = of_node_get(np);
cp->data = data;
cp->xlate = xlate;
- fwnode_dev_initialized(&np->fwnode, true);
+ fwnode_dev_initialized(of_fwnode_handle(np), true);
mutex_lock(&of_genpd_mutex);
list_add(&cp->link, &of_genpd_providers);
@@ -2533,6 +2641,11 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
return ret;
}
+static void genpd_sync_state(struct device *dev)
+{
+ return of_genpd_sync_state(dev->of_node);
+}
+
/**
* of_genpd_add_provider_simple() - Register a simple PM domain provider
* @np: Device node pointer associated with the PM domain provider.
@@ -2541,21 +2654,43 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd)
{
+ struct fwnode_handle *fwnode;
+ struct device *dev;
int ret;
if (!np || !genpd)
return -EINVAL;
+ if (!genpd_bus_registered)
+ return -ENODEV;
+
if (!genpd_present(genpd))
return -EINVAL;
genpd->dev.of_node = np;
+ fwnode = of_fwnode_handle(np);
+ dev = get_dev_from_fwnode(fwnode);
+ if (!dev && !genpd_is_no_sync_state(genpd)) {
+ genpd->sync_state = GENPD_SYNC_STATE_SIMPLE;
+ device_set_node(&genpd->dev, fwnode);
+ } else {
+ dev_set_drv_sync_state(dev, genpd_sync_state);
+ }
+
+ put_device(dev);
+
+ ret = device_add(&genpd->dev);
+ if (ret)
+ return ret;
+
/* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table(&genpd->dev);
- if (ret)
- return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
+ if (ret) {
+ dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n");
+ goto err_del;
+ }
/*
* Save table for faster processing while setting performance
@@ -2566,19 +2701,22 @@ int of_genpd_add_provider_simple(struct device_node *np,
}
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
- if (ret) {
- if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
- dev_pm_opp_put_opp_table(genpd->opp_table);
- dev_pm_opp_of_remove_table(&genpd->dev);
- }
-
- return ret;
- }
+ if (ret)
+ goto err_opp;
- genpd->provider = &np->fwnode;
+ genpd->provider = fwnode;
genpd->has_provider = true;
return 0;
+
+err_opp:
+ if (genpd->opp_table) {
+ dev_pm_opp_put_opp_table(genpd->opp_table);
+ dev_pm_opp_of_remove_table(&genpd->dev);
+ }
+err_del:
+ device_del(&genpd->dev);
+ return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
@@ -2591,15 +2729,30 @@ int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data)
{
struct generic_pm_domain *genpd;
+ struct fwnode_handle *fwnode;
+ struct device *dev;
unsigned int i;
int ret = -EINVAL;
+ bool sync_state = false;
if (!np || !data)
return -EINVAL;
+ if (!genpd_bus_registered)
+ return -ENODEV;
+
if (!data->xlate)
data->xlate = genpd_xlate_onecell;
+ fwnode = of_fwnode_handle(np);
+ dev = get_dev_from_fwnode(fwnode);
+ if (!dev)
+ sync_state = true;
+ else
+ dev_set_drv_sync_state(dev, genpd_sync_state);
+
+ put_device(dev);
+
for (i = 0; i < data->num_domains; i++) {
genpd = data->domains[i];
@@ -2610,12 +2763,23 @@ int of_genpd_add_provider_onecell(struct device_node *np,
genpd->dev.of_node = np;
+ if (sync_state && !genpd_is_no_sync_state(genpd)) {
+ genpd->sync_state = GENPD_SYNC_STATE_ONECELL;
+ device_set_node(&genpd->dev, fwnode);
+ sync_state = false;
+ }
+
+ ret = device_add(&genpd->dev);
+ if (ret)
+ goto error;
+
/* Parse genpd OPP table */
if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
if (ret) {
dev_err_probe(&genpd->dev, ret,
"Failed to add OPP table for index %d\n", i);
+ device_del(&genpd->dev);
goto error;
}
@@ -2627,7 +2791,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
WARN_ON(IS_ERR(genpd->opp_table));
}
- genpd->provider = &np->fwnode;
+ genpd->provider = fwnode;
genpd->has_provider = true;
}
@@ -2647,10 +2811,12 @@ error:
genpd->provider = NULL;
genpd->has_provider = false;
- if (!genpd_is_opp_table_fw(genpd) && genpd->set_performance_state) {
+ if (genpd->opp_table) {
dev_pm_opp_put_opp_table(genpd->opp_table);
dev_pm_opp_of_remove_table(&genpd->dev);
}
+
+ device_del(&genpd->dev);
}
return ret;
@@ -2676,18 +2842,19 @@ void of_genpd_del_provider(struct device_node *np)
* so that the PM domain can be safely removed.
*/
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
- if (gpd->provider == &np->fwnode) {
+ if (gpd->provider == of_fwnode_handle(np)) {
gpd->has_provider = false;
- if (genpd_is_opp_table_fw(gpd) || !gpd->set_performance_state)
- continue;
+ if (gpd->opp_table) {
+ dev_pm_opp_put_opp_table(gpd->opp_table);
+ dev_pm_opp_of_remove_table(&gpd->dev);
+ }
- dev_pm_opp_put_opp_table(gpd->opp_table);
- dev_pm_opp_of_remove_table(&gpd->dev);
+ device_del(&gpd->dev);
}
}
- fwnode_dev_initialized(&cp->node->fwnode, false);
+ fwnode_dev_initialized(of_fwnode_handle(cp->node), false);
list_del(&cp->link);
of_node_put(cp->node);
kfree(cp);
@@ -2866,7 +3033,7 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
mutex_lock(&gpd_list_lock);
list_for_each_entry_safe(gpd, tmp, &gpd_list, gpd_list_node) {
- if (gpd->provider == &np->fwnode) {
+ if (gpd->provider == of_fwnode_handle(np)) {
ret = genpd_remove(gpd);
genpd = ret ? ERR_PTR(ret) : gpd;
break;
@@ -3129,6 +3296,9 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
if (num_domains < 0 || index >= num_domains)
return NULL;
+ if (!genpd_bus_registered)
+ return ERR_PTR(-ENODEV);
+
/* Allocate and register device on the genpd bus. */
virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
if (!virt_dev)
@@ -3219,7 +3389,7 @@ static int genpd_parse_state(struct genpd_power_state *genpd_state,
genpd_state->power_on_latency_ns = 1000LL * exit_latency;
genpd_state->power_off_latency_ns = 1000LL * entry_latency;
- genpd_state->fwnode = &state_node->fwnode;
+ genpd_state->fwnode = of_fwnode_handle(state_node);
return 0;
}
@@ -3305,9 +3475,103 @@ int of_genpd_parse_idle_states(struct device_node *dn,
}
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
+/**
+ * of_genpd_sync_state() - A common sync_state function for genpd providers
+ * @np: The device node the genpd provider is associated with.
+ *
+ * The @np that corresponds to a genpd provider may provide one or multiple
+ * genpds. This function makes use @np to find the genpds that belongs to the
+ * provider. For each genpd we try a power-off.
+ */
+void of_genpd_sync_state(struct device_node *np)
+{
+ struct generic_pm_domain *genpd;
+
+ if (!np)
+ return;
+
+ mutex_lock(&gpd_list_lock);
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
+ if (genpd->provider == of_fwnode_handle(np)) {
+ genpd_lock(genpd);
+ genpd->stay_on = false;
+ genpd_power_off(genpd, false, 0);
+ genpd_unlock(genpd);
+ }
+ }
+ mutex_unlock(&gpd_list_lock);
+}
+EXPORT_SYMBOL_GPL(of_genpd_sync_state);
+
+static int genpd_provider_probe(struct device *dev)
+{
+ return 0;
+}
+
+static void genpd_provider_sync_state(struct device *dev)
+{
+ struct generic_pm_domain *genpd = container_of(dev, struct generic_pm_domain, dev);
+
+ switch (genpd->sync_state) {
+ case GENPD_SYNC_STATE_OFF:
+ break;
+
+ case GENPD_SYNC_STATE_ONECELL:
+ of_genpd_sync_state(dev->of_node);
+ break;
+
+ case GENPD_SYNC_STATE_SIMPLE:
+ genpd_lock(genpd);
+ genpd->stay_on = false;
+ genpd_power_off(genpd, false, 0);
+ genpd_unlock(genpd);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static struct device_driver genpd_provider_drv = {
+ .name = "genpd_provider",
+ .bus = &genpd_provider_bus_type,
+ .probe = genpd_provider_probe,
+ .sync_state = genpd_provider_sync_state,
+ .suppress_bind_attrs = true,
+};
+
static int __init genpd_bus_init(void)
{
- return bus_register(&genpd_bus_type);
+ int ret;
+
+ ret = device_register(&genpd_provider_bus);
+ if (ret) {
+ put_device(&genpd_provider_bus);
+ return ret;
+ }
+
+ ret = bus_register(&genpd_provider_bus_type);
+ if (ret)
+ goto err_dev;
+
+ ret = bus_register(&genpd_bus_type);
+ if (ret)
+ goto err_prov_bus;
+
+ ret = driver_register(&genpd_provider_drv);
+ if (ret)
+ goto err_bus;
+
+ genpd_bus_registered = true;
+ return 0;
+
+err_bus:
+ bus_unregister(&genpd_bus_type);
+err_prov_bus:
+ bus_unregister(&genpd_provider_bus_type);
+err_dev:
+ device_unregister(&genpd_provider_bus);
+ return ret;
}
core_initcall(genpd_bus_init);
@@ -3492,7 +3756,7 @@ static int idle_states_show(struct seq_file *s, void *data)
if (ret)
return -ERESTARTSYS;
- seq_puts(s, "State Time Spent(ms) Usage Rejected\n");
+ seq_puts(s, "State Time Spent(ms) Usage Rejected Above Below\n");
for (i = 0; i < genpd->state_count; i++) {
struct genpd_power_state *state = &genpd->states[i];
@@ -3512,9 +3776,10 @@ static int idle_states_show(struct seq_file *s, void *data)
snprintf(state_name, ARRAY_SIZE(state_name), "S%-13d", i);
do_div(idle_time, NSEC_PER_MSEC);
- seq_printf(s, "%-14s %-14llu %-14llu %llu\n",
+ seq_printf(s, "%-14s %-14llu %-10llu %-10llu %-10llu %llu\n",
state->name ?: state_name, idle_time,
- state->usage, state->rejected);
+ state->usage, state->rejected, state->above,
+ state->below);
}
genpd_unlock(genpd);