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_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_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_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,
|
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
|
||||||
const struct generic_pm_domain *genpd)
|
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);
|
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
|
||||||
atomic_set(&genpd->sd_count, 0);
|
atomic_set(&genpd->sd_count, 0);
|
||||||
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
|
genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
|
||||||
|
genpd->sync_state = GENPD_SYNC_STATE_OFF;
|
||||||
genpd->device_count = 0;
|
genpd->device_count = 0;
|
||||||
genpd->provider = NULL;
|
genpd->provider = NULL;
|
||||||
genpd->device_id = -ENXIO;
|
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,
|
int of_genpd_add_provider_simple(struct device_node *np,
|
||||||
struct generic_pm_domain *genpd)
|
struct generic_pm_domain *genpd)
|
||||||
{
|
{
|
||||||
|
struct fwnode_handle *fwnode;
|
||||||
|
struct device *dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!np || !genpd)
|
if (!np || !genpd)
|
||||||
|
|
@ -2619,6 +2623,15 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
||||||
|
|
||||||
genpd->dev.of_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);
|
ret = device_add(&genpd->dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -2643,7 +2656,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_opp;
|
goto err_opp;
|
||||||
|
|
||||||
genpd->provider = &np->fwnode;
|
genpd->provider = fwnode;
|
||||||
genpd->has_provider = true;
|
genpd->has_provider = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -2668,8 +2681,11 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
||||||
struct genpd_onecell_data *data)
|
struct genpd_onecell_data *data)
|
||||||
{
|
{
|
||||||
struct generic_pm_domain *genpd;
|
struct generic_pm_domain *genpd;
|
||||||
|
struct fwnode_handle *fwnode;
|
||||||
|
struct device *dev;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
bool sync_state = false;
|
||||||
|
|
||||||
if (!np || !data)
|
if (!np || !data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -2680,6 +2696,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
||||||
if (!data->xlate)
|
if (!data->xlate)
|
||||||
data->xlate = genpd_xlate_onecell;
|
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++) {
|
for (i = 0; i < data->num_domains; i++) {
|
||||||
genpd = data->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;
|
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);
|
ret = device_add(&genpd->dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -2712,7 +2741,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
||||||
WARN_ON(IS_ERR(genpd->opp_table));
|
WARN_ON(IS_ERR(genpd->opp_table));
|
||||||
}
|
}
|
||||||
|
|
||||||
genpd->provider = &np->fwnode;
|
genpd->provider = fwnode;
|
||||||
genpd->has_provider = true;
|
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)
|
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 = {
|
static struct device_driver genpd_provider_drv = {
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,12 @@ enum genpd_notication {
|
||||||
GENPD_NOTIFY_ON,
|
GENPD_NOTIFY_ON,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum genpd_sync_state {
|
||||||
|
GENPD_SYNC_STATE_OFF = 0,
|
||||||
|
GENPD_SYNC_STATE_SIMPLE,
|
||||||
|
GENPD_SYNC_STATE_ONECELL,
|
||||||
|
};
|
||||||
|
|
||||||
struct dev_power_governor {
|
struct dev_power_governor {
|
||||||
bool (*power_down_ok)(struct dev_pm_domain *domain);
|
bool (*power_down_ok)(struct dev_pm_domain *domain);
|
||||||
bool (*suspend_ok)(struct device *dev);
|
bool (*suspend_ok)(struct device *dev);
|
||||||
|
|
@ -193,6 +199,7 @@ struct generic_pm_domain {
|
||||||
unsigned int performance_state; /* Aggregated max performance state */
|
unsigned int performance_state; /* Aggregated max performance state */
|
||||||
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
|
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
|
||||||
bool synced_poweroff; /* A consumer needs a synced poweroff */
|
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_off)(struct generic_pm_domain *domain);
|
||||||
int (*power_on)(struct generic_pm_domain *domain);
|
int (*power_on)(struct generic_pm_domain *domain);
|
||||||
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */
|
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue