Blame view

drivers/opp/of.c 28.5 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
2
3
4
5
6
7
8
  /*
   * Generic OPP OF helpers
   *
   * Copyright (C) 2009-2010 Texas Instruments Incorporated.
   *	Nishanth Menon
   *	Romit Dasgupta
   *	Kevin Hilman
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
9
10
11
12
13
14
15
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
  #include <linux/cpu.h>
  #include <linux/errno.h>
  #include <linux/device.h>
9867999f3   Sudeep Holla   PM / OPP: add mis...
16
  #include <linux/of_device.h>
3ba98324e   Viresh Kumar   PM / OPP: Get per...
17
  #include <linux/pm_domain.h>
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
18
  #include <linux/slab.h>
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
19
  #include <linux/export.h>
a4f342b96   Quentin Perret   PM / OPP: Introdu...
20
  #include <linux/energy_model.h>
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
21
22
  
  #include "opp.h"
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  /*
   * Returns opp descriptor node for a device node, caller must
   * do of_node_put().
   */
  static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np,
  						     int index)
  {
  	/* "operating-points-v2" can be an array for power domain providers */
  	return of_parse_phandle(np, "operating-points-v2", index);
  }
  
  /* Returns opp descriptor node for a device, caller must do of_node_put() */
  struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev)
  {
  	return _opp_of_get_opp_desc_node(dev->of_node, 0);
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
283d55e68   Viresh Kumar   OPP: Prevent crea...
40
  struct opp_table *_managed_opp(struct device *dev, int index)
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
41
  {
b83c1899a   Viresh Kumar   PM / OPP: Use dev...
42
  	struct opp_table *opp_table, *managed_table = NULL;
283d55e68   Viresh Kumar   OPP: Prevent crea...
43
  	struct device_node *np;
b83c1899a   Viresh Kumar   PM / OPP: Use dev...
44

283d55e68   Viresh Kumar   OPP: Prevent crea...
45
46
47
  	np = _opp_of_get_opp_desc_node(dev->of_node, index);
  	if (!np)
  		return NULL;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
48

052c6f191   Viresh Kumar   PM / OPP: Move aw...
49
  	list_for_each_entry(opp_table, &opp_tables, node) {
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
50
51
52
53
54
55
56
57
  		if (opp_table->np == np) {
  			/*
  			 * Multiple devices can point to the same OPP table and
  			 * so will have same node-pointer, np.
  			 *
  			 * But the OPPs will be considered as shared only if the
  			 * OPP table contains a "opp-shared" property.
  			 */
b83c1899a   Viresh Kumar   PM / OPP: Use dev...
58
59
60
61
  			if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
  				_get_opp_table_kref(opp_table);
  				managed_table = opp_table;
  			}
79ee2e8f7   Viresh Kumar   PM / OPP: Add 'UN...
62

b83c1899a   Viresh Kumar   PM / OPP: Use dev...
63
  			break;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
64
65
  		}
  	}
283d55e68   Viresh Kumar   OPP: Prevent crea...
66
  	of_node_put(np);
b83c1899a   Viresh Kumar   PM / OPP: Use dev...
67
68
  
  	return managed_table;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
69
  }
5d6d106fa   Viresh Kumar   OPP: Populate req...
70
71
72
73
74
  /* The caller must call dev_pm_opp_put() after the OPP is used */
  static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table,
  					  struct device_node *opp_np)
  {
  	struct dev_pm_opp *opp;
5d6d106fa   Viresh Kumar   OPP: Populate req...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  	mutex_lock(&opp_table->lock);
  
  	list_for_each_entry(opp, &opp_table->opp_list, node) {
  		if (opp->np == opp_np) {
  			dev_pm_opp_get(opp);
  			mutex_unlock(&opp_table->lock);
  			return opp;
  		}
  	}
  
  	mutex_unlock(&opp_table->lock);
  
  	return NULL;
  }
  
  static struct device_node *of_parse_required_opp(struct device_node *np,
  						 int index)
  {
  	struct device_node *required_np;
  
  	required_np = of_parse_phandle(np, "required-opps", index);
  	if (unlikely(!required_np)) {
  		pr_err("%s: Unable to parse required-opps: %pOF, index: %d
  ",
  		       __func__, np, index);
  	}
  
  	return required_np;
  }
  
  /* The caller must call dev_pm_opp_put_opp_table() after the table is used */
  static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
  {
  	struct opp_table *opp_table;
699e21e41   Viresh Kumar   OPP: Improve _fin...
109
  	struct device_node *opp_table_np;
5d6d106fa   Viresh Kumar   OPP: Populate req...
110
111
  
  	lockdep_assert_held(&opp_table_lock);
699e21e41   Viresh Kumar   OPP: Improve _fin...
112
113
114
115
116
117
  	opp_table_np = of_get_parent(opp_np);
  	if (!opp_table_np)
  		goto err;
  
  	/* It is safe to put the node now as all we need now is its address */
  	of_node_put(opp_table_np);
5d6d106fa   Viresh Kumar   OPP: Populate req...
118
  	list_for_each_entry(opp_table, &opp_tables, node) {
699e21e41   Viresh Kumar   OPP: Improve _fin...
119
  		if (opp_table_np == opp_table->np) {
5d6d106fa   Viresh Kumar   OPP: Populate req...
120
121
122
123
  			_get_opp_table_kref(opp_table);
  			return opp_table;
  		}
  	}
699e21e41   Viresh Kumar   OPP: Improve _fin...
124
  err:
5d6d106fa   Viresh Kumar   OPP: Populate req...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  	return ERR_PTR(-ENODEV);
  }
  
  /* Free resources previously acquired by _opp_table_alloc_required_tables() */
  static void _opp_table_free_required_tables(struct opp_table *opp_table)
  {
  	struct opp_table **required_opp_tables = opp_table->required_opp_tables;
  	int i;
  
  	if (!required_opp_tables)
  		return;
  
  	for (i = 0; i < opp_table->required_opp_count; i++) {
  		if (IS_ERR_OR_NULL(required_opp_tables[i]))
  			break;
  
  		dev_pm_opp_put_opp_table(required_opp_tables[i]);
  	}
  
  	kfree(required_opp_tables);
  
  	opp_table->required_opp_count = 0;
  	opp_table->required_opp_tables = NULL;
  }
  
  /*
   * Populate all devices and opp tables which are part of "required-opps" list.
   * Checking only the first OPP node should be enough.
   */
  static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
  					     struct device *dev,
  					     struct device_node *opp_np)
  {
  	struct opp_table **required_opp_tables;
  	struct device_node *required_np, *np;
c0ab9e081   Viresh Kumar   opp: Allocate gen...
160
  	int count, i;
5d6d106fa   Viresh Kumar   OPP: Populate req...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  
  	/* Traversing the first OPP node is all we need */
  	np = of_get_next_available_child(opp_np, NULL);
  	if (!np) {
  		dev_err(dev, "Empty OPP table
  ");
  		return;
  	}
  
  	count = of_count_phandle_with_args(np, "required-opps", NULL);
  	if (!count)
  		goto put_np;
  
  	required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
  				      GFP_KERNEL);
c0ab9e081   Viresh Kumar   opp: Allocate gen...
176
  	if (!required_opp_tables)
5d6d106fa   Viresh Kumar   OPP: Populate req...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  		goto put_np;
  
  	opp_table->required_opp_tables = required_opp_tables;
  	opp_table->required_opp_count = count;
  
  	for (i = 0; i < count; i++) {
  		required_np = of_parse_required_opp(np, i);
  		if (!required_np)
  			goto free_required_tables;
  
  		required_opp_tables[i] = _find_table_of_opp_np(required_np);
  		of_node_put(required_np);
  
  		if (IS_ERR(required_opp_tables[i]))
  			goto free_required_tables;
  
  		/*
  		 * We only support genpd's OPPs in the "required-opps" for now,
  		 * as we don't know how much about other cases. Error out if the
  		 * required OPP doesn't belong to a genpd.
  		 */
  		if (!required_opp_tables[i]->is_genpd) {
  			dev_err(dev, "required-opp doesn't belong to genpd: %pOF
  ",
  				required_np);
  			goto free_required_tables;
  		}
  	}
  
  	goto put_np;
  
  free_required_tables:
  	_opp_table_free_required_tables(opp_table);
  put_np:
  	of_node_put(np);
  }
eb7c8743d   Viresh Kumar   OPP: Pass index t...
213
214
  void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
  			int index)
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
215
  {
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
216
217
  	struct device_node *np, *opp_np;
  	u32 val;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
218
219
220
221
222
223
  
  	/*
  	 * Only required for backward compatibility with v1 bindings, but isn't
  	 * harmful for other cases. And so we do it unconditionally.
  	 */
  	np = of_node_get(dev->of_node);
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
224
225
226
227
228
229
230
  	if (!np)
  		return;
  
  	if (!of_property_read_u32(np, "clock-latency", &val))
  		opp_table->clock_latency_ns_max = val;
  	of_property_read_u32(np, "voltage-tolerance",
  			     &opp_table->voltage_tolerance_v1);
61d8e7c73   Viresh Kumar   OPP: Identify and...
231
232
  	if (of_find_property(np, "#power-domain-cells", NULL))
  		opp_table->is_genpd = true;
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
233
234
235
236
237
238
239
240
241
242
243
244
245
  	/* Get OPP table node */
  	opp_np = _opp_of_get_opp_desc_node(np, index);
  	of_node_put(np);
  
  	if (!opp_np)
  		return;
  
  	if (of_property_read_bool(opp_np, "opp-shared"))
  		opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
  	else
  		opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE;
  
  	opp_table->np = opp_np;
5d6d106fa   Viresh Kumar   OPP: Populate req...
246
  	_opp_table_alloc_required_tables(opp_table, dev, opp_np);
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
247
  	of_node_put(opp_np);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
248
  }
5d6d106fa   Viresh Kumar   OPP: Populate req...
249
250
251
252
  void _of_clear_opp_table(struct opp_table *opp_table)
  {
  	_opp_table_free_required_tables(opp_table);
  }
da544b61e   Viresh Kumar   OPP: Populate OPP...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  /*
   * 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)
  {
  	struct dev_pm_opp **required_opps = opp->required_opps;
  	int i;
  
  	if (!required_opps)
  		return;
  
  	for (i = 0; i < opp_table->required_opp_count; i++) {
  		if (!required_opps[i])
  			break;
  
  		/* Put the reference back */
  		dev_pm_opp_put(required_opps[i]);
  	}
  
  	kfree(required_opps);
  	opp->required_opps = NULL;
  }
  
  /* 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)
  {
  	struct dev_pm_opp **required_opps;
  	struct opp_table *required_table;
  	struct device_node *np;
  	int i, ret, count = opp_table->required_opp_count;
  
  	if (!count)
  		return 0;
  
  	required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL);
  	if (!required_opps)
  		return -ENOMEM;
  
  	opp->required_opps = required_opps;
  
  	for (i = 0; i < count; i++) {
  		required_table = opp_table->required_opp_tables[i];
  
  		np = of_parse_required_opp(opp->np, i);
  		if (unlikely(!np)) {
  			ret = -ENODEV;
  			goto free_required_opps;
  		}
  
  		required_opps[i] = _find_opp_of_np(required_table, np);
  		of_node_put(np);
  
  		if (!required_opps[i]) {
  			pr_err("%s: Unable to find required OPP node: %pOF (%d)
  ",
  			       __func__, opp->np, i);
  			ret = -ENODEV;
  			goto free_required_opps;
  		}
  	}
  
  	return 0;
  
  free_required_opps:
  	_of_opp_free_required_opps(opp_table, opp);
  
  	return ret;
  }
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
324
325
326
327
328
329
  static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
  			      struct device_node *np)
  {
  	unsigned int count = opp_table->supported_hw_count;
  	u32 version;
  	int ret;
a4ee45459   Dave Gerlach   PM / OPP: Don't s...
330
331
332
333
334
335
336
337
338
339
340
341
  	if (!opp_table->supported_hw) {
  		/*
  		 * In the case that no supported_hw has been set by the
  		 * platform but there is an opp-supported-hw value set for
  		 * an OPP then the OPP should not be enabled as there is
  		 * no way to see if the hardware supports it.
  		 */
  		if (of_find_property(np, "opp-supported-hw", NULL))
  			return false;
  		else
  			return true;
  	}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
  
  	while (count--) {
  		ret = of_property_read_u32_index(np, "opp-supported-hw", count,
  						 &version);
  		if (ret) {
  			dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d
  ",
  				 __func__, count, ret);
  			return false;
  		}
  
  		/* Both of these are bitwise masks of the versions */
  		if (!(version & opp_table->supported_hw[count]))
  			return false;
  	}
  
  	return true;
  }
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
360
361
362
  static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
  			      struct opp_table *opp_table)
  {
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
363
  	u32 *microvolt, *microamp = NULL;
46f48aca2   Viresh Kumar   OPP: Fix missing ...
364
  	int supplies = opp_table->regulator_count, vcount, icount, ret, i, j;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  	struct property *prop = NULL;
  	char name[NAME_MAX];
  
  	/* Search for "opp-microvolt-<name>" */
  	if (opp_table->prop_name) {
  		snprintf(name, sizeof(name), "opp-microvolt-%s",
  			 opp_table->prop_name);
  		prop = of_find_property(opp->np, name, NULL);
  	}
  
  	if (!prop) {
  		/* Search for "opp-microvolt" */
  		sprintf(name, "opp-microvolt");
  		prop = of_find_property(opp->np, name, NULL);
  
  		/* Missing property isn't a problem, but an invalid entry is */
688a48b0d   Viresh Kumar   PM / OPP: opp-mic...
381
  		if (!prop) {
46f48aca2   Viresh Kumar   OPP: Fix missing ...
382
383
384
385
386
387
388
  			if (unlikely(supplies == -1)) {
  				/* Initialize regulator_count */
  				opp_table->regulator_count = 0;
  				return 0;
  			}
  
  			if (!supplies)
688a48b0d   Viresh Kumar   PM / OPP: opp-mic...
389
390
391
392
393
394
395
  				return 0;
  
  			dev_err(dev, "%s: opp-microvolt missing although OPP managing regulators
  ",
  				__func__);
  			return -EINVAL;
  		}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
396
  	}
46f48aca2   Viresh Kumar   OPP: Fix missing ...
397
398
399
400
401
402
403
404
  	if (unlikely(supplies == -1)) {
  		/* Initialize regulator_count */
  		supplies = opp_table->regulator_count = 1;
  	} else if (unlikely(!supplies)) {
  		dev_err(dev, "%s: opp-microvolt wasn't expected
  ", __func__);
  		return -EINVAL;
  	}
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
405
406
  	vcount = of_property_count_u32_elems(opp->np, name);
  	if (vcount < 0) {
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
407
408
  		dev_err(dev, "%s: Invalid %s property (%d)
  ",
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
409
410
  			__func__, name, vcount);
  		return vcount;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
411
  	}
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
412
413
414
415
416
  	/* There can be one or three elements per supply */
  	if (vcount != supplies && vcount != supplies * 3) {
  		dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)
  ",
  			__func__, name, vcount, supplies);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
417
418
  		return -EINVAL;
  	}
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
419
420
421
422
423
  	microvolt = kmalloc_array(vcount, sizeof(*microvolt), GFP_KERNEL);
  	if (!microvolt)
  		return -ENOMEM;
  
  	ret = of_property_read_u32_array(opp->np, name, microvolt, vcount);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
424
425
426
  	if (ret) {
  		dev_err(dev, "%s: error parsing %s: %d
  ", __func__, name, ret);
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
427
428
  		ret = -EINVAL;
  		goto free_microvolt;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  	}
  
  	/* Search for "opp-microamp-<name>" */
  	prop = NULL;
  	if (opp_table->prop_name) {
  		snprintf(name, sizeof(name), "opp-microamp-%s",
  			 opp_table->prop_name);
  		prop = of_find_property(opp->np, name, NULL);
  	}
  
  	if (!prop) {
  		/* Search for "opp-microamp" */
  		sprintf(name, "opp-microamp");
  		prop = of_find_property(opp->np, name, NULL);
  	}
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
444
445
446
447
448
449
450
451
452
  	if (prop) {
  		icount = of_property_count_u32_elems(opp->np, name);
  		if (icount < 0) {
  			dev_err(dev, "%s: Invalid %s property (%d)
  ", __func__,
  				name, icount);
  			ret = icount;
  			goto free_microvolt;
  		}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
453

dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
  		if (icount != supplies) {
  			dev_err(dev, "%s: Invalid number of elements in %s property (%d) with supplies (%d)
  ",
  				__func__, name, icount, supplies);
  			ret = -EINVAL;
  			goto free_microvolt;
  		}
  
  		microamp = kmalloc_array(icount, sizeof(*microamp), GFP_KERNEL);
  		if (!microamp) {
  			ret = -EINVAL;
  			goto free_microvolt;
  		}
  
  		ret = of_property_read_u32_array(opp->np, name, microamp,
  						 icount);
  		if (ret) {
  			dev_err(dev, "%s: error parsing %s: %d
  ", __func__,
  				name, ret);
  			ret = -EINVAL;
  			goto free_microamp;
  		}
  	}
  
  	for (i = 0, j = 0; i < supplies; i++) {
  		opp->supplies[i].u_volt = microvolt[j++];
  
  		if (vcount == supplies) {
  			opp->supplies[i].u_volt_min = opp->supplies[i].u_volt;
  			opp->supplies[i].u_volt_max = opp->supplies[i].u_volt;
  		} else {
  			opp->supplies[i].u_volt_min = microvolt[j++];
  			opp->supplies[i].u_volt_max = microvolt[j++];
  		}
  
  		if (microamp)
  			opp->supplies[i].u_amp = microamp[i];
  	}
  
  free_microamp:
  	kfree(microamp);
  free_microvolt:
  	kfree(microvolt);
  
  	return ret;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
500
501
502
503
504
505
506
507
  }
  
  /**
   * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT
   *				  entries
   * @dev:	device pointer used to lookup OPP table.
   *
   * Free OPPs created using static entries present in DT.
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
508
509
510
   */
  void dev_pm_opp_of_remove_table(struct device *dev)
  {
2a4eb7358   Viresh Kumar   OPP: Don't remove...
511
  	_dev_pm_opp_find_and_remove_table(dev);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
512
513
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
514
515
  /**
   * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
516
   * @opp_table:	OPP table
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
517
518
519
520
521
522
523
   * @dev:	device for which we do this operation
   * @np:		device node
   *
   * This function adds an opp definition to the opp table and returns status. The
   * opp can be controlled using dev_pm_opp_enable/disable functions and may be
   * removed by dev_pm_opp_remove.
   *
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
524
   * Return:
deac8703d   Dave Gerlach   PM / OPP: _of_add...
525
526
527
   * Valid OPP pointer:
   *		On success
   * NULL:
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
528
   *		Duplicate OPPs (both freq and volt are same) and opp->available
deac8703d   Dave Gerlach   PM / OPP: _of_add...
529
530
531
   *		OR if the OPP is not supported by hardware.
   * ERR_PTR(-EEXIST):
   *		Freq are same and volt are different OR
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
532
   *		Duplicate OPPs (both freq and volt are same) and !opp->available
deac8703d   Dave Gerlach   PM / OPP: _of_add...
533
534
535
536
   * ERR_PTR(-ENOMEM):
   *		Memory allocation failure
   * ERR_PTR(-EINVAL):
   *		Failed parsing the OPP node
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
537
   */
deac8703d   Dave Gerlach   PM / OPP: _of_add...
538
539
  static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
  		struct device *dev, struct device_node *np)
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
540
  {
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
541
  	struct dev_pm_opp *new_opp;
6a89e012a   Dan Carpenter   PM / OPP: silence...
542
  	u64 rate = 0;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
543
544
  	u32 val;
  	int ret;
a1e8c1360   Viresh Kumar   PM / OPP: "opp-hz...
545
  	bool rate_not_available = false;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
546

8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
547
548
  	new_opp = _opp_allocate(opp_table);
  	if (!new_opp)
deac8703d   Dave Gerlach   PM / OPP: _of_add...
549
  		return ERR_PTR(-ENOMEM);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
550
551
552
  
  	ret = of_property_read_u64(np, "opp-hz", &rate);
  	if (ret < 0) {
a1e8c1360   Viresh Kumar   PM / OPP: "opp-hz...
553
  		/* "opp-hz" is optional for devices like power domains. */
61d8e7c73   Viresh Kumar   OPP: Identify and...
554
  		if (!opp_table->is_genpd) {
a1e8c1360   Viresh Kumar   PM / OPP: "opp-hz...
555
556
557
558
559
560
561
562
563
564
565
566
567
  			dev_err(dev, "%s: opp-hz not found
  ", __func__);
  			goto free_opp;
  		}
  
  		rate_not_available = true;
  	} 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.
  		 */
  		new_opp->rate = (unsigned long)rate;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
568
  	}
5b93ac542   Rajendra Nayak   OPP: Add support ...
569
  	of_property_read_u32(np, "opp-level", &new_opp->level);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
570
571
572
573
574
575
  	/* 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: %llu
  ", rate);
  		goto free_opp;
  	}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
576
577
578
579
580
  	new_opp->turbo = of_property_read_bool(np, "turbo-mode");
  
  	new_opp->np = np;
  	new_opp->dynamic = false;
  	new_opp->available = true;
da544b61e   Viresh Kumar   OPP: Populate OPP...
581
582
583
  	ret = _of_opp_alloc_required_opps(opp_table, new_opp);
  	if (ret)
  		goto free_opp;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
584
585
586
587
588
  	if (!of_property_read_u32(np, "clock-latency-ns", &val))
  		new_opp->clock_latency_ns = val;
  
  	ret = opp_parse_supplies(new_opp, dev, opp_table);
  	if (ret)
da544b61e   Viresh Kumar   OPP: Populate OPP...
589
  		goto free_required_opps;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
590

ca1b5d77b   Viresh Kumar   OPP: Configure al...
591
592
  	if (opp_table->is_genpd)
  		new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
593

a1e8c1360   Viresh Kumar   PM / OPP: "opp-hz...
594
  	ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
7f8538eba   Viresh Kumar   PM / OPP: Fix mem...
595
596
597
598
  	if (ret) {
  		/* Don't return error for duplicate OPPs */
  		if (ret == -EBUSY)
  			ret = 0;
da544b61e   Viresh Kumar   OPP: Populate OPP...
599
  		goto free_required_opps;
7f8538eba   Viresh Kumar   PM / OPP: Fix mem...
600
  	}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
601
602
603
604
  
  	/* OPP to select on device suspend */
  	if (of_property_read_bool(np, "opp-suspend")) {
  		if (opp_table->suspend_opp) {
452755175   Anson Huang   opp: of: Support ...
605
606
607
608
609
610
  			/* Pick the OPP with higher rate as suspend OPP */
  			if (new_opp->rate > opp_table->suspend_opp->rate) {
  				opp_table->suspend_opp->suspend = false;
  				new_opp->suspend = true;
  				opp_table->suspend_opp = new_opp;
  			}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
611
612
613
614
615
616
617
618
  		} else {
  			new_opp->suspend = true;
  			opp_table->suspend_opp = new_opp;
  		}
  	}
  
  	if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
  		opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
619
620
  	pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu
  ",
0f0fe7e01   Viresh Kumar   PM / OPP: Manage ...
621
  		 __func__, new_opp->turbo, new_opp->rate,
dfbe4678d   Viresh Kumar   PM / OPP: Add inf...
622
623
  		 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);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
624
625
626
627
628
  
  	/*
  	 * Notify the changes in the availability of the operable
  	 * frequency/voltage list.
  	 */
052c6f191   Viresh Kumar   PM / OPP: Move aw...
629
  	blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp);
deac8703d   Dave Gerlach   PM / OPP: _of_add...
630
  	return new_opp;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
631

da544b61e   Viresh Kumar   OPP: Populate OPP...
632
633
  free_required_opps:
  	_of_opp_free_required_opps(opp_table, new_opp);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
634
  free_opp:
8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
635
  	_opp_free(new_opp);
deac8703d   Dave Gerlach   PM / OPP: _of_add...
636
  	return ERR_PTR(ret);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
637
638
639
  }
  
  /* Initializes OPP tables based on new bindings */
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
640
  static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
641
642
  {
  	struct device_node *np;
283d55e68   Viresh Kumar   OPP: Prevent crea...
643
  	int ret, count = 0, pstate_count = 0;
3ba98324e   Viresh Kumar   PM / OPP: Get per...
644
  	struct dev_pm_opp *opp;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
645

283d55e68   Viresh Kumar   OPP: Prevent crea...
646
647
648
649
650
  	/* OPP table is already initialized for the device */
  	if (opp_table->parsed_static_opps) {
  		kref_get(&opp_table->list_kref);
  		return 0;
  	}
b19c23551   Viresh Kumar   opp: Reinitialize...
651
652
653
654
655
656
  	/*
  	 * Re-initialize list_kref every time we add static OPPs to the OPP
  	 * table as the reference count may be 0 after the last tie static OPPs
  	 * were removed.
  	 */
  	kref_init(&opp_table->list_kref);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
657
  	/* We have opp-table node now, iterate over it and add OPPs */
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
658
  	for_each_available_child_of_node(opp_table->np, np) {
deac8703d   Dave Gerlach   PM / OPP: _of_add...
659
660
661
  		opp = _opp_add_static_v2(opp_table, dev, np);
  		if (IS_ERR(opp)) {
  			ret = PTR_ERR(opp);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
662
663
664
  			dev_err(dev, "%s: Failed to add OPP, %d
  ", __func__,
  				ret);
7978db344   Tobias Jordan   PM / OPP: Add mis...
665
  			of_node_put(np);
5fed8c513   Viresh Kumar   opp: Free static ...
666
  			goto put_list_kref;
deac8703d   Dave Gerlach   PM / OPP: _of_add...
667
668
  		} else if (opp) {
  			count++;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
669
670
671
672
  		}
  	}
  
  	/* There should be one of more OPP defined */
5fed8c513   Viresh Kumar   opp: Free static ...
673
674
675
676
  	if (WARN_ON(!count)) {
  		ret = -ENOENT;
  		goto put_list_kref;
  	}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
677

3ba98324e   Viresh Kumar   PM / OPP: Get per...
678
679
680
681
682
683
684
685
  	list_for_each_entry(opp, &opp_table->opp_list, node)
  		pstate_count += !!opp->pstate;
  
  	/* Either all or none of the nodes shall have performance state set */
  	if (pstate_count && pstate_count != count) {
  		dev_err(dev, "Not all nodes have performance state set (%d: %d)
  ",
  			count, pstate_count);
5fed8c513   Viresh Kumar   opp: Free static ...
686
687
  		ret = -ENOENT;
  		goto put_list_kref;
3ba98324e   Viresh Kumar   PM / OPP: Get per...
688
689
690
691
  	}
  
  	if (pstate_count)
  		opp_table->genpd_performance_state = true;
f06ed90e7   Viresh Kumar   OPP: Parse OPP ta...
692
  	opp_table->parsed_static_opps = true;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
693

cdd6ed90c   Viresh Kumar   OPP: Use a single...
694
  	return 0;
5fed8c513   Viresh Kumar   opp: Free static ...
695
696
697
698
699
  
  put_list_kref:
  	_put_opp_list_kref(opp_table);
  
  	return ret;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
700
701
702
  }
  
  /* Initializes OPP tables based on old-deprecated bindings */
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
703
  static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
704
705
706
  {
  	const struct property *prop;
  	const __be32 *val;
8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
707
  	int nr, ret = 0;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
  
  	prop = of_find_property(dev->of_node, "operating-points", NULL);
  	if (!prop)
  		return -ENODEV;
  	if (!prop->value)
  		return -ENODATA;
  
  	/*
  	 * Each OPP is a set of tuples consisting of frequency and
  	 * voltage like <freq-kHz vol-uV>.
  	 */
  	nr = prop->length / sizeof(u32);
  	if (nr % 2) {
  		dev_err(dev, "%s: Invalid OPP table
  ", __func__);
  		return -EINVAL;
  	}
  
  	val = prop->value;
  	while (nr) {
  		unsigned long freq = be32_to_cpup(val++) * 1000;
  		unsigned long volt = be32_to_cpup(val++);
8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
730
  		ret = _opp_add_v1(opp_table, dev, freq, volt, false);
04a86a84c   Viresh Kumar   PM / OPP: Error o...
731
732
733
734
  		if (ret) {
  			dev_err(dev, "%s: Failed to add OPP %ld (%d)
  ",
  				__func__, freq, ret);
5fed8c513   Viresh Kumar   opp: Free static ...
735
  			_put_opp_list_kref(opp_table);
cdd6ed90c   Viresh Kumar   OPP: Use a single...
736
  			return ret;
04a86a84c   Viresh Kumar   PM / OPP: Error o...
737
  		}
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
738
739
  		nr -= 2;
  	}
8cd2f6e8f   Viresh Kumar   PM / OPP: Don't a...
740
  	return ret;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
741
742
743
744
745
746
747
748
  }
  
  /**
   * dev_pm_opp_of_add_table() - Initialize opp table from device tree
   * @dev:	device pointer used to lookup OPP table.
   *
   * Register the initial OPP table with the OPP library for given device.
   *
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
749
750
751
752
753
754
755
756
757
758
759
760
761
   * Return:
   * 0		On success OR
   *		Duplicate OPPs (both freq and volt are same) and opp->available
   * -EEXIST	Freq are same and volt are different OR
   *		Duplicate OPPs (both freq and volt are same) and !opp->available
   * -ENOMEM	Memory allocation failure
   * -ENODEV	when 'operating-points' property is not found or is invalid data
   *		in device node.
   * -ENODATA	when empty 'operating-points' property is found
   * -EINVAL	when invalid entries are found in opp-v2 table
   */
  int dev_pm_opp_of_add_table(struct device *dev)
  {
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
762
  	struct opp_table *opp_table;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
763
  	int ret;
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
764
765
766
  	opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0);
  	if (!opp_table)
  		return -ENOMEM;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
767
  	/*
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
768
769
  	 * OPPs have two version of bindings now. Also try the old (v1)
  	 * bindings for backward compatibility with older dtbs.
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
770
  	 */
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
771
772
773
774
  	if (opp_table->np)
  		ret = _of_add_opp_table_v2(dev, opp_table);
  	else
  		ret = _of_add_opp_table_v1(dev, opp_table);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
775

5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
776
777
  	if (ret)
  		dev_pm_opp_put_opp_table(opp_table);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
778
779
780
781
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
fa9b274f8   Viresh Kumar   PM / OPP: Impleme...
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
  /**
   * dev_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree
   * @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.
   *
   * Return:
   * 0		On success OR
   *		Duplicate OPPs (both freq and volt are same) and opp->available
   * -EEXIST	Freq are same and volt are different OR
   *		Duplicate OPPs (both freq and volt are same) and !opp->available
   * -ENOMEM	Memory allocation failure
   * -ENODEV	when 'operating-points' property is not found or is invalid data
   *		in device node.
   * -ENODATA	when empty 'operating-points' property is found
   * -EINVAL	when invalid entries are found in opp-v2 table
   */
  int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
  {
5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
803
  	struct opp_table *opp_table;
8a352fd87   Viresh Kumar   OPP: Allow same O...
804
  	int ret, count;
fa9b274f8   Viresh Kumar   PM / OPP: Impleme...
805

5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
806
  	if (index) {
8a352fd87   Viresh Kumar   OPP: Allow same O...
807
808
809
810
811
812
  		/*
  		 * If only one phandle is present, then the same OPP table
  		 * applies for all index requests.
  		 */
  		count = of_count_phandle_with_args(dev->of_node,
  						   "operating-points-v2", NULL);
3e27c79c4   Viresh Kumar   OPP: Fix parsing ...
813
814
  		if (count == 1)
  			index = 0;
8a352fd87   Viresh Kumar   OPP: Allow same O...
815
  	}
fa9b274f8   Viresh Kumar   PM / OPP: Impleme...
816

5ed4cecd7   Viresh Kumar   OPP: Pass OPP tab...
817
818
819
820
821
822
823
  	opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
  	if (!opp_table)
  		return -ENOMEM;
  
  	ret = _of_add_opp_table_v2(dev, opp_table);
  	if (ret)
  		dev_pm_opp_put_opp_table(opp_table);
fa9b274f8   Viresh Kumar   PM / OPP: Impleme...
824
825
826
827
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
828
829
830
831
832
833
834
835
  /* CPU device specific helpers */
  
  /**
   * dev_pm_opp_of_cpumask_remove_table() - Removes OPP table for @cpumask
   * @cpumask:	cpumask for which OPP table needs to be removed
   *
   * This removes the OPP tables for CPUs present in the @cpumask.
   * This should be used only to remove static entries created from DT.
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
836
837
838
   */
  void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask)
  {
2a4eb7358   Viresh Kumar   OPP: Don't remove...
839
  	_dev_pm_opp_cpumask_remove_table(cpumask, -1);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
840
841
842
843
844
845
846
847
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table);
  
  /**
   * dev_pm_opp_of_cpumask_add_table() - Adds OPP table for @cpumask
   * @cpumask:	cpumask for which OPP table needs to be added.
   *
   * This adds the OPP tables for CPUs present in the @cpumask.
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
848
849
850
851
   */
  int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask)
  {
  	struct device *cpu_dev;
50b6b87c8   Viresh Kumar   OPP: Improve erro...
852
  	int cpu, ret;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
853

50b6b87c8   Viresh Kumar   OPP: Improve erro...
854
855
  	if (WARN_ON(cpumask_empty(cpumask)))
  		return -ENODEV;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
856
857
858
859
860
861
862
  
  	for_each_cpu(cpu, cpumask) {
  		cpu_dev = get_cpu_device(cpu);
  		if (!cpu_dev) {
  			pr_err("%s: failed to get cpu%d device
  ", __func__,
  			       cpu);
50b6b87c8   Viresh Kumar   OPP: Improve erro...
863
864
  			ret = -ENODEV;
  			goto remove_table;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
865
866
867
868
  		}
  
  		ret = dev_pm_opp_of_add_table(cpu_dev);
  		if (ret) {
5b60697cd   Viresh Kumar   PM / OPP: OF: Use...
869
870
871
872
873
874
875
  			/*
  			 * OPP may get registered dynamically, don't print error
  			 * message here.
  			 */
  			pr_debug("%s: couldn't find opp table for cpu:%d, %d
  ",
  				 __func__, cpu, ret);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
876

50b6b87c8   Viresh Kumar   OPP: Improve erro...
877
  			goto remove_table;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
878
879
  		}
  	}
50b6b87c8   Viresh Kumar   OPP: Improve erro...
880
881
882
883
884
  	return 0;
  
  remove_table:
  	/* Free all other OPPs */
  	_dev_pm_opp_cpumask_remove_table(cpumask, cpu);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
  
  /*
   * Works only for OPP v2 bindings.
   *
   * Returns -ENOENT if operating-points-v2 bindings aren't supported.
   */
  /**
   * dev_pm_opp_of_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with
   *				      @cpu_dev using operating-points-v2
   *				      bindings.
   *
   * @cpu_dev:	CPU device for which we do this operation
   * @cpumask:	cpumask to update with information of sharing CPUs
   *
   * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
   *
   * Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev.
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
905
906
907
908
   */
  int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
  				   struct cpumask *cpumask)
  {
762792913   Waldemar Rymarkiewicz   PM / OPP: Fix get...
909
  	struct device_node *np, *tmp_np, *cpu_np;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
910
911
912
  	int cpu, ret = 0;
  
  	/* Get OPP descriptor node */
0764c604c   Dave Gerlach   PM / OPP: Expose ...
913
  	np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
914
  	if (!np) {
349aa92e8   Masahiro Yamada   PM / OPP: fix deb...
915
916
  		dev_dbg(cpu_dev, "%s: Couldn't find opp node.
  ", __func__);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
917
918
919
920
921
922
923
924
925
926
927
928
  		return -ENOENT;
  	}
  
  	cpumask_set_cpu(cpu_dev->id, cpumask);
  
  	/* OPPs are shared ? */
  	if (!of_property_read_bool(np, "opp-shared"))
  		goto put_cpu_node;
  
  	for_each_possible_cpu(cpu) {
  		if (cpu == cpu_dev->id)
  			continue;
9867999f3   Sudeep Holla   PM / OPP: add mis...
929
  		cpu_np = of_cpu_device_node_get(cpu);
762792913   Waldemar Rymarkiewicz   PM / OPP: Fix get...
930
931
932
  		if (!cpu_np) {
  			dev_err(cpu_dev, "%s: failed to get cpu%d node
  ",
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
933
  				__func__, cpu);
762792913   Waldemar Rymarkiewicz   PM / OPP: Fix get...
934
  			ret = -ENOENT;
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
935
936
937
938
  			goto put_cpu_node;
  		}
  
  		/* Get OPP descriptor node */
fa9b274f8   Viresh Kumar   PM / OPP: Impleme...
939
  		tmp_np = _opp_of_get_opp_desc_node(cpu_np, 0);
9867999f3   Sudeep Holla   PM / OPP: add mis...
940
  		of_node_put(cpu_np);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
941
  		if (!tmp_np) {
762792913   Waldemar Rymarkiewicz   PM / OPP: Fix get...
942
943
  			pr_err("%pOF: Couldn't find opp node
  ", cpu_np);
f47b72a15   Viresh Kumar   PM / OPP: Move CO...
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
  			ret = -ENOENT;
  			goto put_cpu_node;
  		}
  
  		/* CPUs are sharing opp node */
  		if (np == tmp_np)
  			cpumask_set_cpu(cpu, cpumask);
  
  		of_node_put(tmp_np);
  	}
  
  put_cpu_node:
  	of_node_put(np);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus);
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
960
961
  
  /**
4c6a343e5   Viresh Kumar   OPP: Rename and r...
962
   * of_get_required_opp_performance_state() - Search for required OPP and return its performance state.
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
963
   * @np: Node that contains the "required-opps" property.
4c6a343e5   Viresh Kumar   OPP: Rename and r...
964
   * @index: Index of the phandle to parse.
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
965
   *
4c6a343e5   Viresh Kumar   OPP: Rename and r...
966
967
   * Returns the performance state of the OPP pointed out by the "required-opps"
   * property at @index in @np.
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
968
   *
2feb5a896   Viresh Kumar   OPP: Don't return...
969
970
   * Return: Zero or positive performance state on success, otherwise negative
   * value on errors.
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
971
   */
2feb5a896   Viresh Kumar   OPP: Don't return...
972
  int of_get_required_opp_performance_state(struct device_node *np, int index)
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
973
  {
4c6a343e5   Viresh Kumar   OPP: Rename and r...
974
  	struct dev_pm_opp *opp;
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
975
976
  	struct device_node *required_np;
  	struct opp_table *opp_table;
2feb5a896   Viresh Kumar   OPP: Don't return...
977
  	int pstate = -EINVAL;
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
978

4c6a343e5   Viresh Kumar   OPP: Rename and r...
979
980
  	required_np = of_parse_required_opp(np, index);
  	if (!required_np)
2feb5a896   Viresh Kumar   OPP: Don't return...
981
  		return -EINVAL;
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
982

4c6a343e5   Viresh Kumar   OPP: Rename and r...
983
984
985
986
987
988
  	opp_table = _find_table_of_opp_np(required_np);
  	if (IS_ERR(opp_table)) {
  		pr_err("%s: Failed to find required OPP table %pOF: %ld
  ",
  		       __func__, np, PTR_ERR(opp_table));
  		goto put_required_np;
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
989
  	}
4c6a343e5   Viresh Kumar   OPP: Rename and r...
990
991
992
993
  	opp = _find_opp_of_np(opp_table, required_np);
  	if (opp) {
  		pstate = opp->pstate;
  		dev_pm_opp_put(opp);
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
994
  	}
4c6a343e5   Viresh Kumar   OPP: Rename and r...
995
  	dev_pm_opp_put_opp_table(opp_table);
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
996

4c6a343e5   Viresh Kumar   OPP: Rename and r...
997
  put_required_np:
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
998
  	of_node_put(required_np);
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
999

4c6a343e5   Viresh Kumar   OPP: Rename and r...
1000
  	return pstate;
a88bd2a51   Viresh Kumar   PM / OPP: Impleme...
1001
  }
4c6a343e5   Viresh Kumar   OPP: Rename and r...
1002
  EXPORT_SYMBOL_GPL(of_get_required_opp_performance_state);
e2f4b5f8d   Viresh Kumar   PM / OPP: Impleme...
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
  
  /**
   * dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp
   * @opp:	opp for which DT node has to be returned for
   *
   * Return: DT node corresponding to the opp, else 0 on success.
   *
   * The caller needs to put the node with of_node_put() after using it.
   */
  struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
  {
  	if (IS_ERR_OR_NULL(opp)) {
  		pr_err("%s: Invalid parameters
  ", __func__);
  		return NULL;
  	}
  
  	return of_node_get(opp->np);
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
a4f342b96   Quentin Perret   PM / OPP: Introdu...
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  
  /*
   * Callback function provided to the Energy Model framework upon registration.
   * This computes the power estimated by @CPU at @kHz if it is the frequency
   * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
   * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
   * frequency and @mW to the associated power. The power is estimated as
   * P = C * V^2 * f with C being the CPU's capacitance and V and f respectively
   * the voltage and frequency of the OPP.
   *
   * Returns -ENODEV if the CPU device cannot be found, -EINVAL if the power
   * calculation failed because of missing parameters, 0 otherwise.
   */
  static int __maybe_unused _get_cpu_power(unsigned long *mW, unsigned long *kHz,
  					 int cpu)
  {
  	struct device *cpu_dev;
  	struct dev_pm_opp *opp;
  	struct device_node *np;
  	unsigned long mV, Hz;
  	u32 cap;
  	u64 tmp;
  	int ret;
  
  	cpu_dev = get_cpu_device(cpu);
  	if (!cpu_dev)
  		return -ENODEV;
  
  	np = of_node_get(cpu_dev->of_node);
  	if (!np)
  		return -EINVAL;
  
  	ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
  	of_node_put(np);
  	if (ret)
  		return -EINVAL;
  
  	Hz = *kHz * 1000;
  	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz);
  	if (IS_ERR(opp))
  		return -EINVAL;
  
  	mV = dev_pm_opp_get_voltage(opp) / 1000;
  	dev_pm_opp_put(opp);
  	if (!mV)
  		return -EINVAL;
  
  	tmp = (u64)cap * mV * mV * (Hz / 1000000);
  	do_div(tmp, 1000000000);
  
  	*mW = (unsigned long)tmp;
  	*kHz = Hz / 1000;
  
  	return 0;
  }
  
  /**
   * dev_pm_opp_of_register_em() - Attempt to register an Energy Model
   * @cpus	: CPUs for which an Energy Model has to be registered
   *
   * This checks whether the "dynamic-power-coefficient" devicetree property has
   * been specified, and tries to register an Energy Model with it if it has.
   */
  void dev_pm_opp_of_register_em(struct cpumask *cpus)
  {
  	struct em_data_callback em_cb = EM_DATA_CB(_get_cpu_power);
  	int ret, nr_opp, cpu = cpumask_first(cpus);
  	struct device *cpu_dev;
  	struct device_node *np;
  	u32 cap;
  
  	cpu_dev = get_cpu_device(cpu);
  	if (!cpu_dev)
  		return;
  
  	nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
  	if (nr_opp <= 0)
  		return;
  
  	np = of_node_get(cpu_dev->of_node);
  	if (!np)
  		return;
  
  	/*
  	 * Register an EM only if the 'dynamic-power-coefficient' property is
  	 * set in devicetree. It is assumed the voltage values are known if that
  	 * property is set since it is useless otherwise. If voltages are not
  	 * known, just let the EM registration fail with an error to alert the
  	 * user about the inconsistent configuration.
  	 */
  	ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
  	of_node_put(np);
  	if (ret || !cap)
  		return;
  
  	em_register_perf_domain(cpus, nr_opp, &em_cb);
  }
  EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em);