]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
pmdomain: core: Add common ->sync_state() support for genpd providers
authorUlf Hansson <ulf.hansson@linaro.org>
Tue, 1 Jul 2025 11:47:20 +0000 (13:47 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Wed, 9 Jul 2025 11:29:41 +0000 (13:29 +0200)
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.org
drivers/pmdomain/core.c
include/linux/pm_domain.h

index 0a6593a1b1c8d6a9d4f50555c17e7d1d4a2a89ae..ca47f91b9e91f59d78b280c8c4c31d5ccac6cca9 100644 (file)
@@ -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 = {
index 9329554b9c4af15106d8c00de8805012989e8323..d68e07dadc99c989dee3a7537a4cb48f7c154d34 100644 (file)
@@ -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 */