From patchwork Tue Mar 28 14:14:01 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jon Hunter X-Patchwork-Id: 744321 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3vstL35h2Gz9s7M for ; Wed, 29 Mar 2017 01:18:51 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753731AbdC1OSG (ORCPT ); Tue, 28 Mar 2017 10:18:06 -0400 Received: from [216.228.121.65] ([216.228.121.65]:10767 "EHLO hqemgate16.nvidia.com" rhost-flags-FAIL-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1753238AbdC1OSD (ORCPT ); Tue, 28 Mar 2017 10:18:03 -0400 Received: from hqnvupgp08.nvidia.com (Not Verified[216.228.121.13]) by hqemgate16.nvidia.com id ; Tue, 28 Mar 2017 07:17:32 -0700 Received: from HQMAIL101.nvidia.com ([172.20.13.39]) by hqnvupgp08.nvidia.com (PGP Universal service); Tue, 28 Mar 2017 07:14:15 -0700 X-PGP-Universal: processed; by hqnvupgp08.nvidia.com on Tue, 28 Mar 2017 07:14:15 -0700 Received: from HQMAIL108.nvidia.com (172.18.146.13) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1263.5; Tue, 28 Mar 2017 14:15:20 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL108.nvidia.com (172.18.146.13) with Microsoft SMTP Server id 15.0.1263.5 via Frontend Transport; Tue, 28 Mar 2017 14:15:20 +0000 Received: from goldfinger.nvidia.com (Not Verified[10.21.132.162]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 5, 8150) id ; Tue, 28 Mar 2017 07:15:20 -0700 From: Jon Hunter To: "Rafael J . Wysocki" , Kevin Hilman , Ulf Hansson , , CC: , , Marek Szyprowski , , , , Jon Hunter Subject: [RFC PATCH 2/4] PM / Domains: Add support for explicit control of PM domains Date: Tue, 28 Mar 2017 15:14:01 +0100 Message-ID: <1490710443-27425-3-git-send-email-jonathanh@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490710443-27425-1-git-send-email-jonathanh@nvidia.com> References: <1490710443-27425-1-git-send-email-jonathanh@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org The current generic PM domain framework (GenDP) only allows a single PM domain to be associated with a given device. There are several use-cases for various system-on-chip devices where it is necessary for a PM domain consumer to control more than one PM domain where the PM domains: i). Do not conform to a parent-child relationship so are not nested ii). May not be powered on and off at the same time so need independent control. To support the above, add new APIs for GenPD to allow consumers to get, power-on, power-off and put PM domains so that they can be explicitly controlled by the consumer. These new APIs for powering on and off the PM domains, call into the existing internal functions, genpd_sync_power_on/off(), to power them on and off. To ensure that PM domains that are both controlled explicitly (via these new APIs) and implicitly (via runtime-pm callbacks) do not conflict, the PM domain 'device_count' and 'suspended_count' counters are used to ensure the PM domain is in the correct state. For PM domains that are controlled explicitly, the debugfs 'summary' node for GenPD will display an 'unknown (X)' under the 'device' column to indicate that 'X' unknown device(s) are controlling the PM domain. Signed-off-by: Jon Hunter --- drivers/base/power/domain.c | 123 +++++++++++++++++++++++++++++++++++++++++++- include/linux/pm_domain.h | 18 +++++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 0eb75954c087..4980ec157750 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -303,6 +303,9 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, if (atomic_read(&genpd->sd_count) > 0) return -EBUSY; + if (genpd->device_count > genpd->suspended_count) + return -EBUSY; + list_for_each_entry(pdd, &genpd->dev_list, list_node) { enum pm_qos_flags_status stat; @@ -1568,6 +1571,117 @@ int pm_genpd_remove(struct generic_pm_domain *genpd) } EXPORT_SYMBOL_GPL(pm_genpd_remove); +/** + * pm_genpd_get - Get a generic I/O PM domain by name + * @name: Name of the PM domain. + * + * Look-ups a PM domain by name. If found, increment the device + * count for PM domain to ensure that the PM domain cannot be + * removed, increment the suspended count so that it can still + * be turned off (when not in-use) and return a pointer to its + * generic_pm_domain structure. If not found return ERR_PTR(). + */ +struct generic_pm_domain *pm_genpd_get(const char *name) +{ + struct generic_pm_domain *gpd, *genpd = ERR_PTR(-EEXIST); + + if (!name) + return ERR_PTR(-EINVAL); + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (!strcmp(gpd->name, name)) { + genpd_lock(gpd); + gpd->device_count++; + gpd->suspended_count++; + genpd_unlock(gpd); + genpd = gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + return genpd; +} +EXPORT_SYMBOL(pm_genpd_get); + +/** + * pm_genpd_put - Put a generic I/O PM domain + * @genpd: Pointer to a PM domain. + */ +void pm_genpd_put(struct generic_pm_domain *genpd) +{ + if (!genpd) + return; + + genpd_lock(genpd); + + if (WARN_ON(!genpd->device_count || !genpd->suspended_count)) + goto out; + + genpd->suspended_count--; + genpd->device_count--; + +out: + genpd_unlock(genpd); +} +EXPORT_SYMBOL(pm_genpd_put); + +/** + * pm_genpd_poweron - Power on a generic I/O PM domain + * @genpd: Pointer to a PM domain. + * + * Powers on a PM domain, if not already on, and decrements the + * 'suspended_count' to prevent the PM domain from being powered off. + */ +int pm_genpd_poweron(struct generic_pm_domain *genpd) +{ + if (!genpd) + return -EINVAL; + + genpd_lock(genpd); + + if (WARN_ON(!genpd->suspended_count)) + goto out; + + genpd->suspended_count--; + genpd_sync_power_on(genpd, true, 0); + +out: + genpd_unlock(genpd); + + return 0; +} +EXPORT_SYMBOL_GPL(pm_genpd_poweron); + +/** + * pm_genpd_poweroff - Power off a generic I/O PM domain + * @genpd: Pointer to a PM domain. + * + * Increments the 'suspended_count' for a PM domain and if the + * 'suspended_count' equals the 'device_count' then will power + * off the PM domain. + */ +int pm_genpd_poweroff(struct generic_pm_domain *genpd) +{ + if (!genpd) + return -EINVAL; + + genpd_lock(genpd); + + if (WARN_ON(genpd->suspended_count >= genpd->device_count)) + goto out; + + genpd->suspended_count++; + genpd_sync_power_off(genpd, false, 0); + +out: + genpd_unlock(genpd); + + return 0; +} +EXPORT_SYMBOL_GPL(pm_genpd_poweroff); + #ifdef CONFIG_PM_GENERIC_DOMAINS_OF typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args, @@ -2171,7 +2285,6 @@ int of_genpd_parse_idle_states(struct device_node *dn, return 0; } EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); - #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ @@ -2223,7 +2336,7 @@ static int pm_genpd_summary_one(struct seq_file *s, const char *kobj_path; struct gpd_link *link; char state[16]; - int ret; + int ret, count; ret = genpd_lock_interruptible(genpd); if (ret) @@ -2250,6 +2363,8 @@ static int pm_genpd_summary_one(struct seq_file *s, seq_puts(s, ", "); } + count = genpd->device_count; + list_for_each_entry(pm_data, &genpd->dev_list, list_node) { kobj_path = kobject_get_path(&pm_data->dev->kobj, genpd_is_irq_safe(genpd) ? @@ -2260,8 +2375,12 @@ static int pm_genpd_summary_one(struct seq_file *s, seq_printf(s, "\n %-50s ", kobj_path); rtpm_status_str(s, pm_data->dev); kfree(kobj_path); + count--; } + if (count > 0) + seq_printf(s, "\n unknown (%d)", count); + seq_puts(s, "\n"); exit: genpd_unlock(genpd); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 5339ed5bd6f9..b3aa1f237d96 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -143,6 +143,10 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, 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 generic_pm_domain *pm_genpd_get(const char *name); +extern void pm_genpd_put(struct generic_pm_domain *genpd); +extern int pm_genpd_poweron(struct generic_pm_domain *genpd); +extern int pm_genpd_poweroff(struct generic_pm_domain *genpd); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -182,6 +186,20 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) { return -ENOTSUPP; } +static inline struct generic_pm_domain *pm_genpd_get(const char *name) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline void pm_genpd_put(struct generic_pm_domain *genpd) {} +static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) +{ + return -ENOTSUPP; +} +static inline int pm_genpd_poweroff(struct generic_pm_domain *genpd) +{ + return -ENOTSUPP; +} #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))