Skip to content

Commit

Permalink
PM / Domains: Add device domain data reference counter
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
rjwysocki committed Jul 5, 2012
1 parent cbc9ef0 commit 1d5fcfe
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 26 deletions.
83 changes: 57 additions & 26 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,

pdd = dev->power.subsys_data ?
dev->power.subsys_data->domain_data : NULL;
if (pdd) {
if (pdd && pdd->dev) {
to_gpd_data(pdd)->td.constraint_changed = true;
genpd = dev_to_genpd(dev);
} else {
Expand Down Expand Up @@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev)

#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.
* @genpd: PM domain to add the device to.
Expand All @@ -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,
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;
int ret = 0;

Expand All @@ -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))
return -EINVAL;

gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
if (!gpd_data)
gpd_data_new = __pm_genpd_alloc_dev_data(dev);
if (!gpd_data_new)
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);

if (genpd->prepared_count > 0) {
Expand All @@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
goto out;
}

ret = dev_pm_get_subsys_data(dev);
if (ret)
goto out;

genpd->device_count++;
genpd->max_off_time_changed = true;

dev_pm_get_subsys_data(dev);

mutex_lock(&gpd_data->lock);
spin_lock_irq(&dev->power.lock);

dev->pm_domain = &genpd->domain;
dev->power.subsys_data->domain_data = &gpd_data->base;
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;
if (dev->power.subsys_data->domain_data) {
gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
} else {
gpd_data = gpd_data_new;
dev->power.subsys_data->domain_data = &gpd_data->base;
}
gpd_data->refcount++;
if (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.effective_constraint_ns = -1;
spin_unlock_irq(&dev->power.lock);
mutex_unlock(&gpd_data->lock);

genpd_release_lock(genpd);

return 0;

out:
genpd_release_lock(genpd);

dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data);
if (gpd_data != gpd_data_new)
__pm_genpd_free_dev_data(dev, gpd_data_new);

return ret;
}

Expand Down Expand Up @@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
{
struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd;
bool remove = false;
int ret = 0;

dev_dbg(dev, "%s()\n", __func__);
Expand All @@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
genpd->max_off_time_changed = true;

spin_lock_irq(&dev->power.lock);

dev->pm_domain = NULL;
pdd = dev->power.subsys_data->domain_data;
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);

gpd_data = to_gpd_data(pdd);
mutex_lock(&gpd_data->lock);
pdd->dev = NULL;
mutex_unlock(&gpd_data->lock);

genpd_release_lock(genpd);

dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
kfree(gpd_data);
dev_pm_put_subsys_data(dev);
if (remove)
__pm_genpd_free_dev_data(dev, gpd_data);

return 0;

out:
Expand Down
1 change: 1 addition & 0 deletions include/linux/pm_domain.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ struct generic_pm_domain_data {
struct gpd_timing_data td;
struct notifier_block nb;
struct mutex lock;
unsigned int refcount;
bool need_restore;
bool always_on;
};
Expand Down

0 comments on commit 1d5fcfe

Please sign in to comment.