Skip to content

Commit 46cd666

Browse files
marcanjannau
authored andcommitted
PM: domains: Add a flag to defer power-off until all consumers probe
In some cases, power domains are active on boot and must remain turned on until all their dependent drivers probe. Examples are: - Boot-time framebuffers - Devices that run coprocessors which are handed off already running - Parent power domains with children that are also on at boot The genpd core currently powers off the genpd as soon as a single consumer device probes and goes into runtime suspend or when general probing is complete, whichever comes first. That breaks any devices which haven't probed yet. To fix this, add a GENPD_FLAG_DEFER_OFF which requests that the genpd core refuse to power down a domain if there are any consumer devices that either haven't probed yet, or whose device nodes do not exist yet (but fwlinks do). Genpd providers can set this if they expect to be critical for devices (e.g. if they are powered on at boot). It is possible for a device to be runtime suspended from its probe callback. If this is the last device to probe, this is allowable. To account for this, check whether the device whose callbacks are being invoked in the probing state, and in that case, allow 1 instead of 0 pending devices. Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent 5cdc0b4 commit 46cd666

2 files changed

Lines changed: 55 additions & 6 deletions

File tree

drivers/pmdomain/core.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define pr_fmt(fmt) "PM: " fmt
88

99
#include <linux/delay.h>
10+
#include <linux/fwnode.h>
1011
#include <linux/idr.h>
1112
#include <linux/kernel.h>
1213
#include <linux/io.h>
@@ -176,6 +177,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
176177
#define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
177178
#define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW)
178179
#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
180+
#define genpd_is_defer_off(genpd) (genpd->flags & GENPD_FLAG_DEFER_OFF)
179181

180182
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
181183
const struct generic_pm_domain *genpd)
@@ -841,6 +843,27 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
841843
queue_work(pm_wq, &genpd->power_off_work);
842844
}
843845

846+
/**
847+
* genpd_must_defer - Check whether the genpd cannot be safely powered off.
848+
* @genpd: PM domain about to be powered down.
849+
* @one_dev_probing: True if we are being called from RPM callbacks on a device that
850+
* is probing, to allow poweroff if that device is the sole remaining consumer probing.
851+
*
852+
* Returns true if the @genpd has the GENPD_FLAG_DEFER_OFF flag and there
853+
* are any consumer devices which either do not exist yet (only represented
854+
* by fwlinks) or whose drivers have not probed yet.
855+
*/
856+
static bool genpd_must_defer(struct generic_pm_domain *genpd, bool one_dev_probing)
857+
{
858+
if (genpd_is_defer_off(genpd) && genpd->has_provider) {
859+
int absent = fw_devlink_count_absent_consumers(genpd->provider);
860+
861+
if (absent > (one_dev_probing ? 1 : 0))
862+
return true;
863+
}
864+
return false;
865+
}
866+
844867
/**
845868
* genpd_power_off - Remove power from a given PM domain.
846869
* @genpd: PM domain to power down.
@@ -854,7 +877,7 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
854877
* have been powered down, remove power from @genpd.
855878
*/
856879
static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
857-
unsigned int depth)
880+
bool one_dev_probing, unsigned int depth)
858881
{
859882
struct pm_domain_data *pdd;
860883
struct gpd_link *link;
@@ -908,6 +931,14 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
908931
if (not_suspended > 1 || (not_suspended == 1 && !one_dev_on))
909932
return -EBUSY;
910933

934+
/*
935+
* Do not allow PM domain to be powered off if it is marked
936+
* as GENPD_FLAG_DEFER_OFF and there are consumer devices
937+
* which have not probed yet.
938+
*/
939+
if (genpd_must_defer(genpd, one_dev_probing))
940+
return -EBUSY;
941+
911942
if (genpd->gov && genpd->gov->power_down_ok) {
912943
if (!genpd->gov->power_down_ok(&genpd->domain))
913944
return -EAGAIN;
@@ -934,7 +965,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
934965
list_for_each_entry(link, &genpd->child_links, child_node) {
935966
genpd_sd_counter_dec(link->parent);
936967
genpd_lock_nested(link->parent, depth + 1);
937-
genpd_power_off(link->parent, false, depth + 1);
968+
genpd_power_off(link->parent, false, false, depth + 1);
938969
genpd_unlock(link->parent);
939970
}
940971

@@ -992,7 +1023,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
9921023
child_node) {
9931024
genpd_sd_counter_dec(link->parent);
9941025
genpd_lock_nested(link->parent, depth + 1);
995-
genpd_power_off(link->parent, false, depth + 1);
1026+
genpd_power_off(link->parent, false, false, depth + 1);
9961027
genpd_unlock(link->parent);
9971028
}
9981029

@@ -1059,7 +1090,7 @@ static void genpd_power_off_work_fn(struct work_struct *work)
10591090
genpd = container_of(work, struct generic_pm_domain, power_off_work);
10601091

10611092
genpd_lock(genpd);
1062-
genpd_power_off(genpd, false, 0);
1093+
genpd_power_off(genpd, false, false, 0);
10631094
genpd_unlock(genpd);
10641095
}
10651096

@@ -1124,6 +1155,7 @@ static int genpd_runtime_suspend(struct device *dev)
11241155
struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
11251156
struct gpd_timing_data *td = gpd_data->td;
11261157
bool runtime_pm = pm_runtime_enabled(dev);
1158+
bool probing = dev->links.status != DL_DEV_DRIVER_BOUND;
11271159
ktime_t time_start = 0;
11281160
s64 elapsed_ns;
11291161
int ret;
@@ -1178,7 +1210,7 @@ static int genpd_runtime_suspend(struct device *dev)
11781210
return 0;
11791211

11801212
genpd_lock(genpd);
1181-
genpd_power_off(genpd, true, 0);
1213+
genpd_power_off(genpd, true, probing, 0);
11821214
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
11831215
genpd_unlock(genpd);
11841216

@@ -1199,6 +1231,7 @@ static int genpd_runtime_resume(struct device *dev)
11991231
struct generic_pm_domain_data *gpd_data = dev_gpd_data(dev);
12001232
struct gpd_timing_data *td = gpd_data->td;
12011233
bool timed = td && pm_runtime_enabled(dev);
1234+
bool probing = dev->links.status != DL_DEV_DRIVER_BOUND;
12021235
ktime_t time_start = 0;
12031236
s64 elapsed_ns;
12041237
int ret;
@@ -1256,7 +1289,7 @@ static int genpd_runtime_resume(struct device *dev)
12561289
err_poweroff:
12571290
if (!pm_runtime_is_irq_safe(dev) || genpd_is_irq_safe(genpd)) {
12581291
genpd_lock(genpd);
1259-
genpd_power_off(genpd, true, 0);
1292+
genpd_power_off(genpd, true, probing, 0);
12601293
gpd_data->rpm_pstate = genpd_drop_performance_state(dev);
12611294
genpd_unlock(genpd);
12621295
}
@@ -1323,6 +1356,9 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
13231356
|| atomic_read(&genpd->sd_count) > 0)
13241357
return;
13251358

1359+
if (genpd_must_defer(genpd, false))
1360+
return;
1361+
13261362
/* Check that the children are in their deepest (powered-off) state. */
13271363
list_for_each_entry(link, &genpd->parent_links, parent_node) {
13281364
struct generic_pm_domain *child = link->child;
@@ -2323,6 +2359,12 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
23232359
return -EINVAL;
23242360
}
23252361

2362+
/* Deferred-off power domains should be powered on at initialization. */
2363+
if (genpd_is_defer_off(genpd) && !genpd_status_on(genpd)) {
2364+
pr_warn("deferred-off PM domain %s is not on at init\n", genpd->name);
2365+
genpd->flags &= ~GENPD_FLAG_DEFER_OFF;
2366+
}
2367+
23262368
/* Multiple states but no governor doesn't make sense. */
23272369
if (!gov && genpd->state_count > 1)
23282370
pr_warn("%s: no governor for states\n", genpd->name);

include/linux/pm_domain.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ struct dev_pm_domain_list {
104104
* GENPD_FLAG_DEV_NAME_FW: Instructs genpd to generate an unique device name
105105
* using ida. It is used by genpd providers which
106106
* get their genpd-names directly from FW.
107+
* GENPD_FLAG_DEFER_OFF: Defer powerdown if there are any consumer
108+
* device fwlinks indicating that some consumer
109+
* devices have not yet probed. This is useful
110+
* for power domains which are active at boot and
111+
* must not be shut down until all consumers
112+
* complete their probe sequence.
107113
*/
108114
#define GENPD_FLAG_PM_CLK (1U << 0)
109115
#define GENPD_FLAG_IRQ_SAFE (1U << 1)
@@ -114,6 +120,7 @@ struct dev_pm_domain_list {
114120
#define GENPD_FLAG_MIN_RESIDENCY (1U << 6)
115121
#define GENPD_FLAG_OPP_TABLE_FW (1U << 7)
116122
#define GENPD_FLAG_DEV_NAME_FW (1U << 8)
123+
#define GENPD_FLAG_DEFER_OFF (1U << 9)
117124

118125
enum gpd_status {
119126
GENPD_STATE_ON = 0, /* PM domain is on */

0 commit comments

Comments
 (0)