summaryrefslogtreecommitdiff
path: root/drivers/opp/of.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-08-03 17:49:38 +0200
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2022-08-03 17:49:38 +0200
commitf6e0b468dab97a9214c9713c298ed9e2dd62f97f (patch)
tree4ddebb6700fa629c1c6bc549b376dd539b5ca4ad /drivers/opp/of.c
parenta771ea6413c00cf4af0570745f2e27084d7e2376 (diff)
parent1d95af02f23031c2e1cca7607c514b86ce85bc6e (diff)
Merge tag 'opp-updates-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull operating performance points (OPP) updates for 5.20-rc1 from Viresh Kumar: "- Make dev_pm_opp_set_regulators() accept NULL terminated list (Viresh Kumar). - Add dev_pm_opp_set_config() and friends and migrate other users/helpers to using them (Viresh Kumar). - Add support for multiple clocks for a device (Viresh Kumar and Krzysztof Kozlowski). - Configure resources before adding OPP table for Venus (Stanimir Varbanov). - Keep reference count up for opp->np and opp_table->np while they are still in use (Liang He). - Minor cleanups (Viresh Kumar and Yang Li)." * tag 'opp-updates-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: (43 commits) venus: pm_helpers: Fix warning in OPP during probe OPP: Don't drop opp->np reference while it is still in use OPP: Don't drop opp_table->np reference while it is still in use OPP: Remove dev{m}_pm_opp_of_add_table_noclk() PM / devfreq: tegra30: Register config_clks helper OPP: Allow config_clks helper for single clk case OPP: Provide a simple implementation to configure multiple clocks OPP: Assert clk_count == 1 for single clk helpers OPP: Add key specific assert() method to key finding helpers OPP: Compare bandwidths for all paths in _opp_compare_key() OPP: Allow multiple clocks for a device dt-bindings: opp: accept array of frequencies OPP: Make dev_pm_opp_set_opp() independent of frequency OPP: Reuse _opp_compare_key() in _opp_add_static_v2() OPP: Remove rate_not_available parameter to _opp_add() OPP: Use consistent names for OPP table instances OPP: Use generic key finding helpers for bandwidth key OPP: Use generic key finding helpers for level key OPP: Add generic key finding helpers and use them for freq APIs OPP: Remove dev_pm_opp_find_freq_ceil_by_volt() ...
Diffstat (limited to 'drivers/opp/of.c')
-rw-r--r--drivers/opp/of.c150
1 files changed, 80 insertions, 70 deletions
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index eb89c9a75985..605d68673f92 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -242,20 +242,20 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
opp_table->np = opp_np;
_opp_table_alloc_required_tables(opp_table, dev, opp_np);
- of_node_put(opp_np);
}
void _of_clear_opp_table(struct opp_table *opp_table)
{
_opp_table_free_required_tables(opp_table);
+ of_node_put(opp_table->np);
}
/*
* Release all resources previously acquired with a call to
* _of_opp_alloc_required_opps().
*/
-void _of_opp_free_required_opps(struct opp_table *opp_table,
- struct dev_pm_opp *opp)
+static void _of_opp_free_required_opps(struct opp_table *opp_table,
+ struct dev_pm_opp *opp)
{
struct dev_pm_opp **required_opps = opp->required_opps;
int i;
@@ -275,6 +275,12 @@ void _of_opp_free_required_opps(struct opp_table *opp_table,
kfree(required_opps);
}
+void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
+{
+ _of_opp_free_required_opps(opp_table, opp);
+ of_node_put(opp->np);
+}
+
/* Populate all required OPPs which are part of "required-opps" list */
static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
struct dev_pm_opp *opp)
@@ -767,7 +773,51 @@ void dev_pm_opp_of_remove_table(struct device *dev)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
-static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
+static int _read_rate(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
+ struct device_node *np)
+{
+ struct property *prop;
+ int i, count, ret;
+ u64 *rates;
+
+ prop = of_find_property(np, "opp-hz", NULL);
+ if (!prop)
+ return -ENODEV;
+
+ count = prop->length / sizeof(u64);
+ if (opp_table->clk_count != count) {
+ pr_err("%s: Count mismatch between opp-hz and clk_count (%d %d)\n",
+ __func__, count, opp_table->clk_count);
+ return -EINVAL;
+ }
+
+ rates = kmalloc_array(count, sizeof(*rates), GFP_KERNEL);
+ if (!rates)
+ return -ENOMEM;
+
+ ret = of_property_read_u64_array(np, "opp-hz", rates, count);
+ if (ret) {
+ pr_err("%s: Error parsing opp-hz: %d\n", __func__, ret);
+ } else {
+ /*
+ * Rate is defined as an unsigned long in clk API, and so
+ * casting explicitly to its type. Must be fixed once rate is 64
+ * bit guaranteed in clk API.
+ */
+ for (i = 0; i < count; i++) {
+ new_opp->rates[i] = (unsigned long)rates[i];
+
+ /* This will happen for frequencies > 4.29 GHz */
+ WARN_ON(new_opp->rates[i] != rates[i]);
+ }
+ }
+
+ kfree(rates);
+
+ return ret;
+}
+
+static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
struct device_node *np, bool peak)
{
const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps";
@@ -780,9 +830,9 @@ static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
return -ENODEV;
count = prop->length / sizeof(u32);
- if (table->path_count != count) {
+ if (opp_table->path_count != count) {
pr_err("%s: Mismatch between %s and paths (%d %d)\n",
- __func__, name, count, table->path_count);
+ __func__, name, count, opp_table->path_count);
return -EINVAL;
}
@@ -808,34 +858,27 @@ out:
return ret;
}
-static int _read_opp_key(struct dev_pm_opp *new_opp, struct opp_table *table,
- struct device_node *np, bool *rate_not_available)
+static int _read_opp_key(struct dev_pm_opp *new_opp,
+ struct opp_table *opp_table, struct device_node *np)
{
bool found = false;
- u64 rate;
int ret;
- ret = of_property_read_u64(np, "opp-hz", &rate);
- if (!ret) {
- /*
- * Rate is defined as an unsigned long in clk API, and so
- * casting explicitly to its type. Must be fixed once rate is 64
- * bit guaranteed in clk API.
- */
- new_opp->rate = (unsigned long)rate;
+ ret = _read_rate(new_opp, opp_table, np);
+ if (!ret)
found = true;
- }
- *rate_not_available = !!ret;
+ else if (ret != -ENODEV)
+ return ret;
/*
* Bandwidth consists of peak and average (optional) values:
* opp-peak-kBps = <path1_value path2_value>;
* opp-avg-kBps = <path1_value path2_value>;
*/
- ret = _read_bw(new_opp, table, np, true);
+ ret = _read_bw(new_opp, opp_table, np, true);
if (!ret) {
found = true;
- ret = _read_bw(new_opp, table, np, false);
+ ret = _read_bw(new_opp, opp_table, np, false);
}
/* The properties were found but we failed to parse them */
@@ -881,13 +924,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
struct dev_pm_opp *new_opp;
u32 val;
int ret;
- bool rate_not_available = false;
new_opp = _opp_allocate(opp_table);
if (!new_opp)
return ERR_PTR(-ENOMEM);
- ret = _read_opp_key(new_opp, opp_table, np, &rate_not_available);
+ ret = _read_opp_key(new_opp, opp_table, np);
if (ret < 0) {
dev_err(dev, "%s: opp key field not found\n", __func__);
goto free_opp;
@@ -895,14 +937,14 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
/* Check if the OPP supports hardware's hierarchy of versions or not */
if (!_opp_is_supported(dev, opp_table, np)) {
- dev_dbg(dev, "OPP not supported by hardware: %lu\n",
- new_opp->rate);
+ dev_dbg(dev, "OPP not supported by hardware: %s\n",
+ of_node_full_name(np));
goto free_opp;
}
new_opp->turbo = of_property_read_bool(np, "turbo-mode");
- new_opp->np = np;
+ new_opp->np = of_node_get(np);
new_opp->dynamic = false;
new_opp->available = true;
@@ -920,7 +962,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
if (opp_table->is_genpd)
new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp);
- ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
+ ret = _opp_add(dev, new_opp, opp_table);
if (ret) {
/* Don't return error for duplicate OPPs */
if (ret == -EBUSY)
@@ -931,8 +973,8 @@ 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) {
- /* Pick the OPP with higher rate as suspend OPP */
- if (new_opp->rate > opp_table->suspend_opp->rate) {
+ /* Pick the OPP with higher rate/bw/level as suspend OPP */
+ if (_opp_compare_key(opp_table, new_opp, opp_table->suspend_opp) == 1) {
opp_table->suspend_opp->suspend = false;
new_opp->suspend = true;
opp_table->suspend_opp = new_opp;
@@ -947,7 +989,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n",
- __func__, new_opp->turbo, new_opp->rate,
+ __func__, new_opp->turbo, new_opp->rates[0],
new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns,
new_opp->level);
@@ -1084,7 +1126,7 @@ remove_static_opp:
return ret;
}
-static int _of_add_table_indexed(struct device *dev, int index, bool getclk)
+static int _of_add_table_indexed(struct device *dev, int index)
{
struct opp_table *opp_table;
int ret, count;
@@ -1100,7 +1142,7 @@ static int _of_add_table_indexed(struct device *dev, int index, bool getclk)
index = 0;
}
- opp_table = _add_opp_table_indexed(dev, index, getclk);
+ opp_table = _add_opp_table_indexed(dev, index, true);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
@@ -1124,11 +1166,11 @@ static void devm_pm_opp_of_table_release(void *data)
dev_pm_opp_of_remove_table(data);
}
-static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk)
+static int _devm_of_add_table_indexed(struct device *dev, int index)
{
int ret;
- ret = _of_add_table_indexed(dev, index, getclk);
+ ret = _of_add_table_indexed(dev, index);
if (ret)
return ret;
@@ -1156,7 +1198,7 @@ static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk
*/
int devm_pm_opp_of_add_table(struct device *dev)
{
- return _devm_of_add_table_indexed(dev, 0, true);
+ return _devm_of_add_table_indexed(dev, 0);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
@@ -1179,7 +1221,7 @@ EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
*/
int dev_pm_opp_of_add_table(struct device *dev)
{
- return _of_add_table_indexed(dev, 0, true);
+ return _of_add_table_indexed(dev, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
@@ -1195,7 +1237,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
*/
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
- return _of_add_table_indexed(dev, index, true);
+ return _of_add_table_indexed(dev, index);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
@@ -1208,42 +1250,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
*/
int devm_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
- return _devm_of_add_table_indexed(dev, index, true);
+ return _devm_of_add_table_indexed(dev, index);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_indexed);
-/**
- * dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
- * tree without getting clk for device.
- * @dev: device pointer used to lookup OPP table.
- * @index: Index number.
- *
- * Register the initial OPP table with the OPP library for given device only
- * using the "operating-points-v2" property. Do not try to get the clk for the
- * device.
- *
- * Return: Refer to dev_pm_opp_of_add_table() for return values.
- */
-int dev_pm_opp_of_add_table_noclk(struct device *dev, int index)
-{
- return _of_add_table_indexed(dev, index, false);
-}
-EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk);
-
-/**
- * devm_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
- * tree without getting clk for device.
- * @dev: device pointer used to lookup OPP table.
- * @index: Index number.
- *
- * This is a resource-managed variant of dev_pm_opp_of_add_table_noclk().
- */
-int devm_pm_opp_of_add_table_noclk(struct device *dev, int index)
-{
- return _devm_of_add_table_indexed(dev, index, false);
-}
-EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_noclk);
-
/* CPU device specific helpers */
/**