xref: /OK3568_Linux_fs/u-boot/drivers/power/dvfs/rockchip_wtemp_dvfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 #include <common.h>
6 #include <dm.h>
7 #include <clk.h>
8 #include <dvfs.h>
9 #include <thermal.h>
10 #include <linux/list.h>
11 
12 #include <asm/arch/clock.h>
13 #include <power/regulator.h>
14 #ifdef CONFIG_ROCKCHIP_DMC
15 #include <asm/arch/rockchip_dmc.h>
16 #endif
17 
18 /*
19  * # This is a simple wide temperature(ie. wtemp) dvfs driver, the policy is:
20  *
21  * 1. U-Boot parse cpu/dmc opp table from kernel dtb, anyone of
22  *    "rockchip,low-temp = <...>" and "rockchip,high-temp = <...>" present in
23  *    cpu/dmc nodes means wtemp is enabled.
24  *
25  *    1.1. When temperature trigger "rockchip,low-temp", increase 50mv voltage
26  *         as target voltage. If target voltage is over "rockchip,max-volt",
27  *         just set "rockchip,max-volt" as target voltage and lower 2 level freq,
28  *
29  *    1.2. When temperature trigger "rockchip,high-temp", just apply opp table[0]
30  *         voltage and freq.
31  *
32  * 2. U-Boot parse cpu/dmc thermal zone "trip-point-0" temperature from kernel
33  *    dtb, and apply the same rules as above [1.2] policy.
34  *
35  *
36  * # The dvfs policy apply moment is:
37  *
38  * 1. Appy it after clk and regulator drivers setup;
39  * 2. Repeat apply it by CONFIG_PREBOOT command until achieve the target
40  *    temperature. user should add: #define CONFIG_PREBOOT "dvfs repeat" and
41  *    assign repeat property in dts:
42  *
43  *	uboot-wide-temperature {
44  *		status = "okay";
45  *		compatible = "rockchip,uboot-wide-temperature";
46  *
47  *		cpu,low-temp-repeat;
48  *		cpu,high-temp-repeat;
49  *		dmc,low-temp-repeat;
50  *		dmc,high-temp-repeat;
51  *	};
52  */
53 
54 #define FDT_PATH_CPUS		"/cpus"
55 #define FDT_PATH_DMC		"/dmc"
56 #define FDT_PATH_THREMAL_TRIP_POINT0	\
57 	"/thermal-zones/soc-thermal/trips/trip-point-0"
58 #define FDT_PATH_THREMAL_COOLING_MAPS	\
59 	"/thermal-zones/soc-thermal/cooling-maps"
60 
61 #define OPP_TABLE_MAX		20
62 #define RATE_LOWER_LEVEL_N	2
63 #define DIFF_VOLTAGE_UV		50000
64 #define TEMP_STRING_LEN		12
65 #define REPEAT_PERIOD_US	1000000
66 
67 static LIST_HEAD(pm_e_head);
68 
69 enum pm_id {
70 	PM_CPU,
71 	PM_DMC,
72 };
73 
74 enum pm_event {
75 	PM_EVT_NONE = 0x0,
76 	PM_EVT_LOW  = 0x1,
77 	PM_EVT_HIGH = 0x2,
78 	PM_EVT_BOTH = PM_EVT_LOW | PM_EVT_HIGH,
79 };
80 
81 struct opp_table {
82 	u64 hz;
83 	u32 uv;
84 };
85 
86 struct lmt_param {
87 	int low_temp;		/* milli degree */
88 	int high_temp;		/* milli degree */
89 	int tz_temp;		/* milli degree */
90 	int max_volt;		/* uV */
91 
92 	bool htemp_repeat;
93 	bool ltemp_repeat;
94 
95 	bool ltemp_limit;
96 	bool htemp_limit;
97 	bool tztemp_limit;
98 };
99 
100 struct pm_element {
101 	int id;
102 	const char *name;
103 	const char *supply_name;
104 	int volt_diff;
105 	u32 opp_nr;
106 	struct opp_table opp[OPP_TABLE_MAX];
107 	struct lmt_param lmt;
108 	struct udevice *supply;
109 	struct clk clk;
110 	struct list_head node;
111 };
112 
113 struct wtemp_dvfs_priv {
114 	struct udevice *thermal;
115 	struct pm_element *cpu;
116 	struct pm_element *dmc;
117 };
118 
119 static struct pm_element pm_cpu = {
120 	.id		= PM_CPU,
121 	.name		= "cpu",
122 	.supply_name	= "cpu-supply",
123 	.volt_diff	= DIFF_VOLTAGE_UV,
124 };
125 
126 static struct pm_element pm_dmc = {
127 	.id		= PM_DMC,
128 	.name		= "dmc",
129 	.supply_name	= "center-supply",
130 	.volt_diff	= DIFF_VOLTAGE_UV,
131 };
132 
temp2string(int temp,char * data,int len)133 static void temp2string(int temp, char *data, int len)
134 {
135 	int decimal_point;
136 	int integer;
137 
138 	integer = abs(temp) / 1000;
139 	decimal_point = abs(temp) % 1000;
140 	snprintf(data, len, "%s%d.%d",
141 		 temp < 0 ? "-" : "", integer, decimal_point);
142 }
143 
wtemp_get_lowlevel_rate(ulong rate,u32 level,struct pm_element * e)144 static ulong wtemp_get_lowlevel_rate(ulong rate, u32 level,
145 				     struct pm_element *e)
146 {
147 	struct opp_table *opp;
148 	int i, count, idx = 0;
149 
150 	opp = e->opp;
151 	count = e->opp_nr;
152 
153 	for (i = 0; i < count; i++) {
154 		if (opp[i].hz >= rate) {
155 			idx = (i <= level) ? 0 : i - level;
156 			break;
157 		}
158 	}
159 
160 	return opp[idx].hz;
161 }
162 
__wtemp_clk_get_rate(struct pm_element * e)163 static ulong __wtemp_clk_get_rate(struct pm_element *e)
164 {
165 #ifdef CONFIG_ROCKCHIP_DMC
166 	if (e->id == PM_DMC)
167 		return rockchip_ddrclk_sip_recalc_rate_v2();
168 #endif
169 	return clk_get_rate(&e->clk);
170 }
171 
__wtemp_clk_set_rate(struct pm_element * e,ulong rate)172 static ulong __wtemp_clk_set_rate(struct pm_element *e, ulong rate)
173 {
174 #ifdef CONFIG_ROCKCHIP_DMC
175 	if (e->id == PM_DMC) {
176 		rate = rockchip_ddrclk_sip_round_rate_v2(rate);
177 		rockchip_ddrclk_sip_set_rate_v2(rate);
178 	} else
179 #endif
180 		clk_set_rate(&e->clk, rate);
181 
182 	return rate;
183 }
184 
__wtemp_regulator_get_value(struct pm_element * e)185 static int __wtemp_regulator_get_value(struct pm_element *e)
186 {
187 	return regulator_get_value(e->supply);
188 }
189 
__wtemp_regulator_set_value(struct pm_element * e,int value)190 static int __wtemp_regulator_set_value(struct pm_element *e, int value)
191 {
192 	return regulator_set_value(e->supply, value);
193 }
194 
195 /*
196  * Policy: Increase voltage
197  *
198  * 1. target volt = original volt + diff volt;
199  * 2. If target volt is not over max_volt, just set it;
200  * 3. Otherwise set max_volt as target volt and lower the rate(front N level).
201  */
wtemp_dvfs_low_temp_adjust(struct udevice * dev,struct pm_element * e)202 static void wtemp_dvfs_low_temp_adjust(struct udevice *dev, struct pm_element *e)
203 {
204 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
205 	ulong org_rate, tgt_rate, rb_rate;
206 	int org_volt, tgt_volt, rb_volt;
207 
208 	org_rate = __wtemp_clk_get_rate(e);
209 	org_volt = __wtemp_regulator_get_value(e);
210 	tgt_volt = org_volt + e->volt_diff;
211 	if ((e->lmt.max_volt != -ENODATA) && (tgt_volt > e->lmt.max_volt)) {
212 		tgt_volt = e->lmt.max_volt;
213 		__wtemp_regulator_set_value(e, tgt_volt);
214 		tgt_rate = wtemp_get_lowlevel_rate(org_rate,
215 						RATE_LOWER_LEVEL_N, priv->cpu);
216 		tgt_rate = __wtemp_clk_set_rate(e, tgt_rate);
217 	} else {
218 		__wtemp_regulator_set_value(e, tgt_volt);
219 		tgt_rate = org_rate;
220 	}
221 
222 	/* Check */
223 	rb_rate = __wtemp_clk_get_rate(e);
224 	rb_volt = __wtemp_regulator_get_value(e);
225 	if (tgt_rate != rb_rate)
226 		printf("DVFS: %s: target rate=%ld, readback rate=%ld !\n",
227 		       e->name, tgt_rate, rb_rate);
228 	if (tgt_volt != rb_volt)
229 		printf("DVFS: %s: target volt=%d, readback volt=%d !\n",
230 		       e->name, tgt_volt, rb_volt);
231 
232 	printf("DVFS: %s(low): %ld->%ld Hz, %d->%d uV\n",
233 	       e->name, org_rate, rb_rate, org_volt, rb_volt);
234 }
235 
236 /*
237  * Policy:
238  *
239  * Just set opp table[0] volt and rate, i.e. the lowest performance.
240  */
wtemp_dvfs_high_temp_adjust(struct udevice * dev,struct pm_element * e)241 static void wtemp_dvfs_high_temp_adjust(struct udevice *dev, struct pm_element *e)
242 {
243 	ulong org_rate, tgt_rate, rb_rate;
244 	int org_volt, tgt_volt, rb_volt;
245 
246 	/* Apply opp[0] */
247 	org_rate = __wtemp_clk_get_rate(e);
248 	tgt_rate = e->opp[0].hz;
249 	tgt_rate = __wtemp_clk_set_rate(e, tgt_rate);
250 
251 	org_volt = __wtemp_regulator_get_value(e);
252 	tgt_volt = e->opp[0].uv;
253 	__wtemp_regulator_set_value(e, tgt_volt);
254 
255 	/* Check */
256 	rb_rate = __wtemp_clk_get_rate(e);
257 	rb_volt = __wtemp_regulator_get_value(e);
258 	if (tgt_rate != rb_rate)
259 		printf("DVFS: %s: target rate=%ld, readback rate=%ld !\n",
260 		       e->name, tgt_rate, rb_rate);
261 	if (tgt_volt != rb_volt)
262 		printf("DVFS: %s: target volt=%d, readback volt=%d !\n",
263 		       e->name, tgt_volt, rb_volt);
264 
265 	printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
266 	       e->name, org_rate, tgt_rate, org_volt, tgt_volt);
267 }
268 
wtemp_dvfs_is_effect(struct pm_element * e,int temp,enum pm_event evt)269 static bool wtemp_dvfs_is_effect(struct pm_element *e,
270 				 int temp, enum pm_event evt)
271 {
272 	if (evt & PM_EVT_LOW) {
273 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
274 			return false;
275 	}
276 
277 	if (evt & PM_EVT_HIGH) {
278 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
279 			return false;
280 		else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
281 			return false;
282 	}
283 
284 	return true;
285 }
286 
__wtemp_dvfs_apply(struct udevice * dev,struct pm_element * e,int temp,enum pm_event evt)287 static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
288 			      int temp, enum pm_event evt)
289 {
290 	enum pm_event ret = PM_EVT_NONE;
291 
292 	if (evt & PM_EVT_LOW) {
293 		/* Over lowest temperature: increase voltage */
294 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
295 			ret |= PM_EVT_LOW;
296 			wtemp_dvfs_low_temp_adjust(dev, e);
297 		}
298 	}
299 
300 	if (evt & PM_EVT_HIGH) {
301 		/* Over highest/thermal_zone temperature: decrease rate and voltage */
302 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
303 			ret |= PM_EVT_HIGH;
304 			wtemp_dvfs_high_temp_adjust(dev, e);
305 		} else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
306 			ret |= PM_EVT_HIGH;
307 			wtemp_dvfs_high_temp_adjust(dev, e);
308 		}
309 	}
310 
311 	return ret;
312 }
313 
__wtemp_common_ofdata_to_platdata(ofnode node,struct pm_element * e)314 static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
315 {
316 	ofnode supply, opp_node;
317 	u32 phandle, uv, clock[2];
318 	uint64_t hz;
319 	int ret;
320 
321 	/* Get regulator and clk */
322 	if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
323 		supply = ofnode_get_by_phandle(phandle);
324 		ret = regulator_get_by_devname(supply.np->name, &e->supply);
325 		if (ret) {
326 			printf("DVFS: %s: Get supply(%s) failed, ret=%d",
327 			       e->name, supply.np->full_name, ret);
328 			return ret;
329 		}
330 		debug("DVFS: supply: %s\n", supply.np->full_name);
331 	}
332 
333 	if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
334 		e->clk.id = clock[1];
335 		ret = rockchip_get_clk(&e->clk.dev);
336 		if (ret) {
337 			printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
338 			return ret;
339 		}
340 	}
341 
342 	/* Get opp-table & limit param */
343 	if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
344 		opp_node = ofnode_get_by_phandle(phandle);
345 		e->lmt.low_temp = ofnode_read_s32_default(opp_node,
346 						"rockchip,low-temp", -ENODATA);
347 		e->lmt.high_temp = ofnode_read_s32_default(opp_node,
348 						"rockchip,high-temp", -ENODATA);
349 		e->lmt.max_volt = ofnode_read_u32_default(opp_node,
350 						"rockchip,max-volt", -ENODATA);
351 
352 		debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
353 		      e->name, e->lmt.low_temp, e->lmt.high_temp,
354 		      e->lmt.max_volt);
355 
356 		ofnode_for_each_subnode(node, opp_node) {
357 			if (e->opp_nr >= OPP_TABLE_MAX) {
358 				printf("DVFS: over max(%d) opp table items\n",
359 				       OPP_TABLE_MAX);
360 				break;
361 			}
362 			ofnode_read_u64(node, "opp-hz", &hz);
363 			ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
364 			e->opp[e->opp_nr].hz = hz;
365 			e->opp[e->opp_nr].uv = uv;
366 			e->opp_nr++;
367 			debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
368 			      e->name, e->opp_nr - 1,
369 			      hz, uv, ofnode_get_name(node));
370 		}
371 	}
372 	if (!e->opp_nr) {
373 		printf("DVFS: %s: Can't find opp table\n", e->name);
374 		return -EINVAL;
375 	}
376 
377 	if (e->lmt.max_volt == -ENODATA)
378 		e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
379 	if (e->lmt.low_temp != -ENODATA)
380 		e->lmt.ltemp_limit = true;
381 	if (e->lmt.high_temp != -ENODATA)
382 		e->lmt.htemp_limit = true;
383 
384 	return 0;
385 }
386 
wtemp_dvfs_apply(struct udevice * dev)387 static int wtemp_dvfs_apply(struct udevice *dev)
388 {
389 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
390 	struct list_head *node;
391 	struct pm_element *e;
392 	char s_temp[TEMP_STRING_LEN];
393 	int temp, ret;
394 
395 	ret = thermal_get_temp(priv->thermal, &temp);
396 	if (ret) {
397 		printf("DVFS: Get temperature failed, ret=%d\n", ret);
398 		return ret;
399 	}
400 
401 	temp2string(temp, s_temp, TEMP_STRING_LEN);
402 	printf("DVFS: %s'c\n", s_temp);
403 
404 	/* Apply dvfs policy for all pm element */
405 	list_for_each(node, &pm_e_head) {
406 		e = list_entry(node, struct pm_element, node);
407 		__wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
408 	}
409 
410 	return 0;
411 }
412 
wtemp_dvfs_repeat_apply(struct udevice * dev)413 static int wtemp_dvfs_repeat_apply(struct udevice *dev)
414 {
415 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
416 	struct list_head *node;
417 	struct pm_element *e;
418 	enum pm_event applied;
419 	char s_temp[TEMP_STRING_LEN];
420 	int temp, ret;
421 
422 repeat:
423 	ret = thermal_get_temp(priv->thermal, &temp);
424 	if (ret) {
425 		printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
426 		return false;
427 	}
428 
429 	/* Apply dvfs policy for all pm element if there is repeat request */
430 	applied = PM_EVT_NONE;
431 	list_for_each(node, &pm_e_head) {
432 		e = list_entry(node, struct pm_element, node);
433 		if (e->lmt.ltemp_repeat)
434 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
435 		if (e->lmt.htemp_repeat)
436 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
437 	}
438 
439 	/* Everything is fine, exit */
440 	if (applied == PM_EVT_NONE)
441 		goto finish;
442 
443 	/* Check repeat result */
444 	udelay(REPEAT_PERIOD_US);
445 	list_for_each(node, &pm_e_head) {
446 		e = list_entry(node, struct pm_element, node);
447 		if (e->lmt.ltemp_repeat &&
448 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
449 			goto repeat;
450 		if (e->lmt.htemp_repeat &&
451 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
452 			goto repeat;
453 	}
454 
455 finish:
456 	list_for_each(node, &pm_e_head) {
457 		e = list_entry(node, struct pm_element, node);
458 		temp2string(temp, s_temp, TEMP_STRING_LEN);
459 		printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
460 		       s_temp, __wtemp_clk_get_rate(e),
461 		       __wtemp_regulator_get_value(e));
462 	}
463 
464 	return 0;
465 }
466 
print_e_state(void)467 static void print_e_state(void)
468 {
469 	struct pm_element *e;
470 	struct list_head *node;
471 	char s_low[TEMP_STRING_LEN];
472 	char s_high[TEMP_STRING_LEN];
473 	char s_tz[TEMP_STRING_LEN];
474 
475 	list_for_each(node, &pm_e_head) {
476 		e = list_entry(node, struct pm_element, node);
477 		if (!e->lmt.ltemp_limit &&
478 		    !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
479 			return;
480 
481 		temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
482 		temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
483 		temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
484 		printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
485 			  "h_repeat=%d, l_repeat=%d\n",
486 			  e->name, e->lmt.ltemp_limit ? s_low : NULL,
487 			  e->lmt.htemp_limit ? s_high : NULL,
488 			  e->lmt.max_volt,
489 			  e->lmt.tztemp_limit ? s_tz : NULL,
490 			  e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
491 	}
492 }
493 
wtemp_dvfs_ofdata_to_platdata(struct udevice * dev)494 static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
495 {
496 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
497 	ofnode tz_trip0, cooling_maps, node;
498 	ofnode cpus, cpu, dmc;
499 	const char *name;
500 	int ret, tz_temp;
501 	u32 phandle;
502 
503 	INIT_LIST_HEAD(&pm_e_head);
504 
505 	/* 1. Parse cpu node */
506 	priv->cpu = &pm_cpu;
507 	cpus = ofnode_path(FDT_PATH_CPUS);
508 	if (!ofnode_valid(cpus)) {
509 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
510 		goto parse_dmc;
511 	}
512 
513 	ofnode_for_each_subnode(cpu, cpus) {
514 		name = ofnode_get_property(cpu, "device_type", NULL);
515 		if (!name)
516 			continue;
517 		if (!strcmp(name, "cpu")) {
518 			ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
519 			if (ret)
520 				return ret;
521 			break;
522 		}
523 	}
524 
525 	priv->cpu->lmt.ltemp_repeat =
526 		dev_read_bool(dev, "cpu,low-temp-repeat");
527 	priv->cpu->lmt.htemp_repeat =
528 		dev_read_bool(dev, "cpu,high-temp-repeat");
529 
530 	list_add_tail(&priv->cpu->node, &pm_e_head);
531 
532 	/* 2. Parse dmc node */
533 parse_dmc:
534 	priv->dmc = &pm_dmc;
535 	dmc = ofnode_path(FDT_PATH_DMC);
536 	if (!ofnode_valid(dmc)) {
537 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
538 		goto parse_tz;
539 	}
540 	if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
541 		debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
542 		goto parse_tz;
543 	}
544 
545 	ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
546 	if (ret)
547 		return ret;
548 
549 	priv->dmc->lmt.ltemp_repeat =
550 		dev_read_bool(dev, "dmc,low-temp-repeat");
551 	priv->dmc->lmt.htemp_repeat =
552 		dev_read_bool(dev, "dmc,high-temp-repeat");
553 
554 	list_add_tail(&priv->dmc->node, &pm_e_head);
555 
556 	/* 3. Parse thermal zone node */
557 parse_tz:
558 	tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
559 	if (!ofnode_valid(tz_trip0)) {
560 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
561 		goto finish;
562 	}
563 
564 	tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
565 	if (tz_temp == -ENODATA) {
566 		debug("DVFS: Can't get thermal zone trip0 temperature\n");
567 		goto finish;
568 	}
569 
570 	cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
571 	if (!ofnode_valid(cooling_maps)) {
572 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
573 		goto finish;
574 	}
575 
576 	ofnode_for_each_subnode(node, cooling_maps) {
577 		ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
578 		name = ofnode_get_name(ofnode_get_by_phandle(phandle));
579 		if (!name)
580 			continue;
581 		if (strstr(name, "cpu")) {
582 			priv->cpu->lmt.tztemp_limit = true;
583 			priv->cpu->lmt.tz_temp = tz_temp;
584 		} else if (strstr(name, "dmc")) {
585 			priv->dmc->lmt.tztemp_limit = true;
586 			priv->dmc->lmt.tz_temp = tz_temp;
587 		}
588 	}
589 
590 finish:
591 	print_e_state();
592 
593 	return 0;
594 }
595 
596 static const struct dm_dvfs_ops wtemp_dvfs_ops = {
597 	.apply = wtemp_dvfs_apply,
598 	.repeat_apply = wtemp_dvfs_repeat_apply,
599 };
600 
wtemp_dvfs_probe(struct udevice * dev)601 static int wtemp_dvfs_probe(struct udevice *dev)
602 {
603 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
604 	int ret;
605 
606 #ifdef CONFIG_ROCKCHIP_DMC
607 	struct udevice *ram_dev;
608 
609 	/* Init dmc */
610 	ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
611 	if (ret) {
612 		printf("DVFS: Get dmc device failed, ret=%d\n", ret);
613 		return ret;
614 	}
615 #endif
616 	/* Init thermal */
617 	ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
618 	if (ret) {
619 		printf("DVFS: Get thermal device failed, ret=%d\n", ret);
620 		return ret;
621 	}
622 
623 	return 0;
624 }
625 
626 static const struct udevice_id wtemp_dvfs_match[] = {
627 	{ .compatible = "rockchip,uboot-wide-temperature", },
628 	{},
629 };
630 
631 U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
632 	.name		      = "rockchip_wide_temp_dvfs",
633 	.id		      = UCLASS_DVFS,
634 	.ops		      = &wtemp_dvfs_ops,
635 	.of_match	      = wtemp_dvfs_match,
636 	.probe		      = wtemp_dvfs_probe,
637 	.ofdata_to_platdata   = wtemp_dvfs_ofdata_to_platdata,
638 	.priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
639 };
640