]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
pmdomain: core: Leave powered-on genpds on until late_initcall_sync
authorUlf Hansson <ulf.hansson@linaro.org>
Tue, 1 Jul 2025 11:47:23 +0000 (13:47 +0200)
committerUlf Hansson <ulf.hansson@linaro.org>
Wed, 9 Jul 2025 11:29:53 +0000 (13:29 +0200)
Powering-off a genpd that was on during boot, before all of its consumer
devices have been probed, is certainly prone to problems.

As a step to improve this situation, let's prevent these genpds from being
powered-off until genpd_power_off_unused() gets called, which is a
late_initcall_sync().

Note that, this still doesn't guarantee that all the consumer devices has
been probed before we allow to power-off the genpds. Yet, this should be a
step in the right direction.

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-22-ulf.hansson@linaro.org
drivers/pmdomain/core.c
include/linux/pm_domain.h

index 5cef6de60c72a18526dc7ecd5c455b7a0482409a..18951ed6295d8e8fb0f453919dde71bd69ca2fba 100644 (file)
@@ -931,11 +931,12 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_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_status_on(genpd) || genpd->prepared_count > 0 ||
            genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd) ||
-           atomic_read(&genpd->sd_count) > 0)
+           genpd->stay_on || atomic_read(&genpd->sd_count) > 0)
                return;
 
        /*
@@ -1346,8 +1347,12 @@ 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);
 
@@ -2352,6 +2357,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->stay_on = !is_off;
        genpd->sync_state = GENPD_SYNC_STATE_OFF;
        genpd->device_count = 0;
        genpd->provider = NULL;
index d68e07dadc99c989dee3a7537a4cb48f7c154d34..99556589f45e809961c0767c21486b002334d983 100644 (file)
@@ -199,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 */
+       bool stay_on;                   /* Stay powered-on during boot. */
        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);