diff options
author | Jon Hunter <jonathanh@nvidia.com> | 2016-09-12 07:01:13 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-09-12 20:49:34 -0400 |
commit | 3fe577107ccf1974958701df710e0b07ef16db75 (patch) | |
tree | 3dd76befaccad67978dd0db1e9ba898705067c00 | |
parent | de0aa06d8b1c39df1071bfe169b3b97ca6bc01ac (diff) |
PM / Domains: Add support for removing PM domains
The genpd framework allows users to add PM domains via the pm_genpd_init()
function, however, there is no corresponding function to remove a PM
domain. For most devices this may be fine as the PM domains are never
removed, however, for devices that wish to populate the PM domains from
within a driver, having the ability to remove a PM domain if the probing
of the device fails or the driver is unloaded is necessary.
Add the function pm_genpd_remove() to remove a PM domain by referencing
it's generic_pm_domain structure. Note that the bulk of the code that
removes the PM domain is placed in a separate local function
genpd_remove() (which is called by pm_genpd_remove()). The code is
structured in this way to prepare for adding another function to remove
a PM domain by provider that will also call genpd_remove(). Note that
users of genpd_remove() must call this function with the mutex,
gpd_list_lock, held.
PM domains can only be removed if the associated provider has been
removed, they are not a parent domain to another PM domain and have no
devices associated with them.
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/domain.c | 60 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 5 |
2 files changed, 65 insertions, 0 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index d5135caa84db..7481e3e316a2 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c | |||
@@ -1358,6 +1358,66 @@ int pm_genpd_init(struct generic_pm_domain *genpd, | |||
1358 | } | 1358 | } |
1359 | EXPORT_SYMBOL_GPL(pm_genpd_init); | 1359 | EXPORT_SYMBOL_GPL(pm_genpd_init); |
1360 | 1360 | ||
1361 | static int genpd_remove(struct generic_pm_domain *genpd) | ||
1362 | { | ||
1363 | struct gpd_link *l, *link; | ||
1364 | |||
1365 | if (IS_ERR_OR_NULL(genpd)) | ||
1366 | return -EINVAL; | ||
1367 | |||
1368 | mutex_lock(&genpd->lock); | ||
1369 | |||
1370 | if (genpd->has_provider) { | ||
1371 | mutex_unlock(&genpd->lock); | ||
1372 | pr_err("Provider present, unable to remove %s\n", genpd->name); | ||
1373 | return -EBUSY; | ||
1374 | } | ||
1375 | |||
1376 | if (!list_empty(&genpd->master_links) || genpd->device_count) { | ||
1377 | mutex_unlock(&genpd->lock); | ||
1378 | pr_err("%s: unable to remove %s\n", __func__, genpd->name); | ||
1379 | return -EBUSY; | ||
1380 | } | ||
1381 | |||
1382 | list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { | ||
1383 | list_del(&link->master_node); | ||
1384 | list_del(&link->slave_node); | ||
1385 | kfree(link); | ||
1386 | } | ||
1387 | |||
1388 | list_del(&genpd->gpd_list_node); | ||
1389 | mutex_unlock(&genpd->lock); | ||
1390 | cancel_work_sync(&genpd->power_off_work); | ||
1391 | pr_debug("%s: removed %s\n", __func__, genpd->name); | ||
1392 | |||
1393 | return 0; | ||
1394 | } | ||
1395 | |||
1396 | /** | ||
1397 | * pm_genpd_remove - Remove a generic I/O PM domain | ||
1398 | * @genpd: Pointer to PM domain that is to be removed. | ||
1399 | * | ||
1400 | * To remove the PM domain, this function: | ||
1401 | * - Removes the PM domain as a subdomain to any parent domains, | ||
1402 | * if it was added. | ||
1403 | * - Removes the PM domain from the list of registered PM domains. | ||
1404 | * | ||
1405 | * The PM domain will only be removed, if the associated provider has | ||
1406 | * been removed, it is not a parent to any other PM domain and has no | ||
1407 | * devices associated with it. | ||
1408 | */ | ||
1409 | int pm_genpd_remove(struct generic_pm_domain *genpd) | ||
1410 | { | ||
1411 | int ret; | ||
1412 | |||
1413 | mutex_lock(&gpd_list_lock); | ||
1414 | ret = genpd_remove(genpd); | ||
1415 | mutex_unlock(&gpd_list_lock); | ||
1416 | |||
1417 | return ret; | ||
1418 | } | ||
1419 | EXPORT_SYMBOL_GPL(pm_genpd_remove); | ||
1420 | |||
1361 | #ifdef CONFIG_PM_GENERIC_DOMAINS_OF | 1421 | #ifdef CONFIG_PM_GENERIC_DOMAINS_OF |
1362 | 1422 | ||
1363 | typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, | 1423 | typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, |
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 554f8915c691..85f7d53a9827 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h | |||
@@ -130,6 +130,7 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, | |||
130 | struct generic_pm_domain *target); | 130 | struct generic_pm_domain *target); |
131 | extern int pm_genpd_init(struct generic_pm_domain *genpd, | 131 | extern int pm_genpd_init(struct generic_pm_domain *genpd, |
132 | struct dev_power_governor *gov, bool is_off); | 132 | struct dev_power_governor *gov, bool is_off); |
133 | extern int pm_genpd_remove(struct generic_pm_domain *genpd); | ||
133 | 134 | ||
134 | extern struct dev_power_governor simple_qos_governor; | 135 | extern struct dev_power_governor simple_qos_governor; |
135 | extern struct dev_power_governor pm_domain_always_on_gov; | 136 | extern struct dev_power_governor pm_domain_always_on_gov; |
@@ -165,6 +166,10 @@ static inline int pm_genpd_init(struct generic_pm_domain *genpd, | |||
165 | { | 166 | { |
166 | return -ENOSYS; | 167 | return -ENOSYS; |
167 | } | 168 | } |
169 | static inline int pm_genpd_remove(struct generic_pm_domain *genpd) | ||
170 | { | ||
171 | return -ENOTSUPP; | ||
172 | } | ||
168 | #endif | 173 | #endif |
169 | 174 | ||
170 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, | 175 | static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, |