From 401ea1572de944df548a13eded82339491a739ff Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 17 Mar 2017 11:26:19 +0530 Subject: PM / Domain: Add struct device to genpd The power-domain core would be using the OPP core going forward and the OPP core has the basic requirement of a device structure for its working. Add a struct device to the genpd structure. This doesn't register the device with device core as the "dev" pointer is mostly used by the OPP core as a cookie for now and registering the device is not mandatory. Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson --- drivers/base/power/domain.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 1ea0e2502e8e..4a3dc9cc0848 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1696,6 +1696,9 @@ int pm_genpd_init(struct generic_pm_domain *genpd, return ret; } + device_initialize(&genpd->dev); + dev_set_name(&genpd->dev, "%s", genpd->name); + mutex_lock(&gpd_list_lock); list_add(&genpd->gpd_list_node, &gpd_list); mutex_unlock(&gpd_list_lock); -- cgit v1.2.1 From 6a0ae73d95956f7e900eb77808a7e8bad67a684d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 5 Apr 2018 15:53:34 +0530 Subject: PM / Domain: Add support to parse domain's OPP table The generic power domains can have an OPP table for themselves now, and phandle of their OPP nodes can be used by the devices powered by the domain. In order for the OPP core to translate requirements between the devices and their power domains, both need to have an OPP table in kernel. Parse the OPP table for power domains if they have their set_performance_state() callback set. With this patch, an OPP table would be created for the genpd in kernel based on the OPP table present in DT, if the genpd have its set_performance_state() callback set. Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson --- drivers/base/power/domain.c | 76 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4a3dc9cc0848..5c0019d70d76 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1895,14 +1896,33 @@ int of_genpd_add_provider_simple(struct device_node *np, mutex_lock(&gpd_list_lock); - if (genpd_present(genpd)) { - ret = genpd_add_provider(np, genpd_xlate_simple, genpd); - if (!ret) { - genpd->provider = &np->fwnode; - genpd->has_provider = true; + if (!genpd_present(genpd)) + goto unlock; + + genpd->dev.of_node = np; + + /* Parse genpd OPP table */ + if (genpd->set_performance_state) { + ret = dev_pm_opp_of_add_table(&genpd->dev); + if (ret) { + dev_err(&genpd->dev, "Failed to add OPP table: %d\n", + ret); + goto unlock; } } + ret = genpd_add_provider(np, genpd_xlate_simple, genpd); + if (ret) { + if (genpd->set_performance_state) + dev_pm_opp_of_remove_table(&genpd->dev); + + goto unlock; + } + + genpd->provider = &np->fwnode; + genpd->has_provider = true; + +unlock: mutex_unlock(&gpd_list_lock); return ret; @@ -1917,6 +1937,7 @@ EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple); int of_genpd_add_provider_onecell(struct device_node *np, struct genpd_onecell_data *data) { + struct generic_pm_domain *genpd; unsigned int i; int ret = -EINVAL; @@ -1929,13 +1950,27 @@ int of_genpd_add_provider_onecell(struct device_node *np, data->xlate = genpd_xlate_onecell; for (i = 0; i < data->num_domains; i++) { - if (!data->domains[i]) + genpd = data->domains[i]; + + if (!genpd) continue; - if (!genpd_present(data->domains[i])) + if (!genpd_present(genpd)) goto error; - data->domains[i]->provider = &np->fwnode; - data->domains[i]->has_provider = true; + genpd->dev.of_node = np; + + /* Parse genpd OPP table */ + if (genpd->set_performance_state) { + ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); + if (ret) { + dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n", + i, ret); + goto error; + } + } + + genpd->provider = &np->fwnode; + genpd->has_provider = true; } ret = genpd_add_provider(np, data->xlate, data); @@ -1948,10 +1983,16 @@ int of_genpd_add_provider_onecell(struct device_node *np, error: while (i--) { - if (!data->domains[i]) + genpd = data->domains[i]; + + if (!genpd) continue; - data->domains[i]->provider = NULL; - data->domains[i]->has_provider = false; + + genpd->provider = NULL; + genpd->has_provider = false; + + if (genpd->set_performance_state) + dev_pm_opp_of_remove_table(&genpd->dev); } mutex_unlock(&gpd_list_lock); @@ -1978,10 +2019,17 @@ void of_genpd_del_provider(struct device_node *np) * 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) + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (gpd->provider == &np->fwnode) { gpd->has_provider = false; + if (!gpd->set_performance_state) + continue; + + dev_pm_opp_of_remove_table(&gpd->dev); + } + } + list_del(&cp->link); of_node_put(cp->node); kfree(cp); -- cgit v1.2.1 From 6e41766a6a504b605a105cc5ab8d276ea20052ba Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 29 Nov 2017 15:21:51 +0530 Subject: PM / Domain: Implement of_genpd_opp_to_performance_state() This implements of_genpd_opp_to_performance_state() which can be used from the device drivers or the OPP core to find the performance state encoded in the "required-opps" property of a node. Normally this would be called only once for each OPP of the device for which the OPP table of the device is getting generated. Different platforms may encode the performance state differently using the OPP table (they may simply return value of opp-hz or opp-microvolt, or apply some algorithm on top of those values) and so a new callback ->opp_to_performance_state() is implemented to allow platform specific drivers to convert the power domain OPP to a performance state value. Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson --- drivers/base/power/domain.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5c0019d70d76..29e25dc0584c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2412,6 +2412,54 @@ int of_genpd_parse_idle_states(struct device_node *dn, } EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states); +/** + * of_genpd_opp_to_performance_state- Gets performance state of device's + * power domain corresponding to a DT node's "required-opps" property. + * + * @dev: Device for which the performance-state needs to be found. + * @opp_node: DT node where the "required-opps" property is present. This can be + * the device node itself (if it doesn't have an OPP table) or a node + * within the OPP table of a device (if device has an OPP table). + * @state: Pointer to return performance state. + * + * Returns performance state corresponding to the "required-opps" property of + * a DT node. This calls platform specific genpd->opp_to_performance_state() + * callback to translate power domain OPP to performance state. + * + * Returns performance state on success and 0 on failure. + */ +unsigned int of_genpd_opp_to_performance_state(struct device *dev, + struct device_node *opp_node) +{ + struct generic_pm_domain *genpd; + struct dev_pm_opp *opp; + int state = 0; + + genpd = dev_to_genpd(dev); + if (IS_ERR(genpd)) + return 0; + + if (unlikely(!genpd->set_performance_state)) + return 0; + + genpd_lock(genpd); + + opp = of_dev_pm_opp_find_required_opp(&genpd->dev, opp_node); + if (IS_ERR(opp)) { + state = PTR_ERR(opp); + goto unlock; + } + + state = genpd->opp_to_performance_state(genpd, opp); + dev_pm_opp_put(opp); + +unlock: + genpd_unlock(genpd); + + return state; +} +EXPORT_SYMBOL_GPL(of_genpd_opp_to_performance_state); + #endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ -- cgit v1.2.1 From 9ad14c001651955ebc390a5bb56858b0ee27ec2d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 24 May 2018 16:02:40 +0530 Subject: PM / Domain: Return 0 on error from of_genpd_opp_to_performance_state() of_genpd_opp_to_performance_state() should return 0 on errors, as its doc comment describes. While it follows that mostly, it returns a negative error number on one of the failures. Fix that. Fixes: 6e41766a6a50 "PM / Domain: Implement of_genpd_opp_to_performance_state()" Reported-by: Rajendra Nayak Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da6c8860c72e..71a1cc79fbaa 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2431,7 +2431,8 @@ unsigned int of_genpd_opp_to_performance_state(struct device *dev, opp = of_dev_pm_opp_find_required_opp(&genpd->dev, opp_node); if (IS_ERR(opp)) { - state = PTR_ERR(opp); + dev_err(dev, "Failed to find required OPP: %ld\n", + PTR_ERR(opp)); goto unlock; } -- cgit v1.2.1 From e89128124c28cc9b74ce9e7a605b22dae031fe5d Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 30 May 2018 15:15:17 +0530 Subject: PM / domains: Add perf_state attribute to genpd debugfs Now that genpd supports performance states, add this additional attribute as part of the power domains debugfs entry, to display the current performance state for the Power domain. Suggested-by: David Collins Signed-off-by: Rajendra Nayak Acked-by: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 71a1cc79fbaa..166259053f8d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2713,6 +2713,19 @@ static int genpd_devices_show(struct seq_file *s, void *data) return ret; } +static int genpd_perf_state_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *genpd = s->private; + + if (genpd_lock_interruptible(genpd)) + return -ERESTARTSYS; + + seq_printf(s, "%u\n", genpd->performance_state); + + genpd_unlock(genpd); + return 0; +} + #define define_genpd_open_function(name) \ static int genpd_##name##_open(struct inode *inode, struct file *file) \ { \ @@ -2726,6 +2739,7 @@ define_genpd_open_function(idle_states); define_genpd_open_function(active_time); define_genpd_open_function(total_idle_time); define_genpd_open_function(devices); +define_genpd_open_function(perf_state); #define define_genpd_debugfs_fops(name) \ static const struct file_operations genpd_##name##_fops = { \ @@ -2742,6 +2756,7 @@ define_genpd_debugfs_fops(idle_states); define_genpd_debugfs_fops(active_time); define_genpd_debugfs_fops(total_idle_time); define_genpd_debugfs_fops(devices); +define_genpd_debugfs_fops(perf_state); static int __init genpd_debug_init(void) { @@ -2775,6 +2790,9 @@ static int __init genpd_debug_init(void) d, genpd, &genpd_total_idle_time_fops); debugfs_create_file("devices", 0444, d, genpd, &genpd_devices_fops); + if (genpd->set_performance_state) + debugfs_create_file("perf_state", 0444, + d, genpd, &genpd_perf_state_fops); } return 0; -- cgit v1.2.1 From 1a7a67072f35b3e65e76fc694b088ca48b4dae35 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 29 May 2018 12:04:14 +0200 Subject: PM / Domains: Drop __pm_genpd_add_device() There are still a few non-DT existing users of genpd, however neither of them uses __pm_genpd_add_device(), hence let's drop it. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 166259053f8d..2bb67e4f6280 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1414,23 +1414,21 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, } /** - * __pm_genpd_add_device - Add a device to an I/O PM domain. + * pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. * @dev: Device to be added. - * @td: Set of PM QoS timing parameters to attach to the device. */ -int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, - struct gpd_timing_data *td) +int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev) { int ret; mutex_lock(&gpd_list_lock); - ret = genpd_add_device(genpd, dev, td); + ret = genpd_add_device(genpd, dev, NULL); mutex_unlock(&gpd_list_lock); return ret; } -EXPORT_SYMBOL_GPL(__pm_genpd_add_device); +EXPORT_SYMBOL_GPL(pm_genpd_add_device); static int genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev) -- cgit v1.2.1 From 924f448699627722a7dcaefb857d09fd324e75c5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 29 May 2018 12:04:15 +0200 Subject: PM / Domains: Drop genpd as in-param for pm_genpd_remove_device() There is no need to pass a genpd struct to pm_genpd_remove_device(), as we already have the information about the PM domain (genpd) through the device structure. Additionally, we don't allow to remove a PM domain from a device, other than the one it may have assigned to it, so really it does not make sense to have a separate in-param for it. For these reason, drop it and update the current only call to pm_genpd_remove_device() from amdgpu_acp. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2bb67e4f6280..83ce6ca6c769 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1475,13 +1475,13 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, /** * pm_genpd_remove_device - Remove a device from an I/O PM domain. - * @genpd: PM domain to remove the device from. * @dev: Device to be removed. */ -int pm_genpd_remove_device(struct generic_pm_domain *genpd, - struct device *dev) +int pm_genpd_remove_device(struct device *dev) { - if (!genpd || genpd != genpd_lookup_dev(dev)) + struct generic_pm_domain *genpd = genpd_lookup_dev(dev); + + if (!genpd) return -EINVAL; return genpd_remove_device(genpd, dev); -- cgit v1.2.1 From 96c1bf68852a1709bb411eafd3edcc59186eb293 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 29 May 2018 12:04:16 +0200 Subject: PM / Domains: Drop unused parameter in genpd_allocate_dev_data() The in-parameter struct generic_pm_domain *genpd to genpd_allocate_dev_data() is unused, so let's drop it. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 83ce6ca6c769..6f403d6fccb2 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1316,7 +1316,6 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); #endif /* CONFIG_PM_SLEEP */ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, - struct generic_pm_domain *genpd, struct gpd_timing_data *td) { struct generic_pm_domain_data *gpd_data; @@ -1385,7 +1384,7 @@ static int 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 = genpd_alloc_dev_data(dev, genpd, td); + gpd_data = genpd_alloc_dev_data(dev, td); if (IS_ERR(gpd_data)) return PTR_ERR(gpd_data); -- cgit v1.2.1