PM / Domains: Add device domain data reference counter
Add a mechanism for counting references to the struct generic_pm_domain_data object pointed to by dev->power.subsys_data->domain_data if the device in question belongs to a generic PM domain. This change is necessary for a subsequent patch making it possible to allocate that object from within pm_genpd_add_callbacks(), so that drivers can attach their PM domain device callbacks to devices before those devices are added to PM domains. This patch has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
This commit is contained in:
parent
cbc9ef0287
commit
1d5fcfec22
2 changed files with 58 additions and 26 deletions
|
@ -297,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
|
||||||
|
|
||||||
pdd = dev->power.subsys_data ?
|
pdd = dev->power.subsys_data ?
|
||||||
dev->power.subsys_data->domain_data : NULL;
|
dev->power.subsys_data->domain_data : NULL;
|
||||||
if (pdd) {
|
if (pdd && pdd->dev) {
|
||||||
to_gpd_data(pdd)->td.constraint_changed = true;
|
to_gpd_data(pdd)->td.constraint_changed = true;
|
||||||
genpd = dev_to_genpd(dev);
|
genpd = dev_to_genpd(dev);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev)
|
||||||
|
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev)
|
||||||
|
{
|
||||||
|
struct generic_pm_domain_data *gpd_data;
|
||||||
|
|
||||||
|
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
|
||||||
|
if (!gpd_data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mutex_init(&gpd_data->lock);
|
||||||
|
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
|
||||||
|
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
|
||||||
|
return gpd_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __pm_genpd_free_dev_data(struct device *dev,
|
||||||
|
struct generic_pm_domain_data *gpd_data)
|
||||||
|
{
|
||||||
|
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
|
||||||
|
kfree(gpd_data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __pm_genpd_add_device - Add a device to an I/O PM domain.
|
* __pm_genpd_add_device - Add a device to an I/O PM domain.
|
||||||
* @genpd: PM domain to add the device to.
|
* @genpd: PM domain to add the device to.
|
||||||
|
@ -1275,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev)
|
||||||
int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||||
struct gpd_timing_data *td)
|
struct gpd_timing_data *td)
|
||||||
{
|
{
|
||||||
struct generic_pm_domain_data *gpd_data;
|
struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL;
|
||||||
struct pm_domain_data *pdd;
|
struct pm_domain_data *pdd;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -1284,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||||
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
|
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
|
gpd_data_new = __pm_genpd_alloc_dev_data(dev);
|
||||||
if (!gpd_data)
|
if (!gpd_data_new)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
mutex_init(&gpd_data->lock);
|
|
||||||
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
|
|
||||||
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
|
|
||||||
|
|
||||||
genpd_acquire_lock(genpd);
|
genpd_acquire_lock(genpd);
|
||||||
|
|
||||||
if (genpd->prepared_count > 0) {
|
if (genpd->prepared_count > 0) {
|
||||||
|
@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = dev_pm_get_subsys_data(dev);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
genpd->device_count++;
|
genpd->device_count++;
|
||||||
genpd->max_off_time_changed = true;
|
genpd->max_off_time_changed = true;
|
||||||
|
|
||||||
dev_pm_get_subsys_data(dev);
|
|
||||||
|
|
||||||
mutex_lock(&gpd_data->lock);
|
|
||||||
spin_lock_irq(&dev->power.lock);
|
spin_lock_irq(&dev->power.lock);
|
||||||
|
|
||||||
dev->pm_domain = &genpd->domain;
|
dev->pm_domain = &genpd->domain;
|
||||||
dev->power.subsys_data->domain_data = &gpd_data->base;
|
if (dev->power.subsys_data->domain_data) {
|
||||||
gpd_data->base.dev = dev;
|
gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
|
||||||
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
|
} else {
|
||||||
gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
|
gpd_data = gpd_data_new;
|
||||||
|
dev->power.subsys_data->domain_data = &gpd_data->base;
|
||||||
|
}
|
||||||
|
gpd_data->refcount++;
|
||||||
if (td)
|
if (td)
|
||||||
gpd_data->td = *td;
|
gpd_data->td = *td;
|
||||||
|
|
||||||
|
spin_unlock_irq(&dev->power.lock);
|
||||||
|
|
||||||
|
mutex_lock(&gpd_data->lock);
|
||||||
|
gpd_data->base.dev = dev;
|
||||||
|
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
|
||||||
|
gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
|
||||||
gpd_data->td.constraint_changed = true;
|
gpd_data->td.constraint_changed = true;
|
||||||
gpd_data->td.effective_constraint_ns = -1;
|
gpd_data->td.effective_constraint_ns = -1;
|
||||||
spin_unlock_irq(&dev->power.lock);
|
|
||||||
mutex_unlock(&gpd_data->lock);
|
mutex_unlock(&gpd_data->lock);
|
||||||
|
|
||||||
genpd_release_lock(genpd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
genpd_release_lock(genpd);
|
genpd_release_lock(genpd);
|
||||||
|
|
||||||
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
|
if (gpd_data != gpd_data_new)
|
||||||
kfree(gpd_data);
|
__pm_genpd_free_dev_data(dev, gpd_data_new);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|
||||||
{
|
{
|
||||||
struct generic_pm_domain_data *gpd_data;
|
struct generic_pm_domain_data *gpd_data;
|
||||||
struct pm_domain_data *pdd;
|
struct pm_domain_data *pdd;
|
||||||
|
bool remove = false;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
dev_dbg(dev, "%s()\n", __func__);
|
dev_dbg(dev, "%s()\n", __func__);
|
||||||
|
@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|
||||||
genpd->max_off_time_changed = true;
|
genpd->max_off_time_changed = true;
|
||||||
|
|
||||||
spin_lock_irq(&dev->power.lock);
|
spin_lock_irq(&dev->power.lock);
|
||||||
|
|
||||||
dev->pm_domain = NULL;
|
dev->pm_domain = NULL;
|
||||||
pdd = dev->power.subsys_data->domain_data;
|
pdd = dev->power.subsys_data->domain_data;
|
||||||
list_del_init(&pdd->list_node);
|
list_del_init(&pdd->list_node);
|
||||||
dev->power.subsys_data->domain_data = NULL;
|
gpd_data = to_gpd_data(pdd);
|
||||||
|
if (--gpd_data->refcount == 0) {
|
||||||
|
dev->power.subsys_data->domain_data = NULL;
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
spin_unlock_irq(&dev->power.lock);
|
spin_unlock_irq(&dev->power.lock);
|
||||||
|
|
||||||
gpd_data = to_gpd_data(pdd);
|
|
||||||
mutex_lock(&gpd_data->lock);
|
mutex_lock(&gpd_data->lock);
|
||||||
pdd->dev = NULL;
|
pdd->dev = NULL;
|
||||||
mutex_unlock(&gpd_data->lock);
|
mutex_unlock(&gpd_data->lock);
|
||||||
|
|
||||||
genpd_release_lock(genpd);
|
genpd_release_lock(genpd);
|
||||||
|
|
||||||
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
|
|
||||||
kfree(gpd_data);
|
|
||||||
dev_pm_put_subsys_data(dev);
|
dev_pm_put_subsys_data(dev);
|
||||||
|
if (remove)
|
||||||
|
__pm_genpd_free_dev_data(dev, gpd_data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -112,6 +112,7 @@ struct generic_pm_domain_data {
|
||||||
struct gpd_timing_data td;
|
struct gpd_timing_data td;
|
||||||
struct notifier_block nb;
|
struct notifier_block nb;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
unsigned int refcount;
|
||||||
bool need_restore;
|
bool need_restore;
|
||||||
bool always_on;
|
bool always_on;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue