xref: /rk3399_rockchip-uboot/drivers/power/dvfs/rockchip_wtemp_dvfs.c (revision 6a8f377ca2e7df7257118262bdf7e8e1412915ef)
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 
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 
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 
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 
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 		rate = clk_set_rate(&e->clk, rate);
181 
182 	return rate;
183 }
184 
185 static int __wtemp_regulator_get_value(struct pm_element *e)
186 {
187 	return regulator_get_value(e->supply);
188 }
189 
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  */
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 		__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 WARN: %s: target rate=%ld, readback rate=%ld !\n",
227 		       e->name, tgt_rate, rb_rate);
228 	if (tgt_volt != rb_volt)
229 		printf("DVFS WARN: %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  */
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 
249 	tgt_rate = e->opp[0].hz;
250 	__wtemp_clk_set_rate(e, tgt_rate);
251 	rb_rate = __wtemp_clk_get_rate(e);
252 	if (tgt_rate != rb_rate) {
253 		printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
254 		       e->name, tgt_rate, rb_rate);
255 		return;
256 	}
257 
258 	org_volt = __wtemp_regulator_get_value(e);
259 	tgt_volt = e->opp[0].uv;
260 	__wtemp_regulator_set_value(e, tgt_volt);
261 
262 	rb_volt = __wtemp_regulator_get_value(e);
263 	if (tgt_volt != rb_volt)
264 		printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
265 		       e->name, tgt_volt, rb_volt);
266 
267 	printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
268 	       e->name, org_rate, tgt_rate, org_volt, tgt_volt);
269 }
270 
271 static bool wtemp_dvfs_is_effect(struct pm_element *e,
272 				 int temp, enum pm_event evt)
273 {
274 	if (evt & PM_EVT_LOW) {
275 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
276 			return false;
277 	}
278 
279 	if (evt & PM_EVT_HIGH) {
280 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
281 			return false;
282 		else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
283 			return false;
284 	}
285 
286 	return true;
287 }
288 
289 static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
290 			      int temp, enum pm_event evt)
291 {
292 	enum pm_event ret = PM_EVT_NONE;
293 
294 	if (evt & PM_EVT_LOW) {
295 		/* Over lowest temperature: increase voltage */
296 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
297 			ret |= PM_EVT_LOW;
298 			wtemp_dvfs_low_temp_adjust(dev, e);
299 		}
300 	}
301 
302 	if (evt & PM_EVT_HIGH) {
303 		/* Over highest/thermal_zone temperature: decrease rate and voltage */
304 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
305 			ret |= PM_EVT_HIGH;
306 			wtemp_dvfs_high_temp_adjust(dev, e);
307 		} else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
308 			ret |= PM_EVT_HIGH;
309 			wtemp_dvfs_high_temp_adjust(dev, e);
310 		}
311 	}
312 
313 	return ret;
314 }
315 
316 static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
317 {
318 	ofnode supply, opp_node;
319 	u32 phandle, uv, clock[2];
320 	uint64_t hz;
321 	int ret;
322 
323 	/* Get regulator and clk */
324 	if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
325 		supply = ofnode_get_by_phandle(phandle);
326 		ret = regulator_get_by_devname(supply.np->name, &e->supply);
327 		if (ret) {
328 			printf("DVFS: %s: Get supply(%s) failed, ret=%d",
329 			       e->name, supply.np->full_name, ret);
330 			return ret;
331 		}
332 		debug("DVFS: supply: %s\n", supply.np->full_name);
333 	}
334 
335 	if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
336 		e->clk.id = clock[1];
337 		ret = rockchip_get_clk(&e->clk.dev);
338 		if (ret) {
339 			printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
340 			return ret;
341 		}
342 	}
343 
344 	/* Get opp-table & limit param */
345 	if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
346 		opp_node = ofnode_get_by_phandle(phandle);
347 		e->lmt.low_temp = ofnode_read_s32_default(opp_node,
348 						"rockchip,low-temp", -ENODATA);
349 		e->lmt.high_temp = ofnode_read_s32_default(opp_node,
350 						"rockchip,high-temp", -ENODATA);
351 		e->lmt.max_volt = ofnode_read_u32_default(opp_node,
352 						"rockchip,max-volt", -ENODATA);
353 
354 		debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
355 		      e->name, e->lmt.low_temp, e->lmt.high_temp,
356 		      e->lmt.max_volt);
357 
358 		ofnode_for_each_subnode(node, opp_node) {
359 			if (e->opp_nr >= OPP_TABLE_MAX) {
360 				printf("DVFS: over max(%d) opp table items\n",
361 				       OPP_TABLE_MAX);
362 				break;
363 			}
364 			ofnode_read_u64(node, "opp-hz", &hz);
365 			ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
366 			e->opp[e->opp_nr].hz = hz;
367 			e->opp[e->opp_nr].uv = uv;
368 			e->opp_nr++;
369 			debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
370 			      e->name, e->opp_nr - 1,
371 			      hz, uv, ofnode_get_name(node));
372 		}
373 	}
374 	if (!e->opp_nr) {
375 		printf("DVFS: %s: Can't find opp table\n", e->name);
376 		return -EINVAL;
377 	}
378 
379 	if (e->lmt.max_volt == -ENODATA)
380 		e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
381 	if (e->lmt.low_temp != -ENODATA)
382 		e->lmt.ltemp_limit = true;
383 	if (e->lmt.high_temp != -ENODATA)
384 		e->lmt.htemp_limit = true;
385 
386 	return 0;
387 }
388 
389 static int wtemp_dvfs_apply(struct udevice *dev)
390 {
391 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
392 	struct list_head *node;
393 	struct pm_element *e;
394 	char s_temp[TEMP_STRING_LEN];
395 	int temp, ret;
396 
397 	ret = thermal_get_temp(priv->thermal, &temp);
398 	if (ret) {
399 		printf("DVFS: Get temperature failed, ret=%d\n", ret);
400 		return ret;
401 	}
402 
403 	temp2string(temp, s_temp, TEMP_STRING_LEN);
404 	printf("DVFS: %s'c\n", s_temp);
405 
406 	/* Apply dvfs policy for all pm element */
407 	list_for_each(node, &pm_e_head) {
408 		e = list_entry(node, struct pm_element, node);
409 		__wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
410 	}
411 
412 	return 0;
413 }
414 
415 static int wtemp_dvfs_repeat_apply(struct udevice *dev)
416 {
417 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
418 	struct list_head *node;
419 	struct pm_element *e;
420 	enum pm_event applied;
421 	char s_temp[TEMP_STRING_LEN];
422 	int temp, ret;
423 
424 repeat:
425 	ret = thermal_get_temp(priv->thermal, &temp);
426 	if (ret) {
427 		printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
428 		return false;
429 	}
430 
431 	/* Apply dvfs policy for all pm element if there is repeat request */
432 	applied = PM_EVT_NONE;
433 	list_for_each(node, &pm_e_head) {
434 		e = list_entry(node, struct pm_element, node);
435 		if (e->lmt.ltemp_repeat)
436 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
437 		if (e->lmt.htemp_repeat)
438 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
439 	}
440 
441 	/* Everything is fine, exit */
442 	if (applied == PM_EVT_NONE)
443 		goto finish;
444 
445 	/* Check repeat result */
446 	udelay(REPEAT_PERIOD_US);
447 	list_for_each(node, &pm_e_head) {
448 		e = list_entry(node, struct pm_element, node);
449 		if (e->lmt.ltemp_repeat &&
450 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
451 			goto repeat;
452 		if (e->lmt.htemp_repeat &&
453 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
454 			goto repeat;
455 	}
456 
457 finish:
458 	list_for_each(node, &pm_e_head) {
459 		e = list_entry(node, struct pm_element, node);
460 		temp2string(temp, s_temp, TEMP_STRING_LEN);
461 		printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
462 		       s_temp, __wtemp_clk_get_rate(e),
463 		       __wtemp_regulator_get_value(e));
464 	}
465 
466 	return 0;
467 }
468 
469 static void print_e_state(void)
470 {
471 	struct pm_element *e;
472 	struct list_head *node;
473 	char s_low[TEMP_STRING_LEN];
474 	char s_high[TEMP_STRING_LEN];
475 	char s_tz[TEMP_STRING_LEN];
476 
477 	list_for_each(node, &pm_e_head) {
478 		e = list_entry(node, struct pm_element, node);
479 		if (!e->lmt.ltemp_limit &&
480 		    !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
481 			return;
482 
483 		temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
484 		temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
485 		temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
486 		printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
487 			  "h_repeat=%d, l_repeat=%d\n",
488 			  e->name, e->lmt.ltemp_limit ? s_low : NULL,
489 			  e->lmt.htemp_limit ? s_high : NULL,
490 			  e->lmt.max_volt,
491 			  e->lmt.tztemp_limit ? s_tz : NULL,
492 			  e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
493 	}
494 }
495 
496 static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
497 {
498 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
499 	ofnode tz_trip0, cooling_maps, node;
500 	ofnode cpus, cpu, dmc;
501 	const char *name;
502 	int ret, tz_temp;
503 	u32 phandle;
504 
505 	INIT_LIST_HEAD(&pm_e_head);
506 
507 	/* 1. Parse cpu node */
508 	priv->cpu = &pm_cpu;
509 	cpus = ofnode_path(FDT_PATH_CPUS);
510 	if (!ofnode_valid(cpus)) {
511 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
512 		goto parse_dmc;
513 	}
514 
515 	ofnode_for_each_subnode(cpu, cpus) {
516 		name = ofnode_get_property(cpu, "device_type", NULL);
517 		if (!name)
518 			continue;
519 		if (!strcmp(name, "cpu")) {
520 			ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
521 			if (ret)
522 				return ret;
523 			break;
524 		}
525 	}
526 
527 	priv->cpu->lmt.ltemp_repeat =
528 		dev_read_bool(dev, "cpu,low-temp-repeat");
529 	priv->cpu->lmt.htemp_repeat =
530 		dev_read_bool(dev, "cpu,high-temp-repeat");
531 
532 	list_add_tail(&priv->cpu->node, &pm_e_head);
533 
534 	/* 2. Parse dmc node */
535 parse_dmc:
536 	priv->dmc = &pm_dmc;
537 	dmc = ofnode_path(FDT_PATH_DMC);
538 	if (!ofnode_valid(dmc)) {
539 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
540 		goto parse_tz;
541 	}
542 	if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
543 		debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
544 		goto parse_tz;
545 	}
546 
547 	ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
548 	if (ret)
549 		return ret;
550 
551 	priv->dmc->lmt.ltemp_repeat =
552 		dev_read_bool(dev, "dmc,low-temp-repeat");
553 	priv->dmc->lmt.htemp_repeat =
554 		dev_read_bool(dev, "dmc,high-temp-repeat");
555 
556 	list_add_tail(&priv->dmc->node, &pm_e_head);
557 
558 	/* 3. Parse thermal zone node */
559 parse_tz:
560 	tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
561 	if (!ofnode_valid(tz_trip0)) {
562 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
563 		goto finish;
564 	}
565 
566 	tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
567 	if (tz_temp == -ENODATA) {
568 		debug("DVFS: Can't get thermal zone trip0 temperature\n");
569 		goto finish;
570 	}
571 
572 	cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
573 	if (!ofnode_valid(cooling_maps)) {
574 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
575 		goto finish;
576 	}
577 
578 	ofnode_for_each_subnode(node, cooling_maps) {
579 		ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
580 		name = ofnode_get_name(ofnode_get_by_phandle(phandle));
581 		if (!name)
582 			continue;
583 		if (strstr(name, "cpu")) {
584 			priv->cpu->lmt.tztemp_limit = true;
585 			priv->cpu->lmt.tz_temp = tz_temp;
586 		} else if (strstr(name, "dmc")) {
587 			priv->dmc->lmt.tztemp_limit = true;
588 			priv->dmc->lmt.tz_temp = tz_temp;
589 		}
590 	}
591 
592 finish:
593 	print_e_state();
594 
595 	return 0;
596 }
597 
598 static const struct dm_dvfs_ops wtemp_dvfs_ops = {
599 	.apply = wtemp_dvfs_apply,
600 	.repeat_apply = wtemp_dvfs_repeat_apply,
601 };
602 
603 static int wtemp_dvfs_probe(struct udevice *dev)
604 {
605 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
606 	int ret;
607 
608 #ifdef CONFIG_ROCKCHIP_DMC
609 	struct udevice *ram_dev;
610 
611 	/* Init dmc */
612 	ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
613 	if (ret) {
614 		printf("DVFS: Get dmc device failed, ret=%d\n", ret);
615 		return ret;
616 	}
617 #endif
618 	/* Init thermal */
619 	ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
620 	if (ret) {
621 		printf("DVFS: Get thermal device failed, ret=%d\n", ret);
622 		return ret;
623 	}
624 
625 	return 0;
626 }
627 
628 static const struct udevice_id wtemp_dvfs_match[] = {
629 	{ .compatible = "rockchip,uboot-wide-temperature", },
630 	{},
631 };
632 
633 U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
634 	.name		      = "rockchip_wide_temp_dvfs",
635 	.id		      = UCLASS_DVFS,
636 	.ops		      = &wtemp_dvfs_ops,
637 	.of_match	      = wtemp_dvfs_match,
638 	.probe		      = wtemp_dvfs_probe,
639 	.ofdata_to_platdata   = wtemp_dvfs_ofdata_to_platdata,
640 	.priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
641 };
642