From ec69572b3f0daf1f26b3e563d56e487d515a3cd1 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:05 +0100 Subject: PM / Domains: Add new helper functions for device-tree Ideally, if we are returning a reference to a PM domain via a call to of_genpd_get_from_provider(), then we should keep track of such references via a reference count. The reference count could then be used to determine if a PM domain can be safely removed. Alternatively, it is possible to avoid such external references by providing APIs to access the PM domain and hence, eliminate any calls to of_genpd_get_from_provider(). Add new helper functions for adding a device and a subdomain to a PM domain when using device-tree, so that external calls to of_genpd_get_from_provider() can be removed. Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- include/linux/pm_domain.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 31fec858088c..e1964a242389 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -208,6 +208,10 @@ struct generic_pm_domain *__of_genpd_xlate_simple( struct generic_pm_domain *__of_genpd_xlate_onecell( struct of_phandle_args *genpdspec, void *data); +extern int of_genpd_add_device(struct of_phandle_args *args, + struct device *dev); +extern int of_genpd_add_subdomain(struct of_phandle_args *parent, + struct of_phandle_args *new_subdomain); int genpd_dev_pm_attach(struct device *dev); #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ @@ -227,6 +231,18 @@ static inline struct generic_pm_domain *of_genpd_get_from_provider( #define __of_genpd_xlate_simple NULL #define __of_genpd_xlate_onecell NULL +static inline int of_genpd_add_device(struct of_phandle_args *args, + struct device *dev) +{ + return -ENODEV; +} + +static inline int of_genpd_add_subdomain(struct of_phandle_args *parent, + struct of_phandle_args *new_subdomain) +{ + return -ENODEV; +} + static inline int genpd_dev_pm_attach(struct device *dev) { return -ENODEV; -- cgit v1.2.3 From f58d4e5ab0ca3453f091eab514474e9fdbfc539f Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:08 +0100 Subject: PM / Domains: Don't expose generic_pm_domain structure to clients There should be no need to expose the generic_pm_domain structure to clients and this eliminates the need to implement reference counting for any external reference to a PM domain. Therefore, make the functions pm_genpd_lookup_dev() and of_genpd_get_from_provider() private to the PM domain core. The functions are renamed in accordance with the naming conventions for genpd static functions. Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 19 +++++++++---------- include/linux/pm_domain.h | 14 -------------- 2 files changed, 9 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e12d3a8a29a9..d8c1eabc7117 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -45,7 +45,7 @@ static DEFINE_MUTEX(gpd_list_lock); * and checks that the PM domain pointer is a real generic PM domain. * Any failure results in NULL being returned. */ -struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) +static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) { struct generic_pm_domain *genpd = NULL, *gpd; @@ -1119,7 +1119,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, dev_dbg(dev, "%s()\n", __func__); - if (!genpd || genpd != pm_genpd_lookup_dev(dev)) + if (!genpd || genpd != genpd_lookup_dev(dev)) return -EINVAL; /* The above validation also means we have existing domain_data. */ @@ -1466,7 +1466,7 @@ void of_genpd_del_provider(struct device_node *np) EXPORT_SYMBOL_GPL(of_genpd_del_provider); /** - * of_genpd_get_from_provider() - Look-up PM domain + * genpd_get_from_provider() - Look-up PM domain * @genpdspec: OF phandle args to use for look-up * * Looks for a PM domain provider under the node specified by @genpdspec and if @@ -1476,7 +1476,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider); * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() * on failure. */ -struct generic_pm_domain *of_genpd_get_from_provider( +static struct generic_pm_domain *genpd_get_from_provider( struct of_phandle_args *genpdspec) { struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); @@ -1499,7 +1499,6 @@ struct generic_pm_domain *of_genpd_get_from_provider( return genpd; } -EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); /** * of_genpd_add_device() - Add a device to an I/O PM domain @@ -1513,7 +1512,7 @@ int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev) { struct generic_pm_domain *genpd; - genpd = of_genpd_get_from_provider(genpdspec); + genpd = genpd_get_from_provider(genpdspec); if (IS_ERR(genpd)) return PTR_ERR(genpd); @@ -1535,11 +1534,11 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent_spec, { struct generic_pm_domain *parent, *subdomain; - parent = of_genpd_get_from_provider(parent_spec); + parent = genpd_get_from_provider(parent_spec); if (IS_ERR(parent)) return PTR_ERR(parent); - subdomain = of_genpd_get_from_provider(subdomain_spec); + subdomain = genpd_get_from_provider(subdomain_spec); if (IS_ERR(subdomain)) return PTR_ERR(subdomain); @@ -1561,7 +1560,7 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off) unsigned int i; int ret = 0; - pd = pm_genpd_lookup_dev(dev); + pd = genpd_lookup_dev(dev); if (!pd) return; @@ -1642,7 +1641,7 @@ int genpd_dev_pm_attach(struct device *dev) return -ENOENT; } - pd = of_genpd_get_from_provider(&pd_args); + pd = genpd_get_from_provider(&pd_args); of_node_put(pd_args.np); if (IS_ERR(pd)) { dev_dbg(dev, "%s() failed to find PM domain: %ld\n", diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index e1964a242389..bd411e754f4a 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -116,7 +116,6 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) return to_gpd_data(dev->power.subsys_data->domain_data); } -extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev); extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td); @@ -138,10 +137,6 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) { return ERR_PTR(-ENOSYS); } -static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) -{ - return NULL; -} static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td) @@ -199,9 +194,6 @@ typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, void *data); void of_genpd_del_provider(struct device_node *np); -struct generic_pm_domain *of_genpd_get_from_provider( - struct of_phandle_args *genpdspec); - struct generic_pm_domain *__of_genpd_xlate_simple( struct of_phandle_args *genpdspec, void *data); @@ -222,12 +214,6 @@ static inline int __of_genpd_add_provider(struct device_node *np, } static inline void of_genpd_del_provider(struct device_node *np) {} -static inline struct generic_pm_domain *of_genpd_get_from_provider( - struct of_phandle_args *genpdspec) -{ - return NULL; -} - #define __of_genpd_xlate_simple NULL #define __of_genpd_xlate_onecell NULL -- cgit v1.2.3 From 892ebdcccb4e11c5b43036a50e89e3869df5b429 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:09 +0100 Subject: PM / Domains: Don't expose xlate and provider helper functions Functions __of_genpd_xlate_simple(), __of_genpd_xlate_onecell() and __of_genpd_add_provider() are not used outside of the core generic PM domain code. Therefore, reduce the number of APIs exposed by making these static. At the same time don't expose the typedef for genpd_xlate_t either and make this a local definition as well. The functions are renamed to follow the naming conventions for static functions in the generic PM domain core. Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 49 ++++++++++++++++++++++++++++++++++----------- include/linux/pm_domain.h | 42 +++++++++++++------------------------- 2 files changed, 51 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index d8c1eabc7117..c93aa3a64839 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1329,6 +1329,10 @@ int pm_genpd_init(struct generic_pm_domain *genpd, EXPORT_SYMBOL_GPL(pm_genpd_init); #ifdef CONFIG_PM_GENERIC_DOMAINS_OF + +typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, + void *data); + /* * Device Tree based PM domain providers. * @@ -1340,8 +1344,8 @@ EXPORT_SYMBOL_GPL(pm_genpd_init); * maps a PM domain specifier retrieved from the device tree to a PM domain. * * Two simple mapping functions have been provided for convenience: - * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. - * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by + * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. + * - genpd_xlate_onecell() for mapping of multiple PM domains per node by * index. */ @@ -1366,7 +1370,7 @@ static LIST_HEAD(of_genpd_providers); static DEFINE_MUTEX(of_genpd_mutex); /** - * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping + * genpd_xlate_simple() - Xlate function for direct node-domain mapping * @genpdspec: OF phandle args to map into a PM domain * @data: xlate function private data - pointer to struct generic_pm_domain * @@ -1374,7 +1378,7 @@ static DEFINE_MUTEX(of_genpd_mutex); * have their own device tree nodes. The private data of xlate function needs * to be a valid pointer to struct generic_pm_domain. */ -struct generic_pm_domain *__of_genpd_xlate_simple( +static struct generic_pm_domain *genpd_xlate_simple( struct of_phandle_args *genpdspec, void *data) { @@ -1382,10 +1386,9 @@ struct generic_pm_domain *__of_genpd_xlate_simple( return ERR_PTR(-EINVAL); return data; } -EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple); /** - * __of_genpd_xlate_onecell() - Xlate function using a single index. + * genpd_xlate_onecell() - Xlate function using a single index. * @genpdspec: OF phandle args to map into a PM domain * @data: xlate function private data - pointer to struct genpd_onecell_data * @@ -1394,7 +1397,7 @@ EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple); * A single cell is used as an index into an array of PM domains specified in * the genpd_onecell_data struct when registering the provider. */ -struct generic_pm_domain *__of_genpd_xlate_onecell( +static struct generic_pm_domain *genpd_xlate_onecell( struct of_phandle_args *genpdspec, void *data) { @@ -1414,16 +1417,15 @@ struct generic_pm_domain *__of_genpd_xlate_onecell( return genpd_data->domains[idx]; } -EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell); /** - * __of_genpd_add_provider() - Register a PM domain provider for a node + * genpd_add_provider() - Register a PM domain provider for a node * @np: Device node pointer associated with the PM domain provider. * @xlate: Callback for decoding PM domain from phandle arguments. * @data: Context pointer for @xlate callback. */ -int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, - void *data) +static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, + void *data) { struct of_genpd_provider *cp; @@ -1442,7 +1444,30 @@ int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, return 0; } -EXPORT_SYMBOL_GPL(__of_genpd_add_provider); + +/** + * of_genpd_add_provider_simple() - Register a simple PM domain provider + * @np: Device node pointer associated with the PM domain provider. + * @genpd: Pointer to PM domain associated with the PM domain provider. + */ +int of_genpd_add_provider_simple(struct device_node *np, + struct generic_pm_domain *genpd) +{ + return genpd_add_provider(np, genpd_xlate_simple, genpd); +} +EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); + +/** + * of_genpd_add_provider_onecell() - Register a onecell PM domain provider + * @np: Device node pointer associated with the PM domain provider. + * @data: Pointer to the data associated with the PM domain provider. + */ +int of_genpd_add_provider_onecell(struct device_node *np, + struct genpd_onecell_data *data) +{ + return genpd_add_provider(np, genpd_xlate_onecell, data); +} +EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); /** * of_genpd_del_provider() - Remove a previously registered PM domain provider diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index bd411e754f4a..f103869db443 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -187,19 +187,12 @@ struct genpd_onecell_data { unsigned int num_domains; }; -typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, - void *data); - #ifdef CONFIG_PM_GENERIC_DOMAINS_OF -int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, - void *data); +int of_genpd_add_provider_simple(struct device_node *np, + struct generic_pm_domain *genpd); +int of_genpd_add_provider_onecell(struct device_node *np, + struct genpd_onecell_data *data); void of_genpd_del_provider(struct device_node *np); -struct generic_pm_domain *__of_genpd_xlate_simple( - struct of_phandle_args *genpdspec, - void *data); -struct generic_pm_domain *__of_genpd_xlate_onecell( - struct of_phandle_args *genpdspec, - void *data); extern int of_genpd_add_device(struct of_phandle_args *args, struct device *dev); extern int of_genpd_add_subdomain(struct of_phandle_args *parent, @@ -207,15 +200,19 @@ extern int of_genpd_add_subdomain(struct of_phandle_args *parent, int genpd_dev_pm_attach(struct device *dev); #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ -static inline int __of_genpd_add_provider(struct device_node *np, - genpd_xlate_t xlate, void *data) +static inline int of_genpd_add_provider_simple(struct device_node *np, + struct generic_pm_domain *genpd) { - return 0; + return -ENOTSUPP; +} + +static inline int of_genpd_add_provider_onecell(struct device_node *np, + struct genpd_onecell_data *data) +{ + return -ENOTSUPP; } -static inline void of_genpd_del_provider(struct device_node *np) {} -#define __of_genpd_xlate_simple NULL -#define __of_genpd_xlate_onecell NULL +static inline void of_genpd_del_provider(struct device_node *np) {} static inline int of_genpd_add_device(struct of_phandle_args *args, struct device *dev) @@ -235,17 +232,6 @@ static inline int genpd_dev_pm_attach(struct device *dev) } #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ -static inline int of_genpd_add_provider_simple(struct device_node *np, - struct generic_pm_domain *genpd) -{ - return __of_genpd_add_provider(np, __of_genpd_xlate_simple, genpd); -} -static inline int of_genpd_add_provider_onecell(struct device_node *np, - struct genpd_onecell_data *data) -{ - return __of_genpd_add_provider(np, __of_genpd_xlate_onecell, data); -} - #ifdef CONFIG_PM extern int dev_pm_domain_attach(struct device *dev, bool power_on); extern void dev_pm_domain_detach(struct device *dev, bool power_off); -- cgit v1.2.3 From de0aa06d8b1c39df1071bfe169b3b97ca6bc01ac Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:12 +0100 Subject: PM / Domains: Store the provider in the PM domain structure It is possible that a device has more than one provider of PM domains and to support the removal of a PM domain by provider, it is necessary to store a reference to the provider in the PM domain structure. Therefore, store a reference to the firmware node handle in the PM domain structure and populate it when providers (only device-tree based providers are currently supported by PM domains) are registered. Please note that when removing PM domains, it is necessary to verify that the PM domain provider has been removed from the list of providers before the PM domain can be removed. To do this add another member to the PM domain structure that indicates if the provider is present and set this member accordingly when providers are added and removed. Initialise the 'provider' and 'has_provider' members of the generic_pm_domain structure when a PM domains is added by calling pm_genpd_init(). Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 42 +++++++++++++++++++++++++++++++++++++----- include/linux/pm_domain.h | 2 ++ 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 1bd8d412db06..d5135caa84db 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1306,6 +1306,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd, genpd->device_count = 0; genpd->max_off_time_ns = -1; genpd->max_off_time_changed = true; + genpd->provider = NULL; + genpd->has_provider = false; genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_resume = genpd_runtime_resume; genpd->domain.ops.prepare = pm_genpd_prepare; @@ -1491,6 +1493,11 @@ int of_genpd_add_provider_simple(struct device_node *np, if (pm_genpd_present(genpd)) ret = genpd_add_provider(np, genpd_xlate_simple, genpd); + if (!ret) { + genpd->provider = &np->fwnode; + genpd->has_provider = true; + } + mutex_unlock(&gpd_list_lock); return ret; @@ -1506,7 +1513,7 @@ int of_genpd_add_provider_onecell(struct device_node *np, struct genpd_onecell_data *data) { unsigned int i; - int ret; + int ret = -EINVAL; if (!np || !data) return -EINVAL; @@ -1514,13 +1521,26 @@ int of_genpd_add_provider_onecell(struct device_node *np, mutex_lock(&gpd_list_lock); for (i = 0; i < data->num_domains; i++) { - if (!pm_genpd_present(data->domains[i])) { - mutex_unlock(&gpd_list_lock); - return -EINVAL; - } + if (!pm_genpd_present(data->domains[i])) + goto error; + + data->domains[i]->provider = &np->fwnode; + data->domains[i]->has_provider = true; } ret = genpd_add_provider(np, genpd_xlate_onecell, data); + if (ret < 0) + goto error; + + mutex_unlock(&gpd_list_lock); + + return 0; + +error: + while (i--) { + data->domains[i]->provider = NULL; + data->domains[i]->has_provider = false; + } mutex_unlock(&gpd_list_lock); @@ -1535,10 +1555,21 @@ EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell); void of_genpd_del_provider(struct device_node *np) { struct of_genpd_provider *cp; + struct generic_pm_domain *gpd; + mutex_lock(&gpd_list_lock); mutex_lock(&of_genpd_mutex); list_for_each_entry(cp, &of_genpd_providers, link) { if (cp->node == np) { + /* + * For each PM domain associated with the + * provider, set the 'has_provider' to false + * so that the PM domain can be safely removed. + */ + list_for_each_entry(gpd, &gpd_list, gpd_list_node) + if (gpd->provider == &np->fwnode) + gpd->has_provider = false; + list_del(&cp->link); of_node_put(cp->node); kfree(cp); @@ -1546,6 +1577,7 @@ void of_genpd_del_provider(struct device_node *np) } } mutex_unlock(&of_genpd_mutex); + mutex_unlock(&gpd_list_lock); } EXPORT_SYMBOL_GPL(of_genpd_del_provider); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index f103869db443..554f8915c691 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -51,6 +51,8 @@ struct generic_pm_domain { struct mutex lock; struct dev_power_governor *gov; struct work_struct power_off_work; + struct fwnode_handle *provider; /* Identity of the domain provider */ + bool has_provider; const char *name; atomic_t sd_count; /* Number of subdomains with power "on" */ enum gpd_status status; /* Current state of the domain */ -- cgit v1.2.3 From 3fe577107ccf1974958701df710e0b07ef16db75 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:13 +0100 Subject: 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 Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 60 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 5 ++++ 2 files changed, 65 insertions(+) (limited to 'include/linux') 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, } 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 (genpd->has_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 the associated provider has + * been removed, 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, 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, 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; @@ -165,6 +166,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, -- cgit v1.2.3 From 17926551c98a4ff5d7fa3a574c60534fedb3f2c6 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 12 Sep 2016 12:01:14 +0100 Subject: PM / Domains: Add support for removing nested PM domains by provider If a device supports PM domains that are subdomains of another PM domain, then the PM domains should be removed in reverse order to ensure that the subdomains are removed first. Furthermore, if there is more than one provider, then there needs to be a way to remove the domains in reverse order for a specific provider. Add the function of_genpd_remove_last() to remove the last PM domain added by a given PM domain provider and return the generic_pm_domain structure for the PM domain that was removed. Signed-off-by: Jon Hunter Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 7 +++++++ 2 files changed, 42 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 7481e3e316a2..b0cf46dcae73 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1744,6 +1744,41 @@ out: } EXPORT_SYMBOL_GPL(of_genpd_add_subdomain); +/** + * of_genpd_remove_last - Remove the last PM domain registered for a provider + * @provider: Pointer to device structure associated with provider + * + * Find the last PM domain that was added by a particular provider and + * remove this PM domain from the list of PM domains. The provider is + * identified by the 'provider' device structure that is passed. The PM + * domain will only be removed, if the provider associated with domain + * has been removed. + * + * Returns a valid pointer to struct generic_pm_domain on success or + * ERR_PTR() on failure. + */ +struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) +{ + struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT); + int ret; + + if (IS_ERR_OR_NULL(np)) + return ERR_PTR(-EINVAL); + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (gpd->provider == &np->fwnode) { + ret = genpd_remove(gpd); + genpd = ret ? ERR_PTR(ret) : gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + return genpd; +} +EXPORT_SYMBOL_GPL(of_genpd_remove_last); + /** * genpd_dev_pm_detach - Detach a device from its PM domain. * @dev: Device to detach. diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 85f7d53a9827..a09fe5c009c8 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -204,6 +204,7 @@ extern int of_genpd_add_device(struct of_phandle_args *args, struct device *dev); extern int of_genpd_add_subdomain(struct of_phandle_args *parent, struct of_phandle_args *new_subdomain); +extern struct generic_pm_domain *of_genpd_remove_last(struct device_node *np); int genpd_dev_pm_attach(struct device *dev); #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ @@ -237,6 +238,12 @@ static inline int genpd_dev_pm_attach(struct device *dev) { return -ENODEV; } + +static inline +struct generic_pm_domain *of_genpd_remove_last(struct device_node *np) +{ + return ERR_PTR(-ENOTSUPP); +} #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ #ifdef CONFIG_PM -- cgit v1.2.3