From 11e1a1648298656722fe206a53e555e2e98668ae Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 3 Jul 2019 15:03:14 +0530 Subject: opp: Don't decrement uninitialized list_kref The list_kref was added for static OPPs and to track their users. The kref is initialized while the static OPPs are added, but removed unconditionally even if the static OPPs were never added. This causes refcount mismatch warnings currently. Fix that by always initializing the kref when the OPP table is first initialized. The refcount is later incremented only for the second user onwards. Fixes: d0e8ae6c26da ("OPP: Create separate kref for static OPPs list") Reported-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 1 + drivers/opp/of.c | 21 ++++----------------- 2 files changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 0ee8c0133d3e..9ff0538ee83a 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -988,6 +988,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index) BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head); INIT_LIST_HEAD(&opp_table->opp_list); kref_init(&opp_table->kref); + kref_init(&opp_table->list_kref); /* Secure the device table modification */ list_add(&opp_table->node, &opp_tables); diff --git a/drivers/opp/of.c b/drivers/opp/of.c index b313aca9894f..698a92c3c17b 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -662,8 +662,6 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) return 0; } - kref_init(&opp_table->list_kref); - /* We have opp-table node now, iterate over it and add OPPs */ for_each_available_child_of_node(opp_table->np, np) { opp = _opp_add_static_v2(opp_table, dev, np); @@ -672,17 +670,15 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, ret); of_node_put(np); - goto put_list_kref; + return ret; } else if (opp) { count++; } } /* There should be one of more OPP defined */ - if (WARN_ON(!count)) { - ret = -ENOENT; - goto put_list_kref; - } + if (WARN_ON(!count)) + return -ENOENT; list_for_each_entry(opp, &opp_table->opp_list, node) pstate_count += !!opp->pstate; @@ -691,8 +687,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) if (pstate_count && pstate_count != count) { dev_err(dev, "Not all nodes have performance state set (%d: %d)\n", count, pstate_count); - ret = -ENOENT; - goto put_list_kref; + return -ENOENT; } if (pstate_count) @@ -701,11 +696,6 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) opp_table->parsed_static_opps = true; return 0; - -put_list_kref: - _put_opp_list_kref(opp_table); - - return ret; } /* Initializes OPP tables based on old-deprecated bindings */ @@ -731,8 +721,6 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) return -EINVAL; } - kref_init(&opp_table->list_kref); - val = prop->value; while (nr) { unsigned long freq = be32_to_cpup(val++) * 1000; @@ -742,7 +730,6 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) if (ret) { dev_err(dev, "%s: Failed to add OPP %ld (%d)\n", __func__, freq, ret); - _put_opp_list_kref(opp_table); return ret; } nr -= 2; -- cgit v1.2.3 From 7f93ff73f7c8c8bfa6be33bcc16470b0b44682aa Mon Sep 17 00:00:00 2001 From: "k.konieczny@partner.samsung.com" Date: Fri, 19 Jul 2019 17:05:32 +0200 Subject: opp: core: add regulators enable and disable Add enable regulators to dev_pm_opp_set_regulators() and disable regulators to dev_pm_opp_put_regulators(). Even if bootloader leaves regulators enabled, they should be enabled in kernel in order to increase the reference count. Signed-off-by: Kamil Konieczny Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 9ff0538ee83a..3b7ffd0234e9 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1626,6 +1626,12 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, goto free_regulators; } + ret = regulator_enable(reg); + if (ret < 0) { + regulator_put(reg); + goto free_regulators; + } + opp_table->regulators[i] = reg; } @@ -1639,8 +1645,10 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev, return opp_table; free_regulators: - while (i != 0) - regulator_put(opp_table->regulators[--i]); + while (i--) { + regulator_disable(opp_table->regulators[i]); + regulator_put(opp_table->regulators[i]); + } kfree(opp_table->regulators); opp_table->regulators = NULL; @@ -1666,8 +1674,10 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table) /* Make sure there are no concurrent readers while updating opp_table */ WARN_ON(!list_empty(&opp_table->opp_list)); - for (i = opp_table->regulator_count - 1; i >= 0; i--) + for (i = opp_table->regulator_count - 1; i >= 0; i--) { + regulator_disable(opp_table->regulators[i]); regulator_put(opp_table->regulators[i]); + } _free_set_opp_data(opp_table); -- cgit v1.2.3 From 4527551750e85f9be9296f2b30b19bb257f342e5 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 9 Jul 2019 16:00:13 +0800 Subject: opp: of: Support multiple suspend OPPs defined in DT With property "opp-supported-hw" introduced, the OPP table in DT could be a large OPP table and ONLY a subset of OPPs are available, based on the version of the hardware running on. That introduces restriction of using "opp-suspend" property to define the suspend OPP, as we are NOT sure if the OPP containing "opp-suspend" property is available for the hardware running on, and the of opp core does NOT allow multiple suspend OPPs defined in DT OPP table. To eliminate this restrition, make of opp core allow multiple suspend OPPs defined in DT, and pick the OPP with highest rate and with "opp-suspend" property present to be suspend OPP, it can speed up the suspend/resume process. Signed-off-by: Anson Huang Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 698a92c3c17b..1813f5ad5fa2 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -617,9 +617,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, /* OPP to select on device suspend */ if (of_property_read_bool(np, "opp-suspend")) { if (opp_table->suspend_opp) { - dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", - __func__, opp_table->suspend_opp->rate, - new_opp->rate); + /* Pick the OPP with higher rate as suspend OPP */ + if (new_opp->rate > opp_table->suspend_opp->rate) { + opp_table->suspend_opp->suspend = false; + new_opp->suspend = true; + opp_table->suspend_opp = new_opp; + } } else { new_opp->suspend = true; opp_table->suspend_opp = new_opp; -- cgit v1.2.3 From ee8193ee96c7802b5ba766243c4c950059a43323 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Aug 2019 12:26:02 +0200 Subject: ACPI: PM: Print debug messages on device power state changes Add an acpi_handle_debug() statement to acpi_device_set_power() to allow ACPI device power state changes to be tracked. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index f616b16c1f0b..4cb93d4f2ab6 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -166,6 +166,10 @@ int acpi_device_set_power(struct acpi_device *device, int state) || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; + acpi_handle_debug(device->handle, "Power state change: %s -> %s\n", + acpi_power_state_string(device->power.state), + acpi_power_state_string(state)); + /* Make sure this is a valid target state */ /* There is a special case for D0 addressed below. */ -- cgit v1.2.3 From b3ad17c09899d491cf9815a6db44d3f9b3f244e7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 29 Aug 2019 16:48:05 +0200 Subject: PM / Domains: Simplify genpd_lookup_dev() genpd_lookup_dev(), is a bit unnecessary heavy, as it walks the gpd_list to try to find a valid PM domain corresponding to the device's attached genpd. Instead of walking the gpd_list, let's use the fact that a genpd always has the ->runtime_suspend() callback assigned to the genpd_runtime_suspend() function. While changing this, let's take the opportunity to also rename genpd_lookup_dev(), into dev_to_genpd_safe() to better reflect its purpose. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b063bc41b0a9..59d6f183d0bb 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -149,29 +149,24 @@ static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, return ret; } +static int genpd_runtime_suspend(struct device *dev); + /* * Get the generic PM domain for a particular struct device. * This validates the struct device pointer, the PM domain pointer, * and checks that the PM domain pointer is a real generic PM domain. * Any failure results in NULL being returned. */ -static struct generic_pm_domain *genpd_lookup_dev(struct device *dev) +static struct generic_pm_domain *dev_to_genpd_safe(struct device *dev) { - struct generic_pm_domain *genpd = NULL, *gpd; - if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) return NULL; - mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (&gpd->domain == dev->pm_domain) { - genpd = gpd; - break; - } - } - mutex_unlock(&gpd_list_lock); + /* A genpd's always have its ->runtime_suspend() callback assigned. */ + if (dev->pm_domain->ops.runtime_suspend == genpd_runtime_suspend) + return pd_to_genpd(dev->pm_domain); - return genpd; + return NULL; } /* @@ -1610,7 +1605,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, */ int pm_genpd_remove_device(struct device *dev) { - struct generic_pm_domain *genpd = genpd_lookup_dev(dev); + struct generic_pm_domain *genpd = dev_to_genpd_safe(dev); if (!genpd) return -EINVAL; -- cgit v1.2.3 From 3ea4ca9267cf5d02149f7d8a11d8b4c82723a668 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 29 Aug 2019 16:48:15 +0200 Subject: PM / Domains: Verify PM domain type in dev_pm_genpd_set_performance_state() The dev_pm_genpd_set_performance_state() could in principle be called for a device that has a different PM domain type attached than a genpd. This would lead to a problem as dev_to_genpd() uses the container_of macro. Address this problem by using dev_to_genpd_safe() instead. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 59d6f183d0bb..cc85e87eaf05 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -380,8 +380,8 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) unsigned int prev; int ret; - genpd = dev_to_genpd(dev); - if (IS_ERR(genpd)) + genpd = dev_to_genpd_safe(dev); + if (!genpd) return -ENODEV; if (unlikely(!genpd->set_performance_state)) -- cgit v1.2.3