@@ -204,6 +204,7 @@ static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
ret = !!atomic_dec_and_test(&genpd->sd_count);
+ atomic_dec(&genpd->usage_count);
return ret;
}
@@ -211,6 +212,7 @@ static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
{
atomic_inc(&genpd->sd_count);
+ atomic_inc(&genpd->usage_count);
smp_mb__after_atomic();
}
@@ -583,6 +585,9 @@ static int pm_genpd_runtime_suspend(struct device *dev)
return ret;
}
+ if (!atomic_dec_and_test(&genpd->usage_count))
+ return 0;
+
/*
* If power.irq_safe is set, this routine may be run with
* IRQ disabled, so suspend only if the power domain is
@@ -620,6 +625,9 @@ static int pm_genpd_runtime_resume(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
+ if (atomic_inc_return(&genpd->usage_count) > 1)
+ goto out;
+
/*
* As we dont power off a non IRQ safe domain, which holds
* an IRQ safe device, we dont need to restore power to it.
@@ -1400,9 +1408,11 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (ret)
genpd_free_dev_data(dev, gpd_data);
- else
+ else {
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
-
+ atomic_inc(&genpd->usage_count);
+ printk("Add device %d\n", atomic_read(&genpd->usage_count));
+ }
return ret;
}
@@ -1457,6 +1467,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
genpd_unlock(genpd);
+ atomic_dec(&genpd->usage_count);
genpd_free_dev_data(dev, gpd_data);
return 0;
@@ -1799,6 +1810,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0);
+ atomic_set(&genpd->usage_count, 0);
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
genpd->device_count = 0;
genpd->max_off_time_ns = -1;
@@ -55,6 +55,7 @@ struct generic_pm_domain {
struct work_struct power_off_work;
const char *name;
atomic_t sd_count; /* Number of subdomains with power "on" */
+ atomic_t usage_count; /* Number of active users of domain "on" */
enum gpd_status status; /* Current state of the domain */
unsigned int device_count; /* Number of devices */
unsigned int suspended_count; /* System suspend device counter */
Locking a domain to check if the domain can be powered on/off is an expensive operations and could hold up multiple devices executing runtime PM at the same time. In the case where there is atleast one active device, the domain would remain active. This can be easily checked by using an atomic counter to record domain usage. This restricts locking only to the last suspending or the first resuming device. Signed-off-by: Lina Iyer <lina.iyer@linaro.org> --- drivers/base/power/domain.c | 16 ++++++++++++++-- include/linux/pm_domain.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-)