[PATCH 08/10] PM / Domains: Add support for removing PM domains
From: jonathanh@nvidia.com (Jon Hunter)
Date: 2016-09-09 15:17:33
Also in:
linux-pm, linux-tegra
Subsystem:
driver core, kobjects, debugfs and sysfs, generic pm domains, hibernation (aka software suspend, aka swsusp), power management core, suspend to ram, the rest · Maintainers:
Greg Kroah-Hartman, "Rafael J. Wysocki", Danilo Krummrich, Ulf Hansson, Linus Torvalds
On 09/09/16 14:54, Jon Hunter wrote:
On 08/09/16 12:49, Ulf Hansson wrote:quoted
On 16 August 2016 at 11:49, Jon Hunter [off-list ref] wrote:quoted
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. PM domains can only be removed if they are not a parent domain to another PM domain and have no devices associated with them.I think we should also check if the there's is a provider registered for the genpd, as it should also prevent the genpd from being removed. Right?Yes I would agree. I had thought that after patch #4 of this series that only the provider itself would be able to call this. However, we should probably still verify that the provider has correctly remove itself.
So now I have the following. I am still not 100% happy. I cannot clear the ->provider when calling of_genpd_del_provider() and so I cannot use this to verify if the provider is present and so I need to check the list of providers and it gets a bit messy. I have been wracking my brains to find a better alternative (including a single function to remove the provider and domains at once but there are issues with that as well). I think that long term it may make sense to reference the providers exclusively by the fwnode_handle and make the list of provider non-DT specific. I could do it now, but it would increase the series. Cheers Jon --- drivers/base/power/domain.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 5 +++ 2 files changed, 94 insertions(+)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 82f87038f108..57c64fc1a164 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c@@ -39,6 +39,8 @@ static LIST_HEAD(gpd_list); static DEFINE_MUTEX(gpd_list_lock); +static bool genpd_provider_present(struct device_node *np); + /* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer,
@@ -1356,6 +1358,68 @@ int pm_genpd_init(struct generic_pm_domain *genpd, } EXPORT_SYMBOL_GPL(pm_genpd_init); +static int genpd_remove(struct generic_pm_domain *genpd) +{ + struct gpd_link *l, *link; + + if (IS_ERR_OR_NULL(genpd)) + return -EINVAL; + + mutex_lock(&genpd->lock); + + if (is_of_node(genpd->provider)) { + if (genpd_provider_present(to_of_node(genpd->provider))) { + mutex_unlock(&genpd->lock); + pr_err("Provider present, unable to remove %s\n", + genpd->name); + return -EBUSY; + } + } + + if (!list_empty(&genpd->master_links) || genpd->device_count) { + mutex_unlock(&genpd->lock); + pr_err("%s: unable to remove %s\n", __func__, genpd->name); + return -EBUSY; + } + + list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) { + list_del(&link->master_node); + list_del(&link->slave_node); + kfree(link); + } + + list_del(&genpd->gpd_list_node); + mutex_unlock(&genpd->lock); + cancel_work_sync(&genpd->power_off_work); + pr_debug("%s: removed %s\n", __func__, genpd->name); + + return 0; +} + +/** + * pm_genpd_remove - Remove a generic I/O PM domain + * @genpd: Pointer to PM domain that is to be removed. + * + * To remove the PM domain, this function: + * - Removes the PM domain as a subdomain to any parent domains, + * if it was added. + * - Removes the PM domain from the list of registered PM domains. + * + * The PM domain will only be removed, if it is not a parent to any + * other PM domain and has no devices associated with it. + */ +int pm_genpd_remove(struct generic_pm_domain *genpd) +{ + int ret; + + mutex_lock(&gpd_list_lock); + ret = genpd_remove(genpd); + mutex_unlock(&gpd_list_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pm_genpd_remove); + #ifdef CONFIG_PM_GENERIC_DOMAINS_OF typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
@@ -1563,6 +1627,26 @@ void of_genpd_del_provider(struct device_node *np) EXPORT_SYMBOL_GPL(of_genpd_del_provider); /** + * genpd_provider_present() - Verify if a PM domain provider is present + * @np: Device node pointer associated with the PM domain provider + */ +static bool genpd_provider_present(struct device_node *np) +{ + struct of_genpd_provider *cp; + + mutex_lock(&of_genpd_mutex); + list_for_each_entry(cp, &of_genpd_providers, link) { + if (cp->node == np) { + mutex_unlock(&of_genpd_mutex); + return true; + } + } + mutex_unlock(&of_genpd_mutex); + + return false; +} + +/** * genpd_get_from_provider() - Look-up PM domain * @genpdspec: OF phandle args to use for look-up *
@@ -1798,6 +1882,11 @@ out: return ret ? -EPROBE_DEFER : 0; } EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); +#else +static bool genpd_provider_present(struct device_node *np) +{ + return false; +} #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index e71764ac7248..4aa285e44eb0 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h@@ -129,6 +129,7 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, struct generic_pm_domain *target); extern int pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); +extern int pm_genpd_remove(struct generic_pm_domain *genpd); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov;
@@ -164,6 +165,10 @@ static inline int pm_genpd_init(struct generic_pm_domain *genpd, { return -ENOSYS; } +static inline int pm_genpd_remove(struct generic_pm_domain *genpd) +{ + return -ENOTSUPP; +} #endif static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
--
2.1.4
--
nvpublic