pmdomain: core: Add common ->sync_state() support for genpd providers
If the genpd provider's fwnode doesn't have an associated struct device with it, we can make use of the generic genpd->dev and it corresponding driver internally in genpd to manage ->sync_state(). More precisely, while adding a genpd OF provider let's check if the fwnode has a device and if not, make the preparation to handle ->sync_state() internally through the genpd_provider_driver and the genpd_provider_bus. Note that, genpd providers may opt out from this behaviour by setting the GENPD_FLAG_NO_SYNC_STATE config options for the genpds in question. Suggested-by: Saravana Kannan <saravanak@google.com> Tested-by: Hiago De Franco <hiago.franco@toradex.com> # Colibri iMX8X Tested-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> # TI AM62A,Xilinx ZynqMP ZCU106 Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Link: https://lore.kernel.org/r/20250701114733.636510-19-ulf.hansson@linaro.orgpull/1309/head
parent
9a4681a485
commit
3b7b8acacf
|
|
@ -186,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)
|
||||
|
|
@ -2351,6 +2352,7 @@ 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->sync_state = GENPD_SYNC_STATE_OFF;
|
||||
genpd->device_count = 0;
|
||||
genpd->provider = NULL;
|
||||
genpd->device_id = -ENXIO;
|
||||
|
|
@ -2606,6 +2608,8 @@ 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)
|
||||
|
|
@ -2619,6 +2623,15 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
put_device(dev);
|
||||
|
||||
ret = device_add(&genpd->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2643,7 +2656,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
|||
if (ret)
|
||||
goto err_opp;
|
||||
|
||||
genpd->provider = &np->fwnode;
|
||||
genpd->provider = fwnode;
|
||||
genpd->has_provider = true;
|
||||
|
||||
return 0;
|
||||
|
|
@ -2668,8 +2681,11 @@ 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;
|
||||
|
|
@ -2680,6 +2696,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
|||
if (!data->xlate)
|
||||
data->xlate = genpd_xlate_onecell;
|
||||
|
||||
fwnode = of_fwnode_handle(np);
|
||||
dev = get_dev_from_fwnode(fwnode);
|
||||
if (!dev)
|
||||
sync_state = true;
|
||||
|
||||
put_device(dev);
|
||||
|
||||
for (i = 0; i < data->num_domains; i++) {
|
||||
genpd = data->domains[i];
|
||||
|
||||
|
|
@ -2690,6 +2713,12 @@ 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;
|
||||
|
|
@ -2712,7 +2741,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;
|
||||
}
|
||||
|
||||
|
|
@ -3430,6 +3459,25 @@ static int genpd_provider_probe(struct device *dev)
|
|||
|
||||
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_power_off(genpd, false, 0);
|
||||
genpd_unlock(genpd);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct device_driver genpd_provider_drv = {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,12 @@ enum genpd_notication {
|
|||
GENPD_NOTIFY_ON,
|
||||
};
|
||||
|
||||
enum genpd_sync_state {
|
||||
GENPD_SYNC_STATE_OFF = 0,
|
||||
GENPD_SYNC_STATE_SIMPLE,
|
||||
GENPD_SYNC_STATE_ONECELL,
|
||||
};
|
||||
|
||||
struct dev_power_governor {
|
||||
bool (*power_down_ok)(struct dev_pm_domain *domain);
|
||||
bool (*suspend_ok)(struct device *dev);
|
||||
|
|
@ -193,6 +199,7 @@ struct generic_pm_domain {
|
|||
unsigned int performance_state; /* Aggregated max performance state */
|
||||
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
|
||||
bool synced_poweroff; /* A consumer needs a synced poweroff */
|
||||
enum genpd_sync_state sync_state; /* How sync_state is managed. */
|
||||
int (*power_off)(struct generic_pm_domain *domain);
|
||||
int (*power_on)(struct generic_pm_domain *domain);
|
||||
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */
|
||||
|
|
|
|||
Loading…
Reference in New Issue