1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27813dd6fSViresh Kumar /*
37813dd6fSViresh Kumar * Generic OPP OF helpers
47813dd6fSViresh Kumar *
57813dd6fSViresh Kumar * Copyright (C) 2009-2010 Texas Instruments Incorporated.
67813dd6fSViresh Kumar * Nishanth Menon
77813dd6fSViresh Kumar * Romit Dasgupta
87813dd6fSViresh Kumar * Kevin Hilman
97813dd6fSViresh Kumar */
107813dd6fSViresh Kumar
117813dd6fSViresh Kumar #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
127813dd6fSViresh Kumar
137813dd6fSViresh Kumar #include <linux/cpu.h>
147813dd6fSViresh Kumar #include <linux/errno.h>
157813dd6fSViresh Kumar #include <linux/device.h>
16cd6f0f51SRob Herring #include <linux/of.h>
173ba98324SViresh Kumar #include <linux/pm_domain.h>
187813dd6fSViresh Kumar #include <linux/slab.h>
197813dd6fSViresh Kumar #include <linux/export.h>
20a4f342b9SQuentin Perret #include <linux/energy_model.h>
217813dd6fSViresh Kumar
227813dd6fSViresh Kumar #include "opp.h"
237813dd6fSViresh Kumar
2464aaeb70SViresh Kumar /* OPP tables with uninitialized required OPPs, protected by opp_table_lock */
25167eb2bdSViresh Kumar static LIST_HEAD(lazy_opp_tables);
26167eb2bdSViresh Kumar
27f06ed90eSViresh Kumar /*
28f06ed90eSViresh Kumar * Returns opp descriptor node for a device node, caller must
29f06ed90eSViresh Kumar * do of_node_put().
30f06ed90eSViresh Kumar */
_opp_of_get_opp_desc_node(struct device_node * np,int index)31f06ed90eSViresh Kumar static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
32f06ed90eSViresh Kumar int index)
33f06ed90eSViresh Kumar {
34f06ed90eSViresh Kumar /* "operating-points-v2" can be an array for power domain providers */
35f06ed90eSViresh Kumar return of_parse_phandle(np, "operating-points-v2", index);
36f06ed90eSViresh Kumar }
37f06ed90eSViresh Kumar
38f06ed90eSViresh Kumar /* Returns opp descriptor node for a device, caller must do of_node_put() */
dev_pm_opp_of_get_opp_desc_node(struct device * dev)39f06ed90eSViresh Kumar struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
40f06ed90eSViresh Kumar {
41f06ed90eSViresh Kumar return _opp_of_get_opp_desc_node(dev->of_node, 0);
42f06ed90eSViresh Kumar }
43f06ed90eSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
44f06ed90eSViresh Kumar
_managed_opp(struct device * dev,int index)45283d55e6SViresh Kumar struct opp_table *_managed_opp(struct device *dev, int index)
467813dd6fSViresh Kumar {
477813dd6fSViresh Kumar struct opp_table *opp_table, *managed_table = NULL;
48283d55e6SViresh Kumar struct device_node *np;
497813dd6fSViresh Kumar
50283d55e6SViresh Kumar np = _opp_of_get_opp_desc_node(dev->of_node, index);
51283d55e6SViresh Kumar if (!np)
52283d55e6SViresh Kumar return NULL;
537813dd6fSViresh Kumar
547813dd6fSViresh Kumar list_for_each_entry(opp_table, &opp_tables, node) {
557813dd6fSViresh Kumar if (opp_table->np == np) {
567813dd6fSViresh Kumar /*
577813dd6fSViresh Kumar * Multiple devices can point to the same OPP table and
587813dd6fSViresh Kumar * so will have same node-pointer, np.
597813dd6fSViresh Kumar *
607813dd6fSViresh Kumar * But the OPPs will be considered as shared only if the
617813dd6fSViresh Kumar * OPP table contains a "opp-shared" property.
627813dd6fSViresh Kumar */
637813dd6fSViresh Kumar if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
647813dd6fSViresh Kumar _get_opp_table_kref(opp_table);
657813dd6fSViresh Kumar managed_table = opp_table;
667813dd6fSViresh Kumar }
677813dd6fSViresh Kumar
687813dd6fSViresh Kumar break;
697813dd6fSViresh Kumar }
707813dd6fSViresh Kumar }
717813dd6fSViresh Kumar
72283d55e6SViresh Kumar of_node_put(np);
737813dd6fSViresh Kumar
747813dd6fSViresh Kumar return managed_table;
757813dd6fSViresh Kumar }
767813dd6fSViresh Kumar
775d6d106fSViresh Kumar /* The caller must call dev_pm_opp_put() after the OPP is used */
_find_opp_of_np(struct opp_table * opp_table,struct device_node * opp_np)785d6d106fSViresh Kumar static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table,
795d6d106fSViresh Kumar struct device_node *opp_np)
805d6d106fSViresh Kumar {
815d6d106fSViresh Kumar struct dev_pm_opp *opp;
825d6d106fSViresh Kumar
835d6d106fSViresh Kumar mutex_lock(&opp_table->lock);
845d6d106fSViresh Kumar
855d6d106fSViresh Kumar list_for_each_entry(opp, &opp_table->opp_list, node) {
865d6d106fSViresh Kumar if (opp->np == opp_np) {
875d6d106fSViresh Kumar dev_pm_opp_get(opp);
885d6d106fSViresh Kumar mutex_unlock(&opp_table->lock);
895d6d106fSViresh Kumar return opp;
905d6d106fSViresh Kumar }
915d6d106fSViresh Kumar }
925d6d106fSViresh Kumar
935d6d106fSViresh Kumar mutex_unlock(&opp_table->lock);
945d6d106fSViresh Kumar
955d6d106fSViresh Kumar return NULL;
965d6d106fSViresh Kumar }
975d6d106fSViresh Kumar
of_parse_required_opp(struct device_node * np,int index)985d6d106fSViresh Kumar static struct device_node *of_parse_required_opp(struct device_node *np,
995d6d106fSViresh Kumar int index)
1005d6d106fSViresh Kumar {
101020d86fcSRajendra Nayak return of_parse_phandle(np, "required-opps", index);
1025d6d106fSViresh Kumar }
1035d6d106fSViresh Kumar
1045d6d106fSViresh Kumar /* The caller must call dev_pm_opp_put_opp_table() after the table is used */
_find_table_of_opp_np(struct device_node * opp_np)1055d6d106fSViresh Kumar static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
1065d6d106fSViresh Kumar {
1075d6d106fSViresh Kumar struct opp_table *opp_table;
108699e21e4SViresh Kumar struct device_node *opp_table_np;
1095d6d106fSViresh Kumar
110699e21e4SViresh Kumar opp_table_np = of_get_parent(opp_np);
111699e21e4SViresh Kumar if (!opp_table_np)
112699e21e4SViresh Kumar goto err;
113699e21e4SViresh Kumar
114699e21e4SViresh Kumar /* It is safe to put the node now as all we need now is its address */
115699e21e4SViresh Kumar of_node_put(opp_table_np);
116699e21e4SViresh Kumar
11727c09484SViresh Kumar mutex_lock(&opp_table_lock);
1185d6d106fSViresh Kumar list_for_each_entry(opp_table, &opp_tables, node) {
119699e21e4SViresh Kumar if (opp_table_np == opp_table->np) {
1205d6d106fSViresh Kumar _get_opp_table_kref(opp_table);
12127c09484SViresh Kumar mutex_unlock(&opp_table_lock);
1225d6d106fSViresh Kumar return opp_table;
1235d6d106fSViresh Kumar }
1245d6d106fSViresh Kumar }
12527c09484SViresh Kumar mutex_unlock(&opp_table_lock);
1265d6d106fSViresh Kumar
127699e21e4SViresh Kumar err:
1285d6d106fSViresh Kumar return ERR_PTR(-ENODEV);
1295d6d106fSViresh Kumar }
1305d6d106fSViresh Kumar
1315d6d106fSViresh Kumar /* Free resources previously acquired by _opp_table_alloc_required_tables() */
_opp_table_free_required_tables(struct opp_table * opp_table)1325d6d106fSViresh Kumar static void _opp_table_free_required_tables(struct opp_table *opp_table)
1335d6d106fSViresh Kumar {
1345d6d106fSViresh Kumar struct opp_table **required_opp_tables = opp_table->required_opp_tables;
1355d6d106fSViresh Kumar int i;
1365d6d106fSViresh Kumar
1375d6d106fSViresh Kumar if (!required_opp_tables)
1385d6d106fSViresh Kumar return;
1395d6d106fSViresh Kumar
1405d6d106fSViresh Kumar for (i = 0; i < opp_table->required_opp_count; i++) {
1415d6d106fSViresh Kumar if (IS_ERR_OR_NULL(required_opp_tables[i]))
1427eba0c76SViresh Kumar continue;
1435d6d106fSViresh Kumar
1445d6d106fSViresh Kumar dev_pm_opp_put_opp_table(required_opp_tables[i]);
1455d6d106fSViresh Kumar }
1465d6d106fSViresh Kumar
1475d6d106fSViresh Kumar kfree(required_opp_tables);
1485d6d106fSViresh Kumar
1495d6d106fSViresh Kumar opp_table->required_opp_count = 0;
1505d6d106fSViresh Kumar opp_table->required_opp_tables = NULL;
15164aaeb70SViresh Kumar
15264aaeb70SViresh Kumar mutex_lock(&opp_table_lock);
1537eba0c76SViresh Kumar list_del(&opp_table->lazy);
15464aaeb70SViresh Kumar mutex_unlock(&opp_table_lock);
1555d6d106fSViresh Kumar }
1565d6d106fSViresh Kumar
1575d6d106fSViresh Kumar /*
1585d6d106fSViresh Kumar * Populate all devices and opp tables which are part of "required-opps" list.
1595d6d106fSViresh Kumar * Checking only the first OPP node should be enough.
1605d6d106fSViresh Kumar */
_opp_table_alloc_required_tables(struct opp_table * opp_table,struct device * dev,struct device_node * opp_np)1615d6d106fSViresh Kumar static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
1625d6d106fSViresh Kumar struct device *dev,
1635d6d106fSViresh Kumar struct device_node *opp_np)
1645d6d106fSViresh Kumar {
1655d6d106fSViresh Kumar struct opp_table **required_opp_tables;
1665d6d106fSViresh Kumar struct device_node *required_np, *np;
1677eba0c76SViresh Kumar bool lazy = false;
168c0ab9e08SViresh Kumar int count, i;
1695d6d106fSViresh Kumar
1705d6d106fSViresh Kumar /* Traversing the first OPP node is all we need */
1715d6d106fSViresh Kumar np = of_get_next_available_child(opp_np, NULL);
1725d6d106fSViresh Kumar if (!np) {
1736ee70e8cSNicola Mazzucato dev_warn(dev, "Empty OPP table\n");
1746ee70e8cSNicola Mazzucato
1755d6d106fSViresh Kumar return;
1765d6d106fSViresh Kumar }
1775d6d106fSViresh Kumar
1785d6d106fSViresh Kumar count = of_count_phandle_with_args(np, "required-opps", NULL);
1798b7912f4SPavankumar Kondeti if (count <= 0)
1805d6d106fSViresh Kumar goto put_np;
1815d6d106fSViresh Kumar
1825d6d106fSViresh Kumar required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
1835d6d106fSViresh Kumar GFP_KERNEL);
184c0ab9e08SViresh Kumar if (!required_opp_tables)
1855d6d106fSViresh Kumar goto put_np;
1865d6d106fSViresh Kumar
1875d6d106fSViresh Kumar opp_table->required_opp_tables = required_opp_tables;
1885d6d106fSViresh Kumar opp_table->required_opp_count = count;
1895d6d106fSViresh Kumar
1905d6d106fSViresh Kumar for (i = 0; i < count; i++) {
1915d6d106fSViresh Kumar required_np = of_parse_required_opp(np, i);
1925d6d106fSViresh Kumar if (!required_np)
1935d6d106fSViresh Kumar goto free_required_tables;
1945d6d106fSViresh Kumar
1955d6d106fSViresh Kumar required_opp_tables[i] = _find_table_of_opp_np(required_np);
1965d6d106fSViresh Kumar of_node_put(required_np);
1975d6d106fSViresh Kumar
1984fa82a87SHsin-Yi Wang if (IS_ERR(required_opp_tables[i]))
1997eba0c76SViresh Kumar lazy = true;
2005d6d106fSViresh Kumar }
2015d6d106fSViresh Kumar
2027eba0c76SViresh Kumar /* Let's do the linking later on */
20364aaeb70SViresh Kumar if (lazy) {
20464aaeb70SViresh Kumar /*
20564aaeb70SViresh Kumar * The OPP table is not held while allocating the table, take it
20664aaeb70SViresh Kumar * now to avoid corruption to the lazy_opp_tables list.
20764aaeb70SViresh Kumar */
20864aaeb70SViresh Kumar mutex_lock(&opp_table_lock);
2097eba0c76SViresh Kumar list_add(&opp_table->lazy, &lazy_opp_tables);
21064aaeb70SViresh Kumar mutex_unlock(&opp_table_lock);
21164aaeb70SViresh Kumar }
212528f2d8dSViresh Kumar else
213528f2d8dSViresh Kumar _update_set_required_opps(opp_table);
2147eba0c76SViresh Kumar
2155d6d106fSViresh Kumar goto put_np;
2165d6d106fSViresh Kumar
2175d6d106fSViresh Kumar free_required_tables:
2185d6d106fSViresh Kumar _opp_table_free_required_tables(opp_table);
2195d6d106fSViresh Kumar put_np:
2205d6d106fSViresh Kumar of_node_put(np);
2215d6d106fSViresh Kumar }
2225d6d106fSViresh Kumar
_of_init_opp_table(struct opp_table * opp_table,struct device * dev,int index)223eb7c8743SViresh Kumar void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
224eb7c8743SViresh Kumar int index)
2257813dd6fSViresh Kumar {
226f06ed90eSViresh Kumar struct device_node *np, *opp_np;
227f06ed90eSViresh Kumar u32 val;
2287813dd6fSViresh Kumar
2297813dd6fSViresh Kumar /*
2307813dd6fSViresh Kumar * Only required for backward compatibility with v1 bindings, but isn't
2317813dd6fSViresh Kumar * harmful for other cases. And so we do it unconditionally.
2327813dd6fSViresh Kumar */
2337813dd6fSViresh Kumar np = of_node_get(dev->of_node);
234f06ed90eSViresh Kumar if (!np)
235f06ed90eSViresh Kumar return;
2367813dd6fSViresh Kumar
2377813dd6fSViresh Kumar if (!of_property_read_u32(np, "clock-latency", &val))
2387813dd6fSViresh Kumar opp_table->clock_latency_ns_max = val;
2397813dd6fSViresh Kumar of_property_read_u32(np, "voltage-tolerance",
2407813dd6fSViresh Kumar &opp_table->voltage_tolerance_v1);
241f06ed90eSViresh Kumar
242e9eadc28SRob Herring if (of_property_present(np, "#power-domain-cells"))
24361d8e7c7SViresh Kumar opp_table->is_genpd = true;
24461d8e7c7SViresh Kumar
245f06ed90eSViresh Kumar /* Get OPP table node */
246f06ed90eSViresh Kumar opp_np = _opp_of_get_opp_desc_node(np, index);
2477813dd6fSViresh Kumar of_node_put(np);
248f06ed90eSViresh Kumar
249f06ed90eSViresh Kumar if (!opp_np)
250f06ed90eSViresh Kumar return;
251f06ed90eSViresh Kumar
252f06ed90eSViresh Kumar if (of_property_read_bool(opp_np, "opp-shared"))
253f06ed90eSViresh Kumar opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
254f06ed90eSViresh Kumar else
255f06ed90eSViresh Kumar opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
256f06ed90eSViresh Kumar
257f06ed90eSViresh Kumar opp_table->np = opp_np;
258f06ed90eSViresh Kumar
2595d6d106fSViresh Kumar _opp_table_alloc_required_tables(opp_table, dev, opp_np);
2607813dd6fSViresh Kumar }
2617813dd6fSViresh Kumar
_of_clear_opp_table(struct opp_table * opp_table)2625d6d106fSViresh Kumar void _of_clear_opp_table(struct opp_table *opp_table)
2635d6d106fSViresh Kumar {
2645d6d106fSViresh Kumar _opp_table_free_required_tables(opp_table);
265ce736cf7SLiang He of_node_put(opp_table->np);
2665d6d106fSViresh Kumar }
2675d6d106fSViresh Kumar
268da544b61SViresh Kumar /*
269da544b61SViresh Kumar * Release all resources previously acquired with a call to
270da544b61SViresh Kumar * _of_opp_alloc_required_opps().
271da544b61SViresh Kumar */
_of_opp_free_required_opps(struct opp_table * opp_table,struct dev_pm_opp * opp)2723466ea2cSLiang He static void _of_opp_free_required_opps(struct opp_table *opp_table,
273da544b61SViresh Kumar struct dev_pm_opp *opp)
274da544b61SViresh Kumar {
275da544b61SViresh Kumar struct dev_pm_opp **required_opps = opp->required_opps;
276da544b61SViresh Kumar int i;
277da544b61SViresh Kumar
278da544b61SViresh Kumar if (!required_opps)
279da544b61SViresh Kumar return;
280da544b61SViresh Kumar
281da544b61SViresh Kumar for (i = 0; i < opp_table->required_opp_count; i++) {
282da544b61SViresh Kumar if (!required_opps[i])
2837eba0c76SViresh Kumar continue;
284da544b61SViresh Kumar
285da544b61SViresh Kumar /* Put the reference back */
286da544b61SViresh Kumar dev_pm_opp_put(required_opps[i]);
287da544b61SViresh Kumar }
288da544b61SViresh Kumar
289da544b61SViresh Kumar opp->required_opps = NULL;
2907eba0c76SViresh Kumar kfree(required_opps);
291da544b61SViresh Kumar }
292da544b61SViresh Kumar
_of_clear_opp(struct opp_table * opp_table,struct dev_pm_opp * opp)2933466ea2cSLiang He void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
2943466ea2cSLiang He {
2953466ea2cSLiang He _of_opp_free_required_opps(opp_table, opp);
2963466ea2cSLiang He of_node_put(opp->np);
2973466ea2cSLiang He }
2983466ea2cSLiang He
299da544b61SViresh Kumar /* Populate all required OPPs which are part of "required-opps" list */
_of_opp_alloc_required_opps(struct opp_table * opp_table,struct dev_pm_opp * opp)300da544b61SViresh Kumar static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
301da544b61SViresh Kumar struct dev_pm_opp *opp)
302da544b61SViresh Kumar {
303da544b61SViresh Kumar struct dev_pm_opp **required_opps;
304da544b61SViresh Kumar struct opp_table *required_table;
305da544b61SViresh Kumar struct device_node *np;
306da544b61SViresh Kumar int i, ret, count = opp_table->required_opp_count;
307da544b61SViresh Kumar
308da544b61SViresh Kumar if (!count)
309da544b61SViresh Kumar return 0;
310da544b61SViresh Kumar
311da544b61SViresh Kumar required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL);
312da544b61SViresh Kumar if (!required_opps)
313da544b61SViresh Kumar return -ENOMEM;
314da544b61SViresh Kumar
315da544b61SViresh Kumar opp->required_opps = required_opps;
316da544b61SViresh Kumar
317da544b61SViresh Kumar for (i = 0; i < count; i++) {
318da544b61SViresh Kumar required_table = opp_table->required_opp_tables[i];
319da544b61SViresh Kumar
3207eba0c76SViresh Kumar /* Required table not added yet, we will link later */
3217eba0c76SViresh Kumar if (IS_ERR_OR_NULL(required_table))
3227eba0c76SViresh Kumar continue;
3237eba0c76SViresh Kumar
324da544b61SViresh Kumar np = of_parse_required_opp(opp->np, i);
325da544b61SViresh Kumar if (unlikely(!np)) {
326da544b61SViresh Kumar ret = -ENODEV;
327da544b61SViresh Kumar goto free_required_opps;
328da544b61SViresh Kumar }
329da544b61SViresh Kumar
330da544b61SViresh Kumar required_opps[i] = _find_opp_of_np(required_table, np);
331da544b61SViresh Kumar of_node_put(np);
332da544b61SViresh Kumar
333da544b61SViresh Kumar if (!required_opps[i]) {
334da544b61SViresh Kumar pr_err("%s: Unable to find required OPP node: %pOF (%d)\n",
335da544b61SViresh Kumar __func__, opp->np, i);
336da544b61SViresh Kumar ret = -ENODEV;
337da544b61SViresh Kumar goto free_required_opps;
338da544b61SViresh Kumar }
339da544b61SViresh Kumar }
340da544b61SViresh Kumar
341da544b61SViresh Kumar return 0;
342da544b61SViresh Kumar
343da544b61SViresh Kumar free_required_opps:
344da544b61SViresh Kumar _of_opp_free_required_opps(opp_table, opp);
345da544b61SViresh Kumar
346da544b61SViresh Kumar return ret;
347da544b61SViresh Kumar }
348da544b61SViresh Kumar
3497eba0c76SViresh Kumar /* Link required OPPs for an individual OPP */
lazy_link_required_opps(struct opp_table * opp_table,struct opp_table * new_table,int index)3507eba0c76SViresh Kumar static int lazy_link_required_opps(struct opp_table *opp_table,
3517eba0c76SViresh Kumar struct opp_table *new_table, int index)
3527eba0c76SViresh Kumar {
3537eba0c76SViresh Kumar struct device_node *required_np;
3547eba0c76SViresh Kumar struct dev_pm_opp *opp;
3557eba0c76SViresh Kumar
3567eba0c76SViresh Kumar list_for_each_entry(opp, &opp_table->opp_list, node) {
3577eba0c76SViresh Kumar required_np = of_parse_required_opp(opp->np, index);
3587eba0c76SViresh Kumar if (unlikely(!required_np))
3597eba0c76SViresh Kumar return -ENODEV;
3607eba0c76SViresh Kumar
3617eba0c76SViresh Kumar opp->required_opps[index] = _find_opp_of_np(new_table, required_np);
3627eba0c76SViresh Kumar of_node_put(required_np);
3637eba0c76SViresh Kumar
3647eba0c76SViresh Kumar if (!opp->required_opps[index]) {
3657eba0c76SViresh Kumar pr_err("%s: Unable to find required OPP node: %pOF (%d)\n",
3667eba0c76SViresh Kumar __func__, opp->np, index);
3677eba0c76SViresh Kumar return -ENODEV;
3687eba0c76SViresh Kumar }
3697eba0c76SViresh Kumar }
3707eba0c76SViresh Kumar
3717eba0c76SViresh Kumar return 0;
3727eba0c76SViresh Kumar }
3737eba0c76SViresh Kumar
3747eba0c76SViresh Kumar /* Link required OPPs for all OPPs of the newly added OPP table */
lazy_link_required_opp_table(struct opp_table * new_table)3757eba0c76SViresh Kumar static void lazy_link_required_opp_table(struct opp_table *new_table)
3767eba0c76SViresh Kumar {
3777eba0c76SViresh Kumar struct opp_table *opp_table, *temp, **required_opp_tables;
3787eba0c76SViresh Kumar struct device_node *required_np, *opp_np, *required_table_np;
3797eba0c76SViresh Kumar struct dev_pm_opp *opp;
3807eba0c76SViresh Kumar int i, ret;
3817eba0c76SViresh Kumar
3827eba0c76SViresh Kumar mutex_lock(&opp_table_lock);
3837eba0c76SViresh Kumar
3847eba0c76SViresh Kumar list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) {
3857eba0c76SViresh Kumar bool lazy = false;
3867eba0c76SViresh Kumar
3877eba0c76SViresh Kumar /* opp_np can't be invalid here */
3887eba0c76SViresh Kumar opp_np = of_get_next_available_child(opp_table->np, NULL);
3897eba0c76SViresh Kumar
3907eba0c76SViresh Kumar for (i = 0; i < opp_table->required_opp_count; i++) {
3917eba0c76SViresh Kumar required_opp_tables = opp_table->required_opp_tables;
3927eba0c76SViresh Kumar
3937eba0c76SViresh Kumar /* Required opp-table is already parsed */
3947eba0c76SViresh Kumar if (!IS_ERR(required_opp_tables[i]))
3957eba0c76SViresh Kumar continue;
3967eba0c76SViresh Kumar
3977eba0c76SViresh Kumar /* required_np can't be invalid here */
3987eba0c76SViresh Kumar required_np = of_parse_required_opp(opp_np, i);
3997eba0c76SViresh Kumar required_table_np = of_get_parent(required_np);
4007eba0c76SViresh Kumar
4017eba0c76SViresh Kumar of_node_put(required_table_np);
4027eba0c76SViresh Kumar of_node_put(required_np);
4037eba0c76SViresh Kumar
4047eba0c76SViresh Kumar /*
4057eba0c76SViresh Kumar * Newly added table isn't the required opp-table for
4067eba0c76SViresh Kumar * opp_table.
4077eba0c76SViresh Kumar */
4087eba0c76SViresh Kumar if (required_table_np != new_table->np) {
4097eba0c76SViresh Kumar lazy = true;
4107eba0c76SViresh Kumar continue;
4117eba0c76SViresh Kumar }
4127eba0c76SViresh Kumar
4137eba0c76SViresh Kumar required_opp_tables[i] = new_table;
4147eba0c76SViresh Kumar _get_opp_table_kref(new_table);
4157eba0c76SViresh Kumar
4167eba0c76SViresh Kumar /* Link OPPs now */
4177eba0c76SViresh Kumar ret = lazy_link_required_opps(opp_table, new_table, i);
4187eba0c76SViresh Kumar if (ret) {
4197eba0c76SViresh Kumar /* The OPPs will be marked unusable */
4207eba0c76SViresh Kumar lazy = false;
4217eba0c76SViresh Kumar break;
4227eba0c76SViresh Kumar }
4237eba0c76SViresh Kumar }
4247eba0c76SViresh Kumar
4257eba0c76SViresh Kumar of_node_put(opp_np);
4267eba0c76SViresh Kumar
4277eba0c76SViresh Kumar /* All required opp-tables found, remove from lazy list */
4287eba0c76SViresh Kumar if (!lazy) {
429528f2d8dSViresh Kumar _update_set_required_opps(opp_table);
430ac9fd3c8SYang Yingliang list_del_init(&opp_table->lazy);
4317eba0c76SViresh Kumar
4327eba0c76SViresh Kumar list_for_each_entry(opp, &opp_table->opp_list, node)
4337eba0c76SViresh Kumar _required_opps_available(opp, opp_table->required_opp_count);
4347eba0c76SViresh Kumar }
4357eba0c76SViresh Kumar }
4367eba0c76SViresh Kumar
4377eba0c76SViresh Kumar mutex_unlock(&opp_table_lock);
4387eba0c76SViresh Kumar }
4397eba0c76SViresh Kumar
_bandwidth_supported(struct device * dev,struct opp_table * opp_table)44045679f9bSSibi Sankar static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table)
44145679f9bSSibi Sankar {
44245679f9bSSibi Sankar struct device_node *np, *opp_np;
44345679f9bSSibi Sankar struct property *prop;
44445679f9bSSibi Sankar
44545679f9bSSibi Sankar if (!opp_table) {
44645679f9bSSibi Sankar np = of_node_get(dev->of_node);
44745679f9bSSibi Sankar if (!np)
44845679f9bSSibi Sankar return -ENODEV;
44945679f9bSSibi Sankar
45045679f9bSSibi Sankar opp_np = _opp_of_get_opp_desc_node(np, 0);
45145679f9bSSibi Sankar of_node_put(np);
45245679f9bSSibi Sankar } else {
45345679f9bSSibi Sankar opp_np = of_node_get(opp_table->np);
45445679f9bSSibi Sankar }
45545679f9bSSibi Sankar
45645679f9bSSibi Sankar /* Lets not fail in case we are parsing opp-v1 bindings */
45745679f9bSSibi Sankar if (!opp_np)
45845679f9bSSibi Sankar return 0;
45945679f9bSSibi Sankar
46045679f9bSSibi Sankar /* Checking only first OPP is sufficient */
46145679f9bSSibi Sankar np = of_get_next_available_child(opp_np, NULL);
462907ed123SDan Carpenter of_node_put(opp_np);
46345679f9bSSibi Sankar if (!np) {
46445679f9bSSibi Sankar dev_err(dev, "OPP table empty\n");
46545679f9bSSibi Sankar return -EINVAL;
46645679f9bSSibi Sankar }
46745679f9bSSibi Sankar
46845679f9bSSibi Sankar prop = of_find_property(np, "opp-peak-kBps", NULL);
46945679f9bSSibi Sankar of_node_put(np);
47045679f9bSSibi Sankar
47145679f9bSSibi Sankar if (!prop || !prop->length)
47245679f9bSSibi Sankar return 0;
47345679f9bSSibi Sankar
47445679f9bSSibi Sankar return 1;
47545679f9bSSibi Sankar }
47645679f9bSSibi Sankar
dev_pm_opp_of_find_icc_paths(struct device * dev,struct opp_table * opp_table)4776d3f922cSGeorgi Djakov int dev_pm_opp_of_find_icc_paths(struct device *dev,
4786d3f922cSGeorgi Djakov struct opp_table *opp_table)
4796d3f922cSGeorgi Djakov {
4806d3f922cSGeorgi Djakov struct device_node *np;
48145679f9bSSibi Sankar int ret, i, count, num_paths;
4826d3f922cSGeorgi Djakov struct icc_path **paths;
4836d3f922cSGeorgi Djakov
48445679f9bSSibi Sankar ret = _bandwidth_supported(dev, opp_table);
4856ee70e8cSNicola Mazzucato if (ret == -EINVAL)
4866ee70e8cSNicola Mazzucato return 0; /* Empty OPP table is a valid corner-case, let's not fail */
4876ee70e8cSNicola Mazzucato else if (ret <= 0)
48845679f9bSSibi Sankar return ret;
48945679f9bSSibi Sankar
49045679f9bSSibi Sankar ret = 0;
49145679f9bSSibi Sankar
4926d3f922cSGeorgi Djakov np = of_node_get(dev->of_node);
4936d3f922cSGeorgi Djakov if (!np)
4946d3f922cSGeorgi Djakov return 0;
4956d3f922cSGeorgi Djakov
4966d3f922cSGeorgi Djakov count = of_count_phandle_with_args(np, "interconnects",
4976d3f922cSGeorgi Djakov "#interconnect-cells");
4986d3f922cSGeorgi Djakov of_node_put(np);
4996d3f922cSGeorgi Djakov if (count < 0)
5006d3f922cSGeorgi Djakov return 0;
5016d3f922cSGeorgi Djakov
5026d3f922cSGeorgi Djakov /* two phandles when #interconnect-cells = <1> */
5036d3f922cSGeorgi Djakov if (count % 2) {
5046d3f922cSGeorgi Djakov dev_err(dev, "%s: Invalid interconnects values\n", __func__);
5056d3f922cSGeorgi Djakov return -EINVAL;
5066d3f922cSGeorgi Djakov }
5076d3f922cSGeorgi Djakov
5086d3f922cSGeorgi Djakov num_paths = count / 2;
5096d3f922cSGeorgi Djakov paths = kcalloc(num_paths, sizeof(*paths), GFP_KERNEL);
5106d3f922cSGeorgi Djakov if (!paths)
5116d3f922cSGeorgi Djakov return -ENOMEM;
5126d3f922cSGeorgi Djakov
5136d3f922cSGeorgi Djakov for (i = 0; i < num_paths; i++) {
5146d3f922cSGeorgi Djakov paths[i] = of_icc_get_by_index(dev, i);
5156d3f922cSGeorgi Djakov if (IS_ERR(paths[i])) {
5165fb2864cSAndrew Halaney ret = dev_err_probe(dev, PTR_ERR(paths[i]), "%s: Unable to get path%d\n", __func__, i);
5176d3f922cSGeorgi Djakov goto err;
5186d3f922cSGeorgi Djakov }
5196d3f922cSGeorgi Djakov }
5206d3f922cSGeorgi Djakov
5216d3f922cSGeorgi Djakov if (opp_table) {
5226d3f922cSGeorgi Djakov opp_table->paths = paths;
5236d3f922cSGeorgi Djakov opp_table->path_count = num_paths;
5246d3f922cSGeorgi Djakov return 0;
5256d3f922cSGeorgi Djakov }
5266d3f922cSGeorgi Djakov
5276d3f922cSGeorgi Djakov err:
5286d3f922cSGeorgi Djakov while (i--)
5296d3f922cSGeorgi Djakov icc_put(paths[i]);
5306d3f922cSGeorgi Djakov
5316d3f922cSGeorgi Djakov kfree(paths);
5326d3f922cSGeorgi Djakov
5336d3f922cSGeorgi Djakov return ret;
5346d3f922cSGeorgi Djakov }
5356d3f922cSGeorgi Djakov EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths);
5366d3f922cSGeorgi Djakov
_opp_is_supported(struct device * dev,struct opp_table * opp_table,struct device_node * np)5377813dd6fSViresh Kumar static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
5387813dd6fSViresh Kumar struct device_node *np)
5397813dd6fSViresh Kumar {
5400ff25c99SViresh Kumar unsigned int levels = opp_table->supported_hw_count;
5410ff25c99SViresh Kumar int count, versions, ret, i, j;
5420ff25c99SViresh Kumar u32 val;
5437813dd6fSViresh Kumar
5447813dd6fSViresh Kumar if (!opp_table->supported_hw) {
5457813dd6fSViresh Kumar /*
5467813dd6fSViresh Kumar * In the case that no supported_hw has been set by the
5477813dd6fSViresh Kumar * platform but there is an opp-supported-hw value set for
5487813dd6fSViresh Kumar * an OPP then the OPP should not be enabled as there is
5497813dd6fSViresh Kumar * no way to see if the hardware supports it.
5507813dd6fSViresh Kumar */
551e9eadc28SRob Herring if (of_property_present(np, "opp-supported-hw"))
5527813dd6fSViresh Kumar return false;
5537813dd6fSViresh Kumar else
5547813dd6fSViresh Kumar return true;
5557813dd6fSViresh Kumar }
5567813dd6fSViresh Kumar
5570ff25c99SViresh Kumar count = of_property_count_u32_elems(np, "opp-supported-hw");
5580ff25c99SViresh Kumar if (count <= 0 || count % levels) {
5590ff25c99SViresh Kumar dev_err(dev, "%s: Invalid opp-supported-hw property (%d)\n",
5600ff25c99SViresh Kumar __func__, count);
5610ff25c99SViresh Kumar return false;
5620ff25c99SViresh Kumar }
5630ff25c99SViresh Kumar
5640ff25c99SViresh Kumar versions = count / levels;
5650ff25c99SViresh Kumar
5660ff25c99SViresh Kumar /* All levels in at least one of the versions should match */
5670ff25c99SViresh Kumar for (i = 0; i < versions; i++) {
5680ff25c99SViresh Kumar bool supported = true;
5690ff25c99SViresh Kumar
5700ff25c99SViresh Kumar for (j = 0; j < levels; j++) {
5710ff25c99SViresh Kumar ret = of_property_read_u32_index(np, "opp-supported-hw",
5720ff25c99SViresh Kumar i * levels + j, &val);
5737813dd6fSViresh Kumar if (ret) {
5747813dd6fSViresh Kumar dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
5750ff25c99SViresh Kumar __func__, i * levels + j, ret);
5767813dd6fSViresh Kumar return false;
5777813dd6fSViresh Kumar }
5787813dd6fSViresh Kumar
5790ff25c99SViresh Kumar /* Check if the level is supported */
5800ff25c99SViresh Kumar if (!(val & opp_table->supported_hw[j])) {
5810ff25c99SViresh Kumar supported = false;
5820ff25c99SViresh Kumar break;
5830ff25c99SViresh Kumar }
5847813dd6fSViresh Kumar }
5857813dd6fSViresh Kumar
5860ff25c99SViresh Kumar if (supported)
5877813dd6fSViresh Kumar return true;
5887813dd6fSViresh Kumar }
5897813dd6fSViresh Kumar
5900ff25c99SViresh Kumar return false;
5910ff25c99SViresh Kumar }
5920ff25c99SViresh Kumar
_parse_named_prop(struct dev_pm_opp * opp,struct device * dev,struct opp_table * opp_table,const char * prop_type,bool * triplet)593e5acb199SViresh Kumar static u32 *_parse_named_prop(struct dev_pm_opp *opp, struct device *dev,
594e5acb199SViresh Kumar struct opp_table *opp_table,
595e5acb199SViresh Kumar const char *prop_type, bool *triplet)
596e5acb199SViresh Kumar {
597e5acb199SViresh Kumar struct property *prop = NULL;
598e5acb199SViresh Kumar char name[NAME_MAX];
599e5acb199SViresh Kumar int count, ret;
600e5acb199SViresh Kumar u32 *out;
601e5acb199SViresh Kumar
602e5acb199SViresh Kumar /* Search for "opp-<prop_type>-<name>" */
603e5acb199SViresh Kumar if (opp_table->prop_name) {
604e5acb199SViresh Kumar snprintf(name, sizeof(name), "opp-%s-%s", prop_type,
605e5acb199SViresh Kumar opp_table->prop_name);
606e5acb199SViresh Kumar prop = of_find_property(opp->np, name, NULL);
607e5acb199SViresh Kumar }
608e5acb199SViresh Kumar
609e5acb199SViresh Kumar if (!prop) {
610e5acb199SViresh Kumar /* Search for "opp-<prop_type>" */
611e5acb199SViresh Kumar snprintf(name, sizeof(name), "opp-%s", prop_type);
612e5acb199SViresh Kumar prop = of_find_property(opp->np, name, NULL);
613e5acb199SViresh Kumar if (!prop)
614e5acb199SViresh Kumar return NULL;
615e5acb199SViresh Kumar }
616e5acb199SViresh Kumar
617e5acb199SViresh Kumar count = of_property_count_u32_elems(opp->np, name);
618e5acb199SViresh Kumar if (count < 0) {
619e5acb199SViresh Kumar dev_err(dev, "%s: Invalid %s property (%d)\n", __func__, name,
620e5acb199SViresh Kumar count);
621e5acb199SViresh Kumar return ERR_PTR(count);
622e5acb199SViresh Kumar }
623e5acb199SViresh Kumar
624e5acb199SViresh Kumar /*
625e5acb199SViresh Kumar * Initialize regulator_count, if regulator information isn't provided
626e5acb199SViresh Kumar * by the platform. Now that one of the properties is available, fix the
627e5acb199SViresh Kumar * regulator_count to 1.
628e5acb199SViresh Kumar */
629e5acb199SViresh Kumar if (unlikely(opp_table->regulator_count == -1))
630e5acb199SViresh Kumar opp_table->regulator_count = 1;
631e5acb199SViresh Kumar
632e5acb199SViresh Kumar if (count != opp_table->regulator_count &&
633e5acb199SViresh Kumar (!triplet || count != opp_table->regulator_count * 3)) {
634e5acb199SViresh Kumar dev_err(dev, "%s: Invalid number of elements in %s property (%u) with supplies (%d)\n",
635e5acb199SViresh Kumar __func__, prop_type, count, opp_table->regulator_count);
636e5acb199SViresh Kumar return ERR_PTR(-EINVAL);
637e5acb199SViresh Kumar }
638e5acb199SViresh Kumar
639e5acb199SViresh Kumar out = kmalloc_array(count, sizeof(*out), GFP_KERNEL);
640e5acb199SViresh Kumar if (!out)
641e5acb199SViresh Kumar return ERR_PTR(-EINVAL);
642e5acb199SViresh Kumar
643e5acb199SViresh Kumar ret = of_property_read_u32_array(opp->np, name, out, count);
644e5acb199SViresh Kumar if (ret) {
645e5acb199SViresh Kumar dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
646e5acb199SViresh Kumar kfree(out);
647e5acb199SViresh Kumar return ERR_PTR(-EINVAL);
648e5acb199SViresh Kumar }
649e5acb199SViresh Kumar
650e5acb199SViresh Kumar if (triplet)
651e5acb199SViresh Kumar *triplet = count != opp_table->regulator_count;
652e5acb199SViresh Kumar
653e5acb199SViresh Kumar return out;
654e5acb199SViresh Kumar }
655e5acb199SViresh Kumar
opp_parse_microvolt(struct dev_pm_opp * opp,struct device * dev,struct opp_table * opp_table,bool * triplet)656e5acb199SViresh Kumar static u32 *opp_parse_microvolt(struct dev_pm_opp *opp, struct device *dev,
657e5acb199SViresh Kumar struct opp_table *opp_table, bool *triplet)
658e5acb199SViresh Kumar {
659e5acb199SViresh Kumar u32 *microvolt;
660e5acb199SViresh Kumar
661e5acb199SViresh Kumar microvolt = _parse_named_prop(opp, dev, opp_table, "microvolt", triplet);
662e5acb199SViresh Kumar if (IS_ERR(microvolt))
663e5acb199SViresh Kumar return microvolt;
664e5acb199SViresh Kumar
665e5acb199SViresh Kumar if (!microvolt) {
666e5acb199SViresh Kumar /*
667e5acb199SViresh Kumar * Missing property isn't a problem, but an invalid
668e5acb199SViresh Kumar * entry is. This property isn't optional if regulator
6692eedf62eSJames Calligeros * information is provided. Check only for the first OPP, as
6702eedf62eSJames Calligeros * regulator_count may get initialized after that to a valid
6712eedf62eSJames Calligeros * value.
672e5acb199SViresh Kumar */
6732eedf62eSJames Calligeros if (list_empty(&opp_table->opp_list) &&
6742eedf62eSJames Calligeros opp_table->regulator_count > 0) {
675e5acb199SViresh Kumar dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators\n",
676e5acb199SViresh Kumar __func__);
677e5acb199SViresh Kumar return ERR_PTR(-EINVAL);
678e5acb199SViresh Kumar }
679e5acb199SViresh Kumar }
680e5acb199SViresh Kumar
681e5acb199SViresh Kumar return microvolt;
682e5acb199SViresh Kumar }
683e5acb199SViresh Kumar
opp_parse_supplies(struct dev_pm_opp * opp,struct device * dev,struct opp_table * opp_table)6847813dd6fSViresh Kumar static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
6857813dd6fSViresh Kumar struct opp_table *opp_table)
6867813dd6fSViresh Kumar {
687e5acb199SViresh Kumar u32 *microvolt, *microamp, *microwatt;
688e5acb199SViresh Kumar int ret = 0, i, j;
689e5acb199SViresh Kumar bool triplet;
6907813dd6fSViresh Kumar
691e5acb199SViresh Kumar microvolt = opp_parse_microvolt(opp, dev, opp_table, &triplet);
6922eedf62eSJames Calligeros if (IS_ERR(microvolt))
693e5acb199SViresh Kumar return PTR_ERR(microvolt);
6947813dd6fSViresh Kumar
695e5acb199SViresh Kumar microamp = _parse_named_prop(opp, dev, opp_table, "microamp", NULL);
696e5acb199SViresh Kumar if (IS_ERR(microamp)) {
697e5acb199SViresh Kumar ret = PTR_ERR(microamp);
6987813dd6fSViresh Kumar goto free_microvolt;
6997813dd6fSViresh Kumar }
7007813dd6fSViresh Kumar
701e5acb199SViresh Kumar microwatt = _parse_named_prop(opp, dev, opp_table, "microwatt", NULL);
702e5acb199SViresh Kumar if (IS_ERR(microwatt)) {
703e5acb199SViresh Kumar ret = PTR_ERR(microwatt);
7044f9a7a1dSLukasz Luba goto free_microamp;
7054f9a7a1dSLukasz Luba }
7064f9a7a1dSLukasz Luba
7072eedf62eSJames Calligeros /*
7082eedf62eSJames Calligeros * Initialize regulator_count if it is uninitialized and no properties
7092eedf62eSJames Calligeros * are found.
7102eedf62eSJames Calligeros */
7112eedf62eSJames Calligeros if (unlikely(opp_table->regulator_count == -1)) {
7122eedf62eSJames Calligeros opp_table->regulator_count = 0;
7132eedf62eSJames Calligeros return 0;
7142eedf62eSJames Calligeros }
7152eedf62eSJames Calligeros
716e5acb199SViresh Kumar for (i = 0, j = 0; i < opp_table->regulator_count; i++) {
7172eedf62eSJames Calligeros if (microvolt) {
7187813dd6fSViresh Kumar opp->supplies[i].u_volt = microvolt[j++];
7197813dd6fSViresh Kumar
720e5acb199SViresh Kumar if (triplet) {
7217813dd6fSViresh Kumar opp->supplies[i].u_volt_min = microvolt[j++];
7227813dd6fSViresh Kumar opp->supplies[i].u_volt_max = microvolt[j++];
723e5acb199SViresh Kumar } else {
724e5acb199SViresh Kumar opp->supplies[i].u_volt_min = opp->supplies[i].u_volt;
725e5acb199SViresh Kumar opp->supplies[i].u_volt_max = opp->supplies[i].u_volt;
7267813dd6fSViresh Kumar }
7272eedf62eSJames Calligeros }
7287813dd6fSViresh Kumar
7297813dd6fSViresh Kumar if (microamp)
7307813dd6fSViresh Kumar opp->supplies[i].u_amp = microamp[i];
7314f9a7a1dSLukasz Luba
7324f9a7a1dSLukasz Luba if (microwatt)
7334f9a7a1dSLukasz Luba opp->supplies[i].u_watt = microwatt[i];
7347813dd6fSViresh Kumar }
7357813dd6fSViresh Kumar
7364f9a7a1dSLukasz Luba kfree(microwatt);
7377813dd6fSViresh Kumar free_microamp:
7387813dd6fSViresh Kumar kfree(microamp);
7397813dd6fSViresh Kumar free_microvolt:
7407813dd6fSViresh Kumar kfree(microvolt);
7417813dd6fSViresh Kumar
7427813dd6fSViresh Kumar return ret;
7437813dd6fSViresh Kumar }
7447813dd6fSViresh Kumar
7457813dd6fSViresh Kumar /**
7467813dd6fSViresh Kumar * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT
7477813dd6fSViresh Kumar * entries
7487813dd6fSViresh Kumar * @dev: device pointer used to lookup OPP table.
7497813dd6fSViresh Kumar *
7507813dd6fSViresh Kumar * Free OPPs created using static entries present in DT.
7517813dd6fSViresh Kumar */
dev_pm_opp_of_remove_table(struct device * dev)7527813dd6fSViresh Kumar void dev_pm_opp_of_remove_table(struct device *dev)
7537813dd6fSViresh Kumar {
7548aaf6264SViresh Kumar dev_pm_opp_remove_table(dev);
7557813dd6fSViresh Kumar }
7567813dd6fSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
7577813dd6fSViresh Kumar
_read_rate(struct dev_pm_opp * new_opp,struct opp_table * opp_table,struct device_node * np)7582083da24SViresh Kumar static int _read_rate(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
7592083da24SViresh Kumar struct device_node *np)
7602083da24SViresh Kumar {
7612083da24SViresh Kumar struct property *prop;
7622083da24SViresh Kumar int i, count, ret;
7632083da24SViresh Kumar u64 *rates;
7642083da24SViresh Kumar
7652083da24SViresh Kumar prop = of_find_property(np, "opp-hz", NULL);
7662083da24SViresh Kumar if (!prop)
7672083da24SViresh Kumar return -ENODEV;
7682083da24SViresh Kumar
7692083da24SViresh Kumar count = prop->length / sizeof(u64);
7702083da24SViresh Kumar if (opp_table->clk_count != count) {
7712083da24SViresh Kumar pr_err("%s: Count mismatch between opp-hz and clk_count (%d %d)\n",
7722083da24SViresh Kumar __func__, count, opp_table->clk_count);
7732083da24SViresh Kumar return -EINVAL;
7742083da24SViresh Kumar }
7752083da24SViresh Kumar
7762083da24SViresh Kumar rates = kmalloc_array(count, sizeof(*rates), GFP_KERNEL);
7772083da24SViresh Kumar if (!rates)
7782083da24SViresh Kumar return -ENOMEM;
7792083da24SViresh Kumar
7802083da24SViresh Kumar ret = of_property_read_u64_array(np, "opp-hz", rates, count);
7812083da24SViresh Kumar if (ret) {
7822083da24SViresh Kumar pr_err("%s: Error parsing opp-hz: %d\n", __func__, ret);
7832083da24SViresh Kumar } else {
7842083da24SViresh Kumar /*
7852083da24SViresh Kumar * Rate is defined as an unsigned long in clk API, and so
7862083da24SViresh Kumar * casting explicitly to its type. Must be fixed once rate is 64
7872083da24SViresh Kumar * bit guaranteed in clk API.
7882083da24SViresh Kumar */
7892083da24SViresh Kumar for (i = 0; i < count; i++) {
7902083da24SViresh Kumar new_opp->rates[i] = (unsigned long)rates[i];
7912083da24SViresh Kumar
7922083da24SViresh Kumar /* This will happen for frequencies > 4.29 GHz */
7932083da24SViresh Kumar WARN_ON(new_opp->rates[i] != rates[i]);
7942083da24SViresh Kumar }
7952083da24SViresh Kumar }
7962083da24SViresh Kumar
7972083da24SViresh Kumar kfree(rates);
7982083da24SViresh Kumar
7992083da24SViresh Kumar return ret;
8002083da24SViresh Kumar }
8012083da24SViresh Kumar
_read_bw(struct dev_pm_opp * new_opp,struct opp_table * opp_table,struct device_node * np,bool peak)802d6134583SViresh Kumar static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *opp_table,
803120e117bSGeorgi Djakov struct device_node *np, bool peak)
8046d3f922cSGeorgi Djakov {
8056d3f922cSGeorgi Djakov const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps";
8066d3f922cSGeorgi Djakov struct property *prop;
8076d3f922cSGeorgi Djakov int i, count, ret;
8086d3f922cSGeorgi Djakov u32 *bw;
8096d3f922cSGeorgi Djakov
8106d3f922cSGeorgi Djakov prop = of_find_property(np, name, NULL);
8116d3f922cSGeorgi Djakov if (!prop)
8126d3f922cSGeorgi Djakov return -ENODEV;
8136d3f922cSGeorgi Djakov
8146d3f922cSGeorgi Djakov count = prop->length / sizeof(u32);
815d6134583SViresh Kumar if (opp_table->path_count != count) {
816120e117bSGeorgi Djakov pr_err("%s: Mismatch between %s and paths (%d %d)\n",
817d6134583SViresh Kumar __func__, name, count, opp_table->path_count);
818120e117bSGeorgi Djakov return -EINVAL;
819120e117bSGeorgi Djakov }
820120e117bSGeorgi Djakov
8216d3f922cSGeorgi Djakov bw = kmalloc_array(count, sizeof(*bw), GFP_KERNEL);
8226d3f922cSGeorgi Djakov if (!bw)
8236d3f922cSGeorgi Djakov return -ENOMEM;
8246d3f922cSGeorgi Djakov
8256d3f922cSGeorgi Djakov ret = of_property_read_u32_array(np, name, bw, count);
8266d3f922cSGeorgi Djakov if (ret) {
8276d3f922cSGeorgi Djakov pr_err("%s: Error parsing %s: %d\n", __func__, name, ret);
8286d3f922cSGeorgi Djakov goto out;
8296d3f922cSGeorgi Djakov }
8306d3f922cSGeorgi Djakov
8316d3f922cSGeorgi Djakov for (i = 0; i < count; i++) {
8326d3f922cSGeorgi Djakov if (peak)
8336d3f922cSGeorgi Djakov new_opp->bandwidth[i].peak = kBps_to_icc(bw[i]);
8346d3f922cSGeorgi Djakov else
8356d3f922cSGeorgi Djakov new_opp->bandwidth[i].avg = kBps_to_icc(bw[i]);
8366d3f922cSGeorgi Djakov }
8376d3f922cSGeorgi Djakov
8386d3f922cSGeorgi Djakov out:
8396d3f922cSGeorgi Djakov kfree(bw);
8406d3f922cSGeorgi Djakov return ret;
8416d3f922cSGeorgi Djakov }
8426d3f922cSGeorgi Djakov
_read_opp_key(struct dev_pm_opp * new_opp,struct opp_table * opp_table,struct device_node * np)8434768914bSViresh Kumar static int _read_opp_key(struct dev_pm_opp *new_opp,
8444768914bSViresh Kumar struct opp_table *opp_table, struct device_node *np)
8456c591eecSSaravana Kannan {
8466d3f922cSGeorgi Djakov bool found = false;
8476c591eecSSaravana Kannan int ret;
8486c591eecSSaravana Kannan
8492083da24SViresh Kumar ret = _read_rate(new_opp, opp_table, np);
8502083da24SViresh Kumar if (!ret)
8516d3f922cSGeorgi Djakov found = true;
8522083da24SViresh Kumar else if (ret != -ENODEV)
8532083da24SViresh Kumar return ret;
8546c591eecSSaravana Kannan
8556d3f922cSGeorgi Djakov /*
8566d3f922cSGeorgi Djakov * Bandwidth consists of peak and average (optional) values:
8576d3f922cSGeorgi Djakov * opp-peak-kBps = <path1_value path2_value>;
8586d3f922cSGeorgi Djakov * opp-avg-kBps = <path1_value path2_value>;
8596d3f922cSGeorgi Djakov */
860d6134583SViresh Kumar ret = _read_bw(new_opp, opp_table, np, true);
8616d3f922cSGeorgi Djakov if (!ret) {
8626d3f922cSGeorgi Djakov found = true;
863d6134583SViresh Kumar ret = _read_bw(new_opp, opp_table, np, false);
8646d3f922cSGeorgi Djakov }
8656d3f922cSGeorgi Djakov
8666d3f922cSGeorgi Djakov /* The properties were found but we failed to parse them */
8676d3f922cSGeorgi Djakov if (ret && ret != -ENODEV)
8686d3f922cSGeorgi Djakov return ret;
8696d3f922cSGeorgi Djakov
8706d3f922cSGeorgi Djakov if (!of_property_read_u32(np, "opp-level", &new_opp->level))
8716d3f922cSGeorgi Djakov found = true;
8726d3f922cSGeorgi Djakov
8736d3f922cSGeorgi Djakov if (found)
8746d3f922cSGeorgi Djakov return 0;
8756c591eecSSaravana Kannan
8766c591eecSSaravana Kannan return ret;
8776c591eecSSaravana Kannan }
8786c591eecSSaravana Kannan
8797813dd6fSViresh Kumar /**
8807813dd6fSViresh Kumar * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
8817813dd6fSViresh Kumar * @opp_table: OPP table
8827813dd6fSViresh Kumar * @dev: device for which we do this operation
8837813dd6fSViresh Kumar * @np: device node
8847813dd6fSViresh Kumar *
8857813dd6fSViresh Kumar * This function adds an opp definition to the opp table and returns status. The
8867813dd6fSViresh Kumar * opp can be controlled using dev_pm_opp_enable/disable functions and may be
8877813dd6fSViresh Kumar * removed by dev_pm_opp_remove.
8887813dd6fSViresh Kumar *
8897813dd6fSViresh Kumar * Return:
890deac8703SDave Gerlach * Valid OPP pointer:
891deac8703SDave Gerlach * On success
892deac8703SDave Gerlach * NULL:
8937813dd6fSViresh Kumar * Duplicate OPPs (both freq and volt are same) and opp->available
894deac8703SDave Gerlach * OR if the OPP is not supported by hardware.
895deac8703SDave Gerlach * ERR_PTR(-EEXIST):
896deac8703SDave Gerlach * Freq are same and volt are different OR
8977813dd6fSViresh Kumar * Duplicate OPPs (both freq and volt are same) and !opp->available
898deac8703SDave Gerlach * ERR_PTR(-ENOMEM):
899deac8703SDave Gerlach * Memory allocation failure
900deac8703SDave Gerlach * ERR_PTR(-EINVAL):
901deac8703SDave Gerlach * Failed parsing the OPP node
9027813dd6fSViresh Kumar */
_opp_add_static_v2(struct opp_table * opp_table,struct device * dev,struct device_node * np)903deac8703SDave Gerlach static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
904deac8703SDave Gerlach struct device *dev, struct device_node *np)
9057813dd6fSViresh Kumar {
9067813dd6fSViresh Kumar struct dev_pm_opp *new_opp;
9077813dd6fSViresh Kumar u32 val;
9087813dd6fSViresh Kumar int ret;
9097813dd6fSViresh Kumar
9107813dd6fSViresh Kumar new_opp = _opp_allocate(opp_table);
9117813dd6fSViresh Kumar if (!new_opp)
912deac8703SDave Gerlach return ERR_PTR(-ENOMEM);
9137813dd6fSViresh Kumar
9144768914bSViresh Kumar ret = _read_opp_key(new_opp, opp_table, np);
9154fa82a87SHsin-Yi Wang if (ret < 0) {
9166c591eecSSaravana Kannan dev_err(dev, "%s: opp key field not found\n", __func__);
9177813dd6fSViresh Kumar goto free_opp;
9187813dd6fSViresh Kumar }
9197813dd6fSViresh Kumar
9207813dd6fSViresh Kumar /* Check if the OPP supports hardware's hierarchy of versions or not */
9217813dd6fSViresh Kumar if (!_opp_is_supported(dev, opp_table, np)) {
9222083da24SViresh Kumar dev_dbg(dev, "OPP not supported by hardware: %s\n",
9232083da24SViresh Kumar of_node_full_name(np));
9247813dd6fSViresh Kumar goto free_opp;
9257813dd6fSViresh Kumar }
9267813dd6fSViresh Kumar
9277813dd6fSViresh Kumar new_opp->turbo = of_property_read_bool(np, "turbo-mode");
9287813dd6fSViresh Kumar
9293466ea2cSLiang He new_opp->np = of_node_get(np);
9307813dd6fSViresh Kumar new_opp->dynamic = false;
9317813dd6fSViresh Kumar new_opp->available = true;
9327813dd6fSViresh Kumar
933da544b61SViresh Kumar ret = _of_opp_alloc_required_opps(opp_table, new_opp);
934da544b61SViresh Kumar if (ret)
935*7ec98ebbSJoe Hattori goto put_node;
936da544b61SViresh Kumar
9377813dd6fSViresh Kumar if (!of_property_read_u32(np, "clock-latency-ns", &val))
9387813dd6fSViresh Kumar new_opp->clock_latency_ns = val;
9397813dd6fSViresh Kumar
9407813dd6fSViresh Kumar ret = opp_parse_supplies(new_opp, dev, opp_table);
9417813dd6fSViresh Kumar if (ret)
942da544b61SViresh Kumar goto free_required_opps;
9437813dd6fSViresh Kumar
9444768914bSViresh Kumar ret = _opp_add(dev, new_opp, opp_table);
9457813dd6fSViresh Kumar if (ret) {
9467813dd6fSViresh Kumar /* Don't return error for duplicate OPPs */
9477813dd6fSViresh Kumar if (ret == -EBUSY)
9487813dd6fSViresh Kumar ret = 0;
949da544b61SViresh Kumar goto free_required_opps;
9507813dd6fSViresh Kumar }
9517813dd6fSViresh Kumar
9527813dd6fSViresh Kumar /* OPP to select on device suspend */
9537813dd6fSViresh Kumar if (of_property_read_bool(np, "opp-suspend")) {
9547813dd6fSViresh Kumar if (opp_table->suspend_opp) {
9558bdac14bSViresh Kumar /* Pick the OPP with higher rate/bw/level as suspend OPP */
9562083da24SViresh Kumar if (_opp_compare_key(opp_table, new_opp, opp_table->suspend_opp) == 1) {
95745275517SAnson Huang opp_table->suspend_opp->suspend = false;
95845275517SAnson Huang new_opp->suspend = true;
95945275517SAnson Huang opp_table->suspend_opp = new_opp;
96045275517SAnson Huang }
9617813dd6fSViresh Kumar } else {
9627813dd6fSViresh Kumar new_opp->suspend = true;
9637813dd6fSViresh Kumar opp_table->suspend_opp = new_opp;
9647813dd6fSViresh Kumar }
9657813dd6fSViresh Kumar }
9667813dd6fSViresh Kumar
9677813dd6fSViresh Kumar if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
9687813dd6fSViresh Kumar opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
9697813dd6fSViresh Kumar
970b6ecd5d4SDmitry Osipenko pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu level:%u\n",
9712083da24SViresh Kumar __func__, new_opp->turbo, new_opp->rates[0],
9727813dd6fSViresh Kumar new_opp->supplies[0].u_volt, new_opp->supplies[0].u_volt_min,
973b6ecd5d4SDmitry Osipenko new_opp->supplies[0].u_volt_max, new_opp->clock_latency_ns,
974b6ecd5d4SDmitry Osipenko new_opp->level);
9757813dd6fSViresh Kumar
9767813dd6fSViresh Kumar /*
9777813dd6fSViresh Kumar * Notify the changes in the availability of the operable
9787813dd6fSViresh Kumar * frequency/voltage list.
9797813dd6fSViresh Kumar */
9807813dd6fSViresh Kumar blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
981deac8703SDave Gerlach return new_opp;
9827813dd6fSViresh Kumar
983da544b61SViresh Kumar free_required_opps:
984da544b61SViresh Kumar _of_opp_free_required_opps(opp_table, new_opp);
985*7ec98ebbSJoe Hattori put_node:
986*7ec98ebbSJoe Hattori of_node_put(np);
9877813dd6fSViresh Kumar free_opp:
9887813dd6fSViresh Kumar _opp_free(new_opp);
9897813dd6fSViresh Kumar
99027ff8187SYueHaibing return ret ? ERR_PTR(ret) : NULL;
9917813dd6fSViresh Kumar }
9927813dd6fSViresh Kumar
9937813dd6fSViresh Kumar /* Initializes OPP tables based on new bindings */
_of_add_opp_table_v2(struct device * dev,struct opp_table * opp_table)9945ed4cecdSViresh Kumar static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
9957813dd6fSViresh Kumar {
9967813dd6fSViresh Kumar struct device_node *np;
997a5663c9bSViresh Kumar int ret, count = 0;
9983ba98324SViresh Kumar struct dev_pm_opp *opp;
9997813dd6fSViresh Kumar
1000283d55e6SViresh Kumar /* OPP table is already initialized for the device */
100103758d60SViresh Kumar mutex_lock(&opp_table->lock);
1002283d55e6SViresh Kumar if (opp_table->parsed_static_opps) {
100303758d60SViresh Kumar opp_table->parsed_static_opps++;
100403758d60SViresh Kumar mutex_unlock(&opp_table->lock);
1005283d55e6SViresh Kumar return 0;
1006283d55e6SViresh Kumar }
1007283d55e6SViresh Kumar
100803758d60SViresh Kumar opp_table->parsed_static_opps = 1;
100903758d60SViresh Kumar mutex_unlock(&opp_table->lock);
1010b19c2355SViresh Kumar
10117813dd6fSViresh Kumar /* We have opp-table node now, iterate over it and add OPPs */
10125ed4cecdSViresh Kumar for_each_available_child_of_node(opp_table->np, np) {
1013deac8703SDave Gerlach opp = _opp_add_static_v2(opp_table, dev, np);
1014deac8703SDave Gerlach if (IS_ERR(opp)) {
1015deac8703SDave Gerlach ret = PTR_ERR(opp);
10167813dd6fSViresh Kumar dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
10177813dd6fSViresh Kumar ret);
10187978db34STobias Jordan of_node_put(np);
101903758d60SViresh Kumar goto remove_static_opp;
1020deac8703SDave Gerlach } else if (opp) {
1021deac8703SDave Gerlach count++;
10227813dd6fSViresh Kumar }
10237813dd6fSViresh Kumar }
10247813dd6fSViresh Kumar
1025335ffab3SMichał Mirosław /* There should be one or more OPPs defined */
1026335ffab3SMichał Mirosław if (!count) {
1027335ffab3SMichał Mirosław dev_err(dev, "%s: no supported OPPs", __func__);
1028ba003319SViresh Kumar ret = -ENOENT;
102903758d60SViresh Kumar goto remove_static_opp;
1030ba003319SViresh Kumar }
10317813dd6fSViresh Kumar
10327eba0c76SViresh Kumar lazy_link_required_opp_table(opp_table);
10337eba0c76SViresh Kumar
1034cdd6ed90SViresh Kumar return 0;
1035ba003319SViresh Kumar
103603758d60SViresh Kumar remove_static_opp:
103703758d60SViresh Kumar _opp_remove_all_static(opp_table);
1038ba003319SViresh Kumar
1039ba003319SViresh Kumar return ret;
10407813dd6fSViresh Kumar }
10417813dd6fSViresh Kumar
10427813dd6fSViresh Kumar /* Initializes OPP tables based on old-deprecated bindings */
_of_add_opp_table_v1(struct device * dev,struct opp_table * opp_table)10435ed4cecdSViresh Kumar static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
10447813dd6fSViresh Kumar {
10457813dd6fSViresh Kumar const struct property *prop;
10467813dd6fSViresh Kumar const __be32 *val;
10477813dd6fSViresh Kumar int nr, ret = 0;
10487813dd6fSViresh Kumar
104990d46d71SViresh Kumar mutex_lock(&opp_table->lock);
105090d46d71SViresh Kumar if (opp_table->parsed_static_opps) {
105190d46d71SViresh Kumar opp_table->parsed_static_opps++;
105290d46d71SViresh Kumar mutex_unlock(&opp_table->lock);
105390d46d71SViresh Kumar return 0;
105490d46d71SViresh Kumar }
105590d46d71SViresh Kumar
105690d46d71SViresh Kumar opp_table->parsed_static_opps = 1;
105790d46d71SViresh Kumar mutex_unlock(&opp_table->lock);
105890d46d71SViresh Kumar
10597813dd6fSViresh Kumar prop = of_find_property(dev->of_node, "operating-points", NULL);
106090d46d71SViresh Kumar if (!prop) {
106190d46d71SViresh Kumar ret = -ENODEV;
106290d46d71SViresh Kumar goto remove_static_opp;
106390d46d71SViresh Kumar }
106490d46d71SViresh Kumar if (!prop->value) {
106590d46d71SViresh Kumar ret = -ENODATA;
106690d46d71SViresh Kumar goto remove_static_opp;
106790d46d71SViresh Kumar }
10687813dd6fSViresh Kumar
10697813dd6fSViresh Kumar /*
10707813dd6fSViresh Kumar * Each OPP is a set of tuples consisting of frequency and
10717813dd6fSViresh Kumar * voltage like <freq-kHz vol-uV>.
10727813dd6fSViresh Kumar */
10737813dd6fSViresh Kumar nr = prop->length / sizeof(u32);
10747813dd6fSViresh Kumar if (nr % 2) {
10757813dd6fSViresh Kumar dev_err(dev, "%s: Invalid OPP table\n", __func__);
107690d46d71SViresh Kumar ret = -EINVAL;
107790d46d71SViresh Kumar goto remove_static_opp;
10787813dd6fSViresh Kumar }
10797813dd6fSViresh Kumar
10807813dd6fSViresh Kumar val = prop->value;
10817813dd6fSViresh Kumar while (nr) {
10827813dd6fSViresh Kumar unsigned long freq = be32_to_cpup(val++) * 1000;
10837813dd6fSViresh Kumar unsigned long volt = be32_to_cpup(val++);
10847813dd6fSViresh Kumar
10857813dd6fSViresh Kumar ret = _opp_add_v1(opp_table, dev, freq, volt, false);
10867813dd6fSViresh Kumar if (ret) {
10877813dd6fSViresh Kumar dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
10887813dd6fSViresh Kumar __func__, freq, ret);
108990d46d71SViresh Kumar goto remove_static_opp;
10907813dd6fSViresh Kumar }
10917813dd6fSViresh Kumar nr -= 2;
10927813dd6fSViresh Kumar }
10937813dd6fSViresh Kumar
10941f6620f8SViresh Kumar return 0;
10951f6620f8SViresh Kumar
109690d46d71SViresh Kumar remove_static_opp:
109790d46d71SViresh Kumar _opp_remove_all_static(opp_table);
109890d46d71SViresh Kumar
10997813dd6fSViresh Kumar return ret;
11007813dd6fSViresh Kumar }
11017813dd6fSViresh Kumar
_of_add_table_indexed(struct device * dev,int index)11021e5fb384SViresh Kumar static int _of_add_table_indexed(struct device *dev, int index)
1103fa9b274fSViresh Kumar {
11045ed4cecdSViresh Kumar struct opp_table *opp_table;
11058a352fd8SViresh Kumar int ret, count;
1106fa9b274fSViresh Kumar
11075ed4cecdSViresh Kumar if (index) {
11088a352fd8SViresh Kumar /*
11098a352fd8SViresh Kumar * If only one phandle is present, then the same OPP table
11108a352fd8SViresh Kumar * applies for all index requests.
11118a352fd8SViresh Kumar */
11128a352fd8SViresh Kumar count = of_count_phandle_with_args(dev->of_node,
11138a352fd8SViresh Kumar "operating-points-v2", NULL);
11143e27c79cSViresh Kumar if (count == 1)
11155ed4cecdSViresh Kumar index = 0;
11168a352fd8SViresh Kumar }
1117fa9b274fSViresh Kumar
11181e5fb384SViresh Kumar opp_table = _add_opp_table_indexed(dev, index, true);
1119dd461cd9SStephan Gerhold if (IS_ERR(opp_table))
1120dd461cd9SStephan Gerhold return PTR_ERR(opp_table);
11215ed4cecdSViresh Kumar
1122406e4765SViresh Kumar /*
1123406e4765SViresh Kumar * OPPs have two version of bindings now. Also try the old (v1)
1124406e4765SViresh Kumar * bindings for backward compatibility with older dtbs.
1125406e4765SViresh Kumar */
1126406e4765SViresh Kumar if (opp_table->np)
11275ed4cecdSViresh Kumar ret = _of_add_opp_table_v2(dev, opp_table);
1128406e4765SViresh Kumar else
1129406e4765SViresh Kumar ret = _of_add_opp_table_v1(dev, opp_table);
1130406e4765SViresh Kumar
11315ed4cecdSViresh Kumar if (ret)
11325ed4cecdSViresh Kumar dev_pm_opp_put_opp_table(opp_table);
1133fa9b274fSViresh Kumar
1134fa9b274fSViresh Kumar return ret;
1135fa9b274fSViresh Kumar }
1136406e4765SViresh Kumar
devm_pm_opp_of_table_release(void * data)11373d5cfbb6SYangtao Li static void devm_pm_opp_of_table_release(void *data)
11383d5cfbb6SYangtao Li {
11393d5cfbb6SYangtao Li dev_pm_opp_of_remove_table(data);
11403d5cfbb6SYangtao Li }
11413d5cfbb6SYangtao Li
_devm_of_add_table_indexed(struct device * dev,int index)11421e5fb384SViresh Kumar static int _devm_of_add_table_indexed(struct device *dev, int index)
1143e69709f6SDmitry Osipenko {
1144e69709f6SDmitry Osipenko int ret;
1145e69709f6SDmitry Osipenko
11461e5fb384SViresh Kumar ret = _of_add_table_indexed(dev, index);
1147e69709f6SDmitry Osipenko if (ret)
1148e69709f6SDmitry Osipenko return ret;
1149e69709f6SDmitry Osipenko
1150e69709f6SDmitry Osipenko return devm_add_action_or_reset(dev, devm_pm_opp_of_table_release, dev);
1151e69709f6SDmitry Osipenko }
1152e69709f6SDmitry Osipenko
11533d5cfbb6SYangtao Li /**
11543d5cfbb6SYangtao Li * devm_pm_opp_of_add_table() - Initialize opp table from device tree
11553d5cfbb6SYangtao Li * @dev: device pointer used to lookup OPP table.
11563d5cfbb6SYangtao Li *
11573d5cfbb6SYangtao Li * Register the initial OPP table with the OPP library for given device.
11583d5cfbb6SYangtao Li *
11593d5cfbb6SYangtao Li * The opp_table structure will be freed after the device is destroyed.
11603d5cfbb6SYangtao Li *
11613d5cfbb6SYangtao Li * Return:
11623d5cfbb6SYangtao Li * 0 On success OR
11633d5cfbb6SYangtao Li * Duplicate OPPs (both freq and volt are same) and opp->available
11643d5cfbb6SYangtao Li * -EEXIST Freq are same and volt are different OR
11653d5cfbb6SYangtao Li * Duplicate OPPs (both freq and volt are same) and !opp->available
11663d5cfbb6SYangtao Li * -ENOMEM Memory allocation failure
11673d5cfbb6SYangtao Li * -ENODEV when 'operating-points' property is not found or is invalid data
11683d5cfbb6SYangtao Li * in device node.
11693d5cfbb6SYangtao Li * -ENODATA when empty 'operating-points' property is found
11703d5cfbb6SYangtao Li * -EINVAL when invalid entries are found in opp-v2 table
11713d5cfbb6SYangtao Li */
devm_pm_opp_of_add_table(struct device * dev)11723d5cfbb6SYangtao Li int devm_pm_opp_of_add_table(struct device *dev)
11733d5cfbb6SYangtao Li {
11741e5fb384SViresh Kumar return _devm_of_add_table_indexed(dev, 0);
11753d5cfbb6SYangtao Li }
11763d5cfbb6SYangtao Li EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
11773d5cfbb6SYangtao Li
1178406e4765SViresh Kumar /**
1179406e4765SViresh Kumar * dev_pm_opp_of_add_table() - Initialize opp table from device tree
1180406e4765SViresh Kumar * @dev: device pointer used to lookup OPP table.
1181406e4765SViresh Kumar *
1182406e4765SViresh Kumar * Register the initial OPP table with the OPP library for given device.
1183406e4765SViresh Kumar *
1184406e4765SViresh Kumar * Return:
1185406e4765SViresh Kumar * 0 On success OR
1186406e4765SViresh Kumar * Duplicate OPPs (both freq and volt are same) and opp->available
1187406e4765SViresh Kumar * -EEXIST Freq are same and volt are different OR
1188406e4765SViresh Kumar * Duplicate OPPs (both freq and volt are same) and !opp->available
1189406e4765SViresh Kumar * -ENOMEM Memory allocation failure
1190406e4765SViresh Kumar * -ENODEV when 'operating-points' property is not found or is invalid data
1191406e4765SViresh Kumar * in device node.
1192406e4765SViresh Kumar * -ENODATA when empty 'operating-points' property is found
1193406e4765SViresh Kumar * -EINVAL when invalid entries are found in opp-v2 table
1194406e4765SViresh Kumar */
dev_pm_opp_of_add_table(struct device * dev)1195406e4765SViresh Kumar int dev_pm_opp_of_add_table(struct device *dev)
1196406e4765SViresh Kumar {
11971e5fb384SViresh Kumar return _of_add_table_indexed(dev, 0);
1198406e4765SViresh Kumar }
1199406e4765SViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
1200406e4765SViresh Kumar
1201406e4765SViresh Kumar /**
1202406e4765SViresh Kumar * dev_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree
1203406e4765SViresh Kumar * @dev: device pointer used to lookup OPP table.
1204406e4765SViresh Kumar * @index: Index number.
1205406e4765SViresh Kumar *
1206406e4765SViresh Kumar * Register the initial OPP table with the OPP library for given device only
1207406e4765SViresh Kumar * using the "operating-points-v2" property.
1208406e4765SViresh Kumar *
1209406e4765SViresh Kumar * Return: Refer to dev_pm_opp_of_add_table() for return values.
1210406e4765SViresh Kumar */
dev_pm_opp_of_add_table_indexed(struct device * dev,int index)1211406e4765SViresh Kumar int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
1212406e4765SViresh Kumar {
12131e5fb384SViresh Kumar return _of_add_table_indexed(dev, index);
1214406e4765SViresh Kumar }
1215fa9b274fSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
1216fa9b274fSViresh Kumar
1217559fef0dSViresh Kumar /**
1218e69709f6SDmitry Osipenko * devm_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree
1219e69709f6SDmitry Osipenko * @dev: device pointer used to lookup OPP table.
1220e69709f6SDmitry Osipenko * @index: Index number.
1221e69709f6SDmitry Osipenko *
1222e69709f6SDmitry Osipenko * This is a resource-managed variant of dev_pm_opp_of_add_table_indexed().
1223e69709f6SDmitry Osipenko */
devm_pm_opp_of_add_table_indexed(struct device * dev,int index)1224e69709f6SDmitry Osipenko int devm_pm_opp_of_add_table_indexed(struct device *dev, int index)
1225e69709f6SDmitry Osipenko {
12261e5fb384SViresh Kumar return _devm_of_add_table_indexed(dev, index);
1227e69709f6SDmitry Osipenko }
1228e69709f6SDmitry Osipenko EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_indexed);
1229e69709f6SDmitry Osipenko
12307813dd6fSViresh Kumar /* CPU device specific helpers */
12317813dd6fSViresh Kumar
12327813dd6fSViresh Kumar /**
12337813dd6fSViresh Kumar * dev_pm_opp_of_cpumask_remove_table() - Removes OPP table for @cpumask
12347813dd6fSViresh Kumar * @cpumask: cpumask for which OPP table needs to be removed
12357813dd6fSViresh Kumar *
12367813dd6fSViresh Kumar * This removes the OPP tables for CPUs present in the @cpumask.
12377813dd6fSViresh Kumar * This should be used only to remove static entries created from DT.
12387813dd6fSViresh Kumar */
dev_pm_opp_of_cpumask_remove_table(const struct cpumask * cpumask)12397813dd6fSViresh Kumar void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
12407813dd6fSViresh Kumar {
12412a4eb735SViresh Kumar _dev_pm_opp_cpumask_remove_table(cpumask, -1);
12427813dd6fSViresh Kumar }
12437813dd6fSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table);
12447813dd6fSViresh Kumar
12457813dd6fSViresh Kumar /**
12467813dd6fSViresh Kumar * dev_pm_opp_of_cpumask_add_table() - Adds OPP table for @cpumask
12477813dd6fSViresh Kumar * @cpumask: cpumask for which OPP table needs to be added.
12487813dd6fSViresh Kumar *
12497813dd6fSViresh Kumar * This adds the OPP tables for CPUs present in the @cpumask.
12507813dd6fSViresh Kumar */
dev_pm_opp_of_cpumask_add_table(const struct cpumask * cpumask)12517813dd6fSViresh Kumar int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
12527813dd6fSViresh Kumar {
12537813dd6fSViresh Kumar struct device *cpu_dev;
125450b6b87cSViresh Kumar int cpu, ret;
12557813dd6fSViresh Kumar
125650b6b87cSViresh Kumar if (WARN_ON(cpumask_empty(cpumask)))
125750b6b87cSViresh Kumar return -ENODEV;
12587813dd6fSViresh Kumar
12597813dd6fSViresh Kumar for_each_cpu(cpu, cpumask) {
12607813dd6fSViresh Kumar cpu_dev = get_cpu_device(cpu);
12617813dd6fSViresh Kumar if (!cpu_dev) {
12627813dd6fSViresh Kumar pr_err("%s: failed to get cpu%d device\n", __func__,
12637813dd6fSViresh Kumar cpu);
126450b6b87cSViresh Kumar ret = -ENODEV;
126550b6b87cSViresh Kumar goto remove_table;
12667813dd6fSViresh Kumar }
12677813dd6fSViresh Kumar
12687813dd6fSViresh Kumar ret = dev_pm_opp_of_add_table(cpu_dev);
12697813dd6fSViresh Kumar if (ret) {
12707813dd6fSViresh Kumar /*
12717813dd6fSViresh Kumar * OPP may get registered dynamically, don't print error
12727813dd6fSViresh Kumar * message here.
12737813dd6fSViresh Kumar */
12747813dd6fSViresh Kumar pr_debug("%s: couldn't find opp table for cpu:%d, %d\n",
12757813dd6fSViresh Kumar __func__, cpu, ret);
12767813dd6fSViresh Kumar
127750b6b87cSViresh Kumar goto remove_table;
127850b6b87cSViresh Kumar }
127950b6b87cSViresh Kumar }
128050b6b87cSViresh Kumar
128150b6b87cSViresh Kumar return 0;
128250b6b87cSViresh Kumar
128350b6b87cSViresh Kumar remove_table:
12847813dd6fSViresh Kumar /* Free all other OPPs */
12852a4eb735SViresh Kumar _dev_pm_opp_cpumask_remove_table(cpumask, cpu);
12867813dd6fSViresh Kumar
12877813dd6fSViresh Kumar return ret;
12887813dd6fSViresh Kumar }
12897813dd6fSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
12907813dd6fSViresh Kumar
12917813dd6fSViresh Kumar /*
12927813dd6fSViresh Kumar * Works only for OPP v2 bindings.
12937813dd6fSViresh Kumar *
12947813dd6fSViresh Kumar * Returns -ENOENT if operating-points-v2 bindings aren't supported.
12957813dd6fSViresh Kumar */
12967813dd6fSViresh Kumar /**
12977813dd6fSViresh Kumar * dev_pm_opp_of_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with
12987813dd6fSViresh Kumar * @cpu_dev using operating-points-v2
12997813dd6fSViresh Kumar * bindings.
13007813dd6fSViresh Kumar *
13017813dd6fSViresh Kumar * @cpu_dev: CPU device for which we do this operation
13027813dd6fSViresh Kumar * @cpumask: cpumask to update with information of sharing CPUs
13037813dd6fSViresh Kumar *
13047813dd6fSViresh Kumar * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
13057813dd6fSViresh Kumar *
13067813dd6fSViresh Kumar * Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev.
13077813dd6fSViresh Kumar */
dev_pm_opp_of_get_sharing_cpus(struct device * cpu_dev,struct cpumask * cpumask)13087813dd6fSViresh Kumar int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
13097813dd6fSViresh Kumar struct cpumask *cpumask)
13107813dd6fSViresh Kumar {
13117813dd6fSViresh Kumar struct device_node *np, *tmp_np, *cpu_np;
13127813dd6fSViresh Kumar int cpu, ret = 0;
13137813dd6fSViresh Kumar
13147813dd6fSViresh Kumar /* Get OPP descriptor node */
13157813dd6fSViresh Kumar np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
13167813dd6fSViresh Kumar if (!np) {
13177813dd6fSViresh Kumar dev_dbg(cpu_dev, "%s: Couldn't find opp node.\n", __func__);
13187813dd6fSViresh Kumar return -ENOENT;
13197813dd6fSViresh Kumar }
13207813dd6fSViresh Kumar
13217813dd6fSViresh Kumar cpumask_set_cpu(cpu_dev->id, cpumask);
13227813dd6fSViresh Kumar
13237813dd6fSViresh Kumar /* OPPs are shared ? */
13247813dd6fSViresh Kumar if (!of_property_read_bool(np, "opp-shared"))
13257813dd6fSViresh Kumar goto put_cpu_node;
13267813dd6fSViresh Kumar
13277813dd6fSViresh Kumar for_each_possible_cpu(cpu) {
13287813dd6fSViresh Kumar if (cpu == cpu_dev->id)
13297813dd6fSViresh Kumar continue;
13307813dd6fSViresh Kumar
13319867999fSSudeep Holla cpu_np = of_cpu_device_node_get(cpu);
13327813dd6fSViresh Kumar if (!cpu_np) {
13337813dd6fSViresh Kumar dev_err(cpu_dev, "%s: failed to get cpu%d node\n",
13347813dd6fSViresh Kumar __func__, cpu);
13357813dd6fSViresh Kumar ret = -ENOENT;
13367813dd6fSViresh Kumar goto put_cpu_node;
13377813dd6fSViresh Kumar }
13387813dd6fSViresh Kumar
13397813dd6fSViresh Kumar /* Get OPP descriptor node */
1340fa9b274fSViresh Kumar tmp_np = _opp_of_get_opp_desc_node(cpu_np, 0);
13419867999fSSudeep Holla of_node_put(cpu_np);
13427813dd6fSViresh Kumar if (!tmp_np) {
13437813dd6fSViresh Kumar pr_err("%pOF: Couldn't find opp node\n", cpu_np);
13447813dd6fSViresh Kumar ret = -ENOENT;
13457813dd6fSViresh Kumar goto put_cpu_node;
13467813dd6fSViresh Kumar }
13477813dd6fSViresh Kumar
13487813dd6fSViresh Kumar /* CPUs are sharing opp node */
13497813dd6fSViresh Kumar if (np == tmp_np)
13507813dd6fSViresh Kumar cpumask_set_cpu(cpu, cpumask);
13517813dd6fSViresh Kumar
13527813dd6fSViresh Kumar of_node_put(tmp_np);
13537813dd6fSViresh Kumar }
13547813dd6fSViresh Kumar
13557813dd6fSViresh Kumar put_cpu_node:
13567813dd6fSViresh Kumar of_node_put(np);
13577813dd6fSViresh Kumar return ret;
13587813dd6fSViresh Kumar }
13597813dd6fSViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus);
1360a88bd2a5SViresh Kumar
1361a88bd2a5SViresh Kumar /**
13624c6a343eSViresh Kumar * of_get_required_opp_performance_state() - Search for required OPP and return its performance state.
1363a88bd2a5SViresh Kumar * @np: Node that contains the "required-opps" property.
13644c6a343eSViresh Kumar * @index: Index of the phandle to parse.
1365a88bd2a5SViresh Kumar *
13664c6a343eSViresh Kumar * Returns the performance state of the OPP pointed out by the "required-opps"
13674c6a343eSViresh Kumar * property at @index in @np.
1368a88bd2a5SViresh Kumar *
13692feb5a89SViresh Kumar * Return: Zero or positive performance state on success, otherwise negative
13702feb5a89SViresh Kumar * value on errors.
1371a88bd2a5SViresh Kumar */
of_get_required_opp_performance_state(struct device_node * np,int index)13722feb5a89SViresh Kumar int of_get_required_opp_performance_state(struct device_node *np, int index)
1373a88bd2a5SViresh Kumar {
13744c6a343eSViresh Kumar struct dev_pm_opp *opp;
1375a88bd2a5SViresh Kumar struct device_node *required_np;
1376a88bd2a5SViresh Kumar struct opp_table *opp_table;
13772feb5a89SViresh Kumar int pstate = -EINVAL;
1378a88bd2a5SViresh Kumar
13794c6a343eSViresh Kumar required_np = of_parse_required_opp(np, index);
13804c6a343eSViresh Kumar if (!required_np)
1381020d86fcSRajendra Nayak return -ENODEV;
1382a88bd2a5SViresh Kumar
13834c6a343eSViresh Kumar opp_table = _find_table_of_opp_np(required_np);
13844c6a343eSViresh Kumar if (IS_ERR(opp_table)) {
13854c6a343eSViresh Kumar pr_err("%s: Failed to find required OPP table %pOF: %ld\n",
13864c6a343eSViresh Kumar __func__, np, PTR_ERR(opp_table));
13874c6a343eSViresh Kumar goto put_required_np;
1388a88bd2a5SViresh Kumar }
1389a88bd2a5SViresh Kumar
139084cb7ff3SViresh Kumar /* The OPP tables must belong to a genpd */
139184cb7ff3SViresh Kumar if (unlikely(!opp_table->is_genpd)) {
139284cb7ff3SViresh Kumar pr_err("%s: Performance state is only valid for genpds.\n", __func__);
139384cb7ff3SViresh Kumar goto put_required_np;
139484cb7ff3SViresh Kumar }
139584cb7ff3SViresh Kumar
13964c6a343eSViresh Kumar opp = _find_opp_of_np(opp_table, required_np);
13974c6a343eSViresh Kumar if (opp) {
13987c41cdcdSViresh Kumar pstate = opp->level;
13994c6a343eSViresh Kumar dev_pm_opp_put(opp);
1400a88bd2a5SViresh Kumar }
1401a88bd2a5SViresh Kumar
1402a88bd2a5SViresh Kumar dev_pm_opp_put_opp_table(opp_table);
1403a88bd2a5SViresh Kumar
14044c6a343eSViresh Kumar put_required_np:
14054c6a343eSViresh Kumar of_node_put(required_np);
14064c6a343eSViresh Kumar
14074c6a343eSViresh Kumar return pstate;
1408a88bd2a5SViresh Kumar }
14094c6a343eSViresh Kumar EXPORT_SYMBOL_GPL(of_get_required_opp_performance_state);
1410e2f4b5f8SViresh Kumar
1411e2f4b5f8SViresh Kumar /**
1412e2f4b5f8SViresh Kumar * dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp
1413e2f4b5f8SViresh Kumar * @opp: opp for which DT node has to be returned for
1414e2f4b5f8SViresh Kumar *
1415e2f4b5f8SViresh Kumar * Return: DT node corresponding to the opp, else 0 on success.
1416e2f4b5f8SViresh Kumar *
1417e2f4b5f8SViresh Kumar * The caller needs to put the node with of_node_put() after using it.
1418e2f4b5f8SViresh Kumar */
dev_pm_opp_get_of_node(struct dev_pm_opp * opp)1419e2f4b5f8SViresh Kumar struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
1420e2f4b5f8SViresh Kumar {
1421e2f4b5f8SViresh Kumar if (IS_ERR_OR_NULL(opp)) {
1422e2f4b5f8SViresh Kumar pr_err("%s: Invalid parameters\n", __func__);
1423e2f4b5f8SViresh Kumar return NULL;
1424e2f4b5f8SViresh Kumar }
1425e2f4b5f8SViresh Kumar
1426e2f4b5f8SViresh Kumar return of_node_get(opp->np);
1427e2f4b5f8SViresh Kumar }
1428e2f4b5f8SViresh Kumar EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
1429a4f342b9SQuentin Perret
1430a4f342b9SQuentin Perret /*
1431a4f342b9SQuentin Perret * Callback function provided to the Energy Model framework upon registration.
143232bf8bc9SLukasz Luba * It provides the power used by @dev at @kHz if it is the frequency of an
143332bf8bc9SLukasz Luba * existing OPP, or at the frequency of the first OPP above @kHz otherwise
143432bf8bc9SLukasz Luba * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
1435ae6ccaa6SLukasz Luba * frequency and @uW to the associated power.
143632bf8bc9SLukasz Luba *
143732bf8bc9SLukasz Luba * Returns 0 on success or a proper -EINVAL value in case of error.
143832bf8bc9SLukasz Luba */
143932bf8bc9SLukasz Luba static int __maybe_unused
_get_dt_power(struct device * dev,unsigned long * uW,unsigned long * kHz)1440ae6ccaa6SLukasz Luba _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
144132bf8bc9SLukasz Luba {
144232bf8bc9SLukasz Luba struct dev_pm_opp *opp;
144332bf8bc9SLukasz Luba unsigned long opp_freq, opp_power;
144432bf8bc9SLukasz Luba
144532bf8bc9SLukasz Luba /* Find the right frequency and related OPP */
144632bf8bc9SLukasz Luba opp_freq = *kHz * 1000;
144732bf8bc9SLukasz Luba opp = dev_pm_opp_find_freq_ceil(dev, &opp_freq);
144832bf8bc9SLukasz Luba if (IS_ERR(opp))
144932bf8bc9SLukasz Luba return -EINVAL;
145032bf8bc9SLukasz Luba
145132bf8bc9SLukasz Luba opp_power = dev_pm_opp_get_power(opp);
145232bf8bc9SLukasz Luba dev_pm_opp_put(opp);
145332bf8bc9SLukasz Luba if (!opp_power)
145432bf8bc9SLukasz Luba return -EINVAL;
145532bf8bc9SLukasz Luba
145632bf8bc9SLukasz Luba *kHz = opp_freq / 1000;
1457ae6ccaa6SLukasz Luba *uW = opp_power;
145832bf8bc9SLukasz Luba
145932bf8bc9SLukasz Luba return 0;
146032bf8bc9SLukasz Luba }
146132bf8bc9SLukasz Luba
146232bf8bc9SLukasz Luba /*
146332bf8bc9SLukasz Luba * Callback function provided to the Energy Model framework upon registration.
14640e0ffa85SLukasz Luba * This computes the power estimated by @dev at @kHz if it is the frequency
1465a4f342b9SQuentin Perret * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
1466a4f342b9SQuentin Perret * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
1467ae6ccaa6SLukasz Luba * frequency and @uW to the associated power. The power is estimated as
14680e0ffa85SLukasz Luba * P = C * V^2 * f with C being the device's capacitance and V and f
14690e0ffa85SLukasz Luba * respectively the voltage and frequency of the OPP.
1470a4f342b9SQuentin Perret *
14710e0ffa85SLukasz Luba * Returns -EINVAL if the power calculation failed because of missing
14720e0ffa85SLukasz Luba * parameters, 0 otherwise.
1473a4f342b9SQuentin Perret */
_get_power(struct device * dev,unsigned long * uW,unsigned long * kHz)1474ae6ccaa6SLukasz Luba static int __maybe_unused _get_power(struct device *dev, unsigned long *uW,
147575a3a99aSLukasz Luba unsigned long *kHz)
1476a4f342b9SQuentin Perret {
1477a4f342b9SQuentin Perret struct dev_pm_opp *opp;
1478a4f342b9SQuentin Perret struct device_node *np;
1479a4f342b9SQuentin Perret unsigned long mV, Hz;
1480a4f342b9SQuentin Perret u32 cap;
1481a4f342b9SQuentin Perret u64 tmp;
1482a4f342b9SQuentin Perret int ret;
1483a4f342b9SQuentin Perret
14840e0ffa85SLukasz Luba np = of_node_get(dev->of_node);
1485a4f342b9SQuentin Perret if (!np)
1486a4f342b9SQuentin Perret return -EINVAL;
1487a4f342b9SQuentin Perret
1488a4f342b9SQuentin Perret ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
1489a4f342b9SQuentin Perret of_node_put(np);
1490a4f342b9SQuentin Perret if (ret)
1491a4f342b9SQuentin Perret return -EINVAL;
1492a4f342b9SQuentin Perret
1493a4f342b9SQuentin Perret Hz = *kHz * 1000;
14940e0ffa85SLukasz Luba opp = dev_pm_opp_find_freq_ceil(dev, &Hz);
1495a4f342b9SQuentin Perret if (IS_ERR(opp))
1496a4f342b9SQuentin Perret return -EINVAL;
1497a4f342b9SQuentin Perret
1498a4f342b9SQuentin Perret mV = dev_pm_opp_get_voltage(opp) / 1000;
1499a4f342b9SQuentin Perret dev_pm_opp_put(opp);
1500a4f342b9SQuentin Perret if (!mV)
1501a4f342b9SQuentin Perret return -EINVAL;
1502a4f342b9SQuentin Perret
1503a4f342b9SQuentin Perret tmp = (u64)cap * mV * mV * (Hz / 1000000);
1504ae6ccaa6SLukasz Luba /* Provide power in micro-Watts */
1505ae6ccaa6SLukasz Luba do_div(tmp, 1000000);
1506a4f342b9SQuentin Perret
1507ae6ccaa6SLukasz Luba *uW = (unsigned long)tmp;
1508a4f342b9SQuentin Perret *kHz = Hz / 1000;
1509a4f342b9SQuentin Perret
1510a4f342b9SQuentin Perret return 0;
1511a4f342b9SQuentin Perret }
1512a4f342b9SQuentin Perret
_of_has_opp_microwatt_property(struct device * dev)151332bf8bc9SLukasz Luba static bool _of_has_opp_microwatt_property(struct device *dev)
151432bf8bc9SLukasz Luba {
151532bf8bc9SLukasz Luba unsigned long power, freq = 0;
151632bf8bc9SLukasz Luba struct dev_pm_opp *opp;
151732bf8bc9SLukasz Luba
151832bf8bc9SLukasz Luba /* Check if at least one OPP has needed property */
151932bf8bc9SLukasz Luba opp = dev_pm_opp_find_freq_ceil(dev, &freq);
152032bf8bc9SLukasz Luba if (IS_ERR(opp))
152132bf8bc9SLukasz Luba return false;
152232bf8bc9SLukasz Luba
152332bf8bc9SLukasz Luba power = dev_pm_opp_get_power(opp);
152432bf8bc9SLukasz Luba dev_pm_opp_put(opp);
152532bf8bc9SLukasz Luba if (!power)
152632bf8bc9SLukasz Luba return false;
152732bf8bc9SLukasz Luba
152832bf8bc9SLukasz Luba return true;
152932bf8bc9SLukasz Luba }
153032bf8bc9SLukasz Luba
1531a4f342b9SQuentin Perret /**
1532a4f342b9SQuentin Perret * dev_pm_opp_of_register_em() - Attempt to register an Energy Model
15330e0ffa85SLukasz Luba * @dev : Device for which an Energy Model has to be registered
15340e0ffa85SLukasz Luba * @cpus : CPUs for which an Energy Model has to be registered. For
15350e0ffa85SLukasz Luba * other type of devices it should be set to NULL.
1536a4f342b9SQuentin Perret *
1537a4f342b9SQuentin Perret * This checks whether the "dynamic-power-coefficient" devicetree property has
1538a4f342b9SQuentin Perret * been specified, and tries to register an Energy Model with it if it has.
15390e0ffa85SLukasz Luba * Having this property means the voltages are known for OPPs and the EM
15400e0ffa85SLukasz Luba * might be calculated.
1541a4f342b9SQuentin Perret */
dev_pm_opp_of_register_em(struct device * dev,struct cpumask * cpus)15420e0ffa85SLukasz Luba int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
1543a4f342b9SQuentin Perret {
154432bf8bc9SLukasz Luba struct em_data_callback em_cb;
1545a4f342b9SQuentin Perret struct device_node *np;
15460e0ffa85SLukasz Luba int ret, nr_opp;
1547a4f342b9SQuentin Perret u32 cap;
1548a4f342b9SQuentin Perret
15490e0ffa85SLukasz Luba if (IS_ERR_OR_NULL(dev)) {
15500e0ffa85SLukasz Luba ret = -EINVAL;
15510e0ffa85SLukasz Luba goto failed;
15520e0ffa85SLukasz Luba }
1553a4f342b9SQuentin Perret
15540e0ffa85SLukasz Luba nr_opp = dev_pm_opp_get_opp_count(dev);
15550e0ffa85SLukasz Luba if (nr_opp <= 0) {
15560e0ffa85SLukasz Luba ret = -EINVAL;
15570e0ffa85SLukasz Luba goto failed;
15580e0ffa85SLukasz Luba }
1559a4f342b9SQuentin Perret
156032bf8bc9SLukasz Luba /* First, try to find more precised Energy Model in DT */
156132bf8bc9SLukasz Luba if (_of_has_opp_microwatt_property(dev)) {
156232bf8bc9SLukasz Luba EM_SET_ACTIVE_POWER_CB(em_cb, _get_dt_power);
156332bf8bc9SLukasz Luba goto register_em;
156432bf8bc9SLukasz Luba }
156532bf8bc9SLukasz Luba
15660e0ffa85SLukasz Luba np = of_node_get(dev->of_node);
15670e0ffa85SLukasz Luba if (!np) {
15680e0ffa85SLukasz Luba ret = -EINVAL;
15690e0ffa85SLukasz Luba goto failed;
15700e0ffa85SLukasz Luba }
1571a4f342b9SQuentin Perret
1572a4f342b9SQuentin Perret /*
1573a4f342b9SQuentin Perret * Register an EM only if the 'dynamic-power-coefficient' property is
1574a4f342b9SQuentin Perret * set in devicetree. It is assumed the voltage values are known if that
1575a4f342b9SQuentin Perret * property is set since it is useless otherwise. If voltages are not
1576a4f342b9SQuentin Perret * known, just let the EM registration fail with an error to alert the
1577a4f342b9SQuentin Perret * user about the inconsistent configuration.
1578a4f342b9SQuentin Perret */
1579a4f342b9SQuentin Perret ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
1580a4f342b9SQuentin Perret of_node_put(np);
15810e0ffa85SLukasz Luba if (ret || !cap) {
15820e0ffa85SLukasz Luba dev_dbg(dev, "Couldn't find proper 'dynamic-power-coefficient' in DT\n");
15830e0ffa85SLukasz Luba ret = -EINVAL;
15840e0ffa85SLukasz Luba goto failed;
15850e0ffa85SLukasz Luba }
1586a4f342b9SQuentin Perret
158732bf8bc9SLukasz Luba EM_SET_ACTIVE_POWER_CB(em_cb, _get_power);
158832bf8bc9SLukasz Luba
158932bf8bc9SLukasz Luba register_em:
1590c250d50fSLukasz Luba ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
15910e0ffa85SLukasz Luba if (ret)
15920e0ffa85SLukasz Luba goto failed;
15930e0ffa85SLukasz Luba
15940e0ffa85SLukasz Luba return 0;
15950e0ffa85SLukasz Luba
15960e0ffa85SLukasz Luba failed:
15970e0ffa85SLukasz Luba dev_dbg(dev, "Couldn't register Energy Model %d\n", ret);
15980e0ffa85SLukasz Luba return ret;
1599a4f342b9SQuentin Perret }
1600a4f342b9SQuentin Perret EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em);
1601