xref: /rk3399_rockchip-uboot/drivers/power/dvfs/rockchip_wtemp_dvfs.c (revision 3e169ed6d1dfc694be0c8d48ef58532f2407d1ff)
10eea0d25SJoseph Chen // SPDX-License-Identifier: GPL-2.0
20eea0d25SJoseph Chen /*
30eea0d25SJoseph Chen  * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
40eea0d25SJoseph Chen  */
50eea0d25SJoseph Chen #include <common.h>
60eea0d25SJoseph Chen #include <dm.h>
70eea0d25SJoseph Chen #include <clk.h>
80eea0d25SJoseph Chen #include <dvfs.h>
90eea0d25SJoseph Chen #include <thermal.h>
100eea0d25SJoseph Chen #include <linux/list.h>
110eea0d25SJoseph Chen 
120eea0d25SJoseph Chen #include <asm/arch/clock.h>
130eea0d25SJoseph Chen #include <power/regulator.h>
140eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
150eea0d25SJoseph Chen #include <asm/arch/rockchip_dmc.h>
160eea0d25SJoseph Chen #endif
170eea0d25SJoseph Chen 
180eea0d25SJoseph Chen /*
190eea0d25SJoseph Chen  * # This is a simple wide temperature(ie. wtemp) dvfs driver, the policy is:
200eea0d25SJoseph Chen  *
210eea0d25SJoseph Chen  * 1. U-Boot parse cpu/dmc opp table from kernel dtb, anyone of
220eea0d25SJoseph Chen  *    "rockchip,low-temp = <...>" and "rockchip,high-temp = <...>" present in
230eea0d25SJoseph Chen  *    cpu/dmc nodes means wtemp is enabled.
240eea0d25SJoseph Chen  *
250eea0d25SJoseph Chen  *    1.1. When temperature trigger "rockchip,low-temp", increase 50mv voltage
260eea0d25SJoseph Chen  *         as target voltage. If target voltage is over "rockchip,max-volt",
270eea0d25SJoseph Chen  *         just set "rockchip,max-volt" as target voltage and lower 2 level freq,
280eea0d25SJoseph Chen  *
290eea0d25SJoseph Chen  *    1.2. When temperature trigger "rockchip,high-temp", just apply opp table[0]
300eea0d25SJoseph Chen  *         voltage and freq.
310eea0d25SJoseph Chen  *
320eea0d25SJoseph Chen  * 2. U-Boot parse cpu/dmc thermal zone "trip-point-0" temperature from kernel
330eea0d25SJoseph Chen  *    dtb, and apply the same rules as above [1.2] policy.
340eea0d25SJoseph Chen  *
350eea0d25SJoseph Chen  *
360eea0d25SJoseph Chen  * # The dvfs policy apply moment is:
370eea0d25SJoseph Chen  *
380eea0d25SJoseph Chen  * 1. Appy it after clk and regulator drivers setup;
390eea0d25SJoseph Chen  * 2. Repeat apply it by CONFIG_PREBOOT command until achieve the target
400eea0d25SJoseph Chen  *    temperature. user should add: #define CONFIG_PREBOOT "dvfs repeat" and
410eea0d25SJoseph Chen  *    assign repeat property in dts:
420eea0d25SJoseph Chen  *
430eea0d25SJoseph Chen  *	uboot-wide-temperature {
440eea0d25SJoseph Chen  *		status = "okay";
450eea0d25SJoseph Chen  *		compatible = "rockchip,uboot-wide-temperature";
460eea0d25SJoseph Chen  *
470eea0d25SJoseph Chen  *		cpu,low-temp-repeat;
480eea0d25SJoseph Chen  *		cpu,high-temp-repeat;
490eea0d25SJoseph Chen  *		dmc,low-temp-repeat;
500eea0d25SJoseph Chen  *		dmc,high-temp-repeat;
510eea0d25SJoseph Chen  *	};
520eea0d25SJoseph Chen  */
530eea0d25SJoseph Chen 
540eea0d25SJoseph Chen #define FDT_PATH_CPUS		"/cpus"
550eea0d25SJoseph Chen #define FDT_PATH_DMC		"/dmc"
560eea0d25SJoseph Chen #define FDT_PATH_THREMAL_TRIP_POINT0	\
570eea0d25SJoseph Chen 	"/thermal-zones/soc-thermal/trips/trip-point-0"
580eea0d25SJoseph Chen #define FDT_PATH_THREMAL_COOLING_MAPS	\
590eea0d25SJoseph Chen 	"/thermal-zones/soc-thermal/cooling-maps"
600eea0d25SJoseph Chen 
610eea0d25SJoseph Chen #define OPP_TABLE_MAX		20
620eea0d25SJoseph Chen #define RATE_LOWER_LEVEL_N	2
630eea0d25SJoseph Chen #define DIFF_VOLTAGE_UV		50000
640eea0d25SJoseph Chen #define TEMP_STRING_LEN		12
650eea0d25SJoseph Chen #define REPEAT_PERIOD_US	1000000
660eea0d25SJoseph Chen 
670eea0d25SJoseph Chen static LIST_HEAD(pm_e_head);
680eea0d25SJoseph Chen 
690eea0d25SJoseph Chen enum pm_id {
700eea0d25SJoseph Chen 	PM_CPU,
710eea0d25SJoseph Chen 	PM_DMC,
720eea0d25SJoseph Chen };
730eea0d25SJoseph Chen 
740eea0d25SJoseph Chen enum pm_event {
750eea0d25SJoseph Chen 	PM_EVT_NONE = 0x0,
760eea0d25SJoseph Chen 	PM_EVT_LOW  = 0x1,
770eea0d25SJoseph Chen 	PM_EVT_HIGH = 0x2,
780eea0d25SJoseph Chen 	PM_EVT_BOTH = PM_EVT_LOW | PM_EVT_HIGH,
790eea0d25SJoseph Chen };
800eea0d25SJoseph Chen 
810eea0d25SJoseph Chen struct opp_table {
820eea0d25SJoseph Chen 	u64 hz;
830eea0d25SJoseph Chen 	u32 uv;
840eea0d25SJoseph Chen };
850eea0d25SJoseph Chen 
860eea0d25SJoseph Chen struct lmt_param {
870eea0d25SJoseph Chen 	int low_temp;		/* milli degree */
880eea0d25SJoseph Chen 	int high_temp;		/* milli degree */
890eea0d25SJoseph Chen 	int tz_temp;		/* milli degree */
900eea0d25SJoseph Chen 	int max_volt;		/* uV */
910eea0d25SJoseph Chen 
920eea0d25SJoseph Chen 	bool htemp_repeat;
930eea0d25SJoseph Chen 	bool ltemp_repeat;
940eea0d25SJoseph Chen 
950eea0d25SJoseph Chen 	bool ltemp_limit;
960eea0d25SJoseph Chen 	bool htemp_limit;
970eea0d25SJoseph Chen 	bool tztemp_limit;
980eea0d25SJoseph Chen };
990eea0d25SJoseph Chen 
1000eea0d25SJoseph Chen struct pm_element {
1010eea0d25SJoseph Chen 	int id;
1020eea0d25SJoseph Chen 	const char *name;
1030eea0d25SJoseph Chen 	const char *supply_name;
1040eea0d25SJoseph Chen 	int volt_diff;
1050eea0d25SJoseph Chen 	u32 opp_nr;
1060eea0d25SJoseph Chen 	struct opp_table opp[OPP_TABLE_MAX];
1070eea0d25SJoseph Chen 	struct lmt_param lmt;
1080eea0d25SJoseph Chen 	struct udevice *supply;
1090eea0d25SJoseph Chen 	struct clk clk;
1100eea0d25SJoseph Chen 	struct list_head node;
1110eea0d25SJoseph Chen };
1120eea0d25SJoseph Chen 
1130eea0d25SJoseph Chen struct wtemp_dvfs_priv {
1140eea0d25SJoseph Chen 	struct udevice *thermal;
1150eea0d25SJoseph Chen 	struct pm_element *cpu;
1160eea0d25SJoseph Chen 	struct pm_element *dmc;
1170eea0d25SJoseph Chen };
1180eea0d25SJoseph Chen 
1190eea0d25SJoseph Chen static struct pm_element pm_cpu = {
1200eea0d25SJoseph Chen 	.id		= PM_CPU,
1210eea0d25SJoseph Chen 	.name		= "cpu",
1220eea0d25SJoseph Chen 	.supply_name	= "cpu-supply",
1230eea0d25SJoseph Chen 	.volt_diff	= DIFF_VOLTAGE_UV,
1240eea0d25SJoseph Chen };
1250eea0d25SJoseph Chen 
1260eea0d25SJoseph Chen static struct pm_element pm_dmc = {
1270eea0d25SJoseph Chen 	.id		= PM_DMC,
1280eea0d25SJoseph Chen 	.name		= "dmc",
1290eea0d25SJoseph Chen 	.supply_name	= "center-supply",
1300eea0d25SJoseph Chen 	.volt_diff	= DIFF_VOLTAGE_UV,
1310eea0d25SJoseph Chen };
1320eea0d25SJoseph Chen 
temp2string(int temp,char * data,int len)1330eea0d25SJoseph Chen static void temp2string(int temp, char *data, int len)
1340eea0d25SJoseph Chen {
1350eea0d25SJoseph Chen 	int decimal_point;
1360eea0d25SJoseph Chen 	int integer;
1370eea0d25SJoseph Chen 
1380eea0d25SJoseph Chen 	integer = abs(temp) / 1000;
1390eea0d25SJoseph Chen 	decimal_point = abs(temp) % 1000;
1400eea0d25SJoseph Chen 	snprintf(data, len, "%s%d.%d",
1410eea0d25SJoseph Chen 		 temp < 0 ? "-" : "", integer, decimal_point);
1420eea0d25SJoseph Chen }
1430eea0d25SJoseph Chen 
wtemp_get_lowlevel_rate(ulong rate,u32 level,struct pm_element * e)1440eea0d25SJoseph Chen static ulong wtemp_get_lowlevel_rate(ulong rate, u32 level,
1450eea0d25SJoseph Chen 				     struct pm_element *e)
1460eea0d25SJoseph Chen {
1470eea0d25SJoseph Chen 	struct opp_table *opp;
1480eea0d25SJoseph Chen 	int i, count, idx = 0;
1490eea0d25SJoseph Chen 
1500eea0d25SJoseph Chen 	opp = e->opp;
1510eea0d25SJoseph Chen 	count = e->opp_nr;
1520eea0d25SJoseph Chen 
1530eea0d25SJoseph Chen 	for (i = 0; i < count; i++) {
1540eea0d25SJoseph Chen 		if (opp[i].hz >= rate) {
1550eea0d25SJoseph Chen 			idx = (i <= level) ? 0 : i - level;
1560eea0d25SJoseph Chen 			break;
1570eea0d25SJoseph Chen 		}
1580eea0d25SJoseph Chen 	}
1590eea0d25SJoseph Chen 
1600eea0d25SJoseph Chen 	return opp[idx].hz;
1610eea0d25SJoseph Chen }
1620eea0d25SJoseph Chen 
__wtemp_clk_get_rate(struct pm_element * e)1630eea0d25SJoseph Chen static ulong __wtemp_clk_get_rate(struct pm_element *e)
1640eea0d25SJoseph Chen {
1650eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
1660eea0d25SJoseph Chen 	if (e->id == PM_DMC)
1670eea0d25SJoseph Chen 		return rockchip_ddrclk_sip_recalc_rate_v2();
1680eea0d25SJoseph Chen #endif
1690eea0d25SJoseph Chen 	return clk_get_rate(&e->clk);
1700eea0d25SJoseph Chen }
1710eea0d25SJoseph Chen 
__wtemp_clk_set_rate(struct pm_element * e,ulong rate)1720eea0d25SJoseph Chen static ulong __wtemp_clk_set_rate(struct pm_element *e, ulong rate)
1730eea0d25SJoseph Chen {
1740eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
1750eea0d25SJoseph Chen 	if (e->id == PM_DMC) {
1760eea0d25SJoseph Chen 		rate = rockchip_ddrclk_sip_round_rate_v2(rate);
1770eea0d25SJoseph Chen 		rockchip_ddrclk_sip_set_rate_v2(rate);
1780eea0d25SJoseph Chen 	} else
1790eea0d25SJoseph Chen #endif
180*3e169ed6SJoseph Chen 		rate = clk_set_rate(&e->clk, rate);
1810eea0d25SJoseph Chen 
1820eea0d25SJoseph Chen 	return rate;
1830eea0d25SJoseph Chen }
1840eea0d25SJoseph Chen 
__wtemp_regulator_get_value(struct pm_element * e)1850eea0d25SJoseph Chen static int __wtemp_regulator_get_value(struct pm_element *e)
1860eea0d25SJoseph Chen {
1870eea0d25SJoseph Chen 	return regulator_get_value(e->supply);
1880eea0d25SJoseph Chen }
1890eea0d25SJoseph Chen 
__wtemp_regulator_set_value(struct pm_element * e,int value)1900eea0d25SJoseph Chen static int __wtemp_regulator_set_value(struct pm_element *e, int value)
1910eea0d25SJoseph Chen {
1920eea0d25SJoseph Chen 	return regulator_set_value(e->supply, value);
1930eea0d25SJoseph Chen }
1940eea0d25SJoseph Chen 
1950eea0d25SJoseph Chen /*
1960eea0d25SJoseph Chen  * Policy: Increase voltage
1970eea0d25SJoseph Chen  *
1980eea0d25SJoseph Chen  * 1. target volt = original volt + diff volt;
1990eea0d25SJoseph Chen  * 2. If target volt is not over max_volt, just set it;
2000eea0d25SJoseph Chen  * 3. Otherwise set max_volt as target volt and lower the rate(front N level).
2010eea0d25SJoseph Chen  */
wtemp_dvfs_low_temp_adjust(struct udevice * dev,struct pm_element * e)2020eea0d25SJoseph Chen static void wtemp_dvfs_low_temp_adjust(struct udevice *dev, struct pm_element *e)
2030eea0d25SJoseph Chen {
2040eea0d25SJoseph Chen 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
2050eea0d25SJoseph Chen 	ulong org_rate, tgt_rate, rb_rate;
2060eea0d25SJoseph Chen 	int org_volt, tgt_volt, rb_volt;
2070eea0d25SJoseph Chen 
2080eea0d25SJoseph Chen 	org_rate = __wtemp_clk_get_rate(e);
2090eea0d25SJoseph Chen 	org_volt = __wtemp_regulator_get_value(e);
2100eea0d25SJoseph Chen 	tgt_volt = org_volt + e->volt_diff;
2110eea0d25SJoseph Chen 	if ((e->lmt.max_volt != -ENODATA) && (tgt_volt > e->lmt.max_volt)) {
2120eea0d25SJoseph Chen 		tgt_volt = e->lmt.max_volt;
2130eea0d25SJoseph Chen 		__wtemp_regulator_set_value(e, tgt_volt);
2140eea0d25SJoseph Chen 		tgt_rate = wtemp_get_lowlevel_rate(org_rate,
2150eea0d25SJoseph Chen 						RATE_LOWER_LEVEL_N, priv->cpu);
216*3e169ed6SJoseph Chen 		__wtemp_clk_set_rate(e, tgt_rate);
2170eea0d25SJoseph Chen 	} else {
2180eea0d25SJoseph Chen 		__wtemp_regulator_set_value(e, tgt_volt);
2190eea0d25SJoseph Chen 		tgt_rate = org_rate;
2200eea0d25SJoseph Chen 	}
2210eea0d25SJoseph Chen 
2220eea0d25SJoseph Chen 	/* Check */
2230eea0d25SJoseph Chen 	rb_rate = __wtemp_clk_get_rate(e);
2240eea0d25SJoseph Chen 	rb_volt = __wtemp_regulator_get_value(e);
2250eea0d25SJoseph Chen 	if (tgt_rate != rb_rate)
226*3e169ed6SJoseph Chen 		printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
2270eea0d25SJoseph Chen 		       e->name, tgt_rate, rb_rate);
2280eea0d25SJoseph Chen 	if (tgt_volt != rb_volt)
229*3e169ed6SJoseph Chen 		printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
2300eea0d25SJoseph Chen 		       e->name, tgt_volt, rb_volt);
2310eea0d25SJoseph Chen 
2320eea0d25SJoseph Chen 	printf("DVFS: %s(low): %ld->%ld Hz, %d->%d uV\n",
2330eea0d25SJoseph Chen 	       e->name, org_rate, rb_rate, org_volt, rb_volt);
2340eea0d25SJoseph Chen }
2350eea0d25SJoseph Chen 
2360eea0d25SJoseph Chen /*
2370eea0d25SJoseph Chen  * Policy:
2380eea0d25SJoseph Chen  *
2390eea0d25SJoseph Chen  * Just set opp table[0] volt and rate, i.e. the lowest performance.
2400eea0d25SJoseph Chen  */
wtemp_dvfs_high_temp_adjust(struct udevice * dev,struct pm_element * e)2410eea0d25SJoseph Chen static void wtemp_dvfs_high_temp_adjust(struct udevice *dev, struct pm_element *e)
2420eea0d25SJoseph Chen {
2430eea0d25SJoseph Chen 	ulong org_rate, tgt_rate, rb_rate;
2440eea0d25SJoseph Chen 	int org_volt, tgt_volt, rb_volt;
2450eea0d25SJoseph Chen 
2460eea0d25SJoseph Chen 	/* Apply opp[0] */
2470eea0d25SJoseph Chen 	org_rate = __wtemp_clk_get_rate(e);
248*3e169ed6SJoseph Chen 
2490eea0d25SJoseph Chen 	tgt_rate = e->opp[0].hz;
250*3e169ed6SJoseph Chen 	__wtemp_clk_set_rate(e, tgt_rate);
251*3e169ed6SJoseph Chen 	rb_rate = __wtemp_clk_get_rate(e);
252*3e169ed6SJoseph Chen 	if (tgt_rate != rb_rate) {
253*3e169ed6SJoseph Chen 		printf("DVFS WARN: %s: target rate=%ld, readback rate=%ld !\n",
254*3e169ed6SJoseph Chen 		       e->name, tgt_rate, rb_rate);
255*3e169ed6SJoseph Chen 		return;
256*3e169ed6SJoseph Chen 	}
2570eea0d25SJoseph Chen 
2580eea0d25SJoseph Chen 	org_volt = __wtemp_regulator_get_value(e);
2590eea0d25SJoseph Chen 	tgt_volt = e->opp[0].uv;
2600eea0d25SJoseph Chen 	__wtemp_regulator_set_value(e, tgt_volt);
2610eea0d25SJoseph Chen 
2620eea0d25SJoseph Chen 	rb_volt = __wtemp_regulator_get_value(e);
2630eea0d25SJoseph Chen 	if (tgt_volt != rb_volt)
264*3e169ed6SJoseph Chen 		printf("DVFS WARN: %s: target volt=%d, readback volt=%d !\n",
2650eea0d25SJoseph Chen 		       e->name, tgt_volt, rb_volt);
2660eea0d25SJoseph Chen 
2670eea0d25SJoseph Chen 	printf("DVFS: %s(high): %ld->%ld Hz, %d->%d uV\n",
2680eea0d25SJoseph Chen 	       e->name, org_rate, tgt_rate, org_volt, tgt_volt);
2690eea0d25SJoseph Chen }
2700eea0d25SJoseph Chen 
wtemp_dvfs_is_effect(struct pm_element * e,int temp,enum pm_event evt)2710eea0d25SJoseph Chen static bool wtemp_dvfs_is_effect(struct pm_element *e,
2720eea0d25SJoseph Chen 				 int temp, enum pm_event evt)
2730eea0d25SJoseph Chen {
2740eea0d25SJoseph Chen 	if (evt & PM_EVT_LOW) {
2750eea0d25SJoseph Chen 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp)
2760eea0d25SJoseph Chen 			return false;
2770eea0d25SJoseph Chen 	}
2780eea0d25SJoseph Chen 
2790eea0d25SJoseph Chen 	if (evt & PM_EVT_HIGH) {
2800eea0d25SJoseph Chen 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp)
2810eea0d25SJoseph Chen 			return false;
2820eea0d25SJoseph Chen 		else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp)
2830eea0d25SJoseph Chen 			return false;
2840eea0d25SJoseph Chen 	}
2850eea0d25SJoseph Chen 
2860eea0d25SJoseph Chen 	return true;
2870eea0d25SJoseph Chen }
2880eea0d25SJoseph Chen 
__wtemp_dvfs_apply(struct udevice * dev,struct pm_element * e,int temp,enum pm_event evt)2890eea0d25SJoseph Chen static int __wtemp_dvfs_apply(struct udevice *dev, struct pm_element *e,
2900eea0d25SJoseph Chen 			      int temp, enum pm_event evt)
2910eea0d25SJoseph Chen {
2920eea0d25SJoseph Chen 	enum pm_event ret = PM_EVT_NONE;
2930eea0d25SJoseph Chen 
2940eea0d25SJoseph Chen 	if (evt & PM_EVT_LOW) {
2950eea0d25SJoseph Chen 		/* Over lowest temperature: increase voltage */
2960eea0d25SJoseph Chen 		if (e->lmt.ltemp_limit && temp <= e->lmt.low_temp) {
2970eea0d25SJoseph Chen 			ret |= PM_EVT_LOW;
2980eea0d25SJoseph Chen 			wtemp_dvfs_low_temp_adjust(dev, e);
2990eea0d25SJoseph Chen 		}
3000eea0d25SJoseph Chen 	}
3010eea0d25SJoseph Chen 
3020eea0d25SJoseph Chen 	if (evt & PM_EVT_HIGH) {
3030eea0d25SJoseph Chen 		/* Over highest/thermal_zone temperature: decrease rate and voltage */
3040eea0d25SJoseph Chen 		if (e->lmt.tztemp_limit && temp >= e->lmt.tz_temp) {
3050eea0d25SJoseph Chen 			ret |= PM_EVT_HIGH;
3060eea0d25SJoseph Chen 			wtemp_dvfs_high_temp_adjust(dev, e);
3070eea0d25SJoseph Chen 		} else if (e->lmt.htemp_limit && temp >= e->lmt.high_temp) {
3080eea0d25SJoseph Chen 			ret |= PM_EVT_HIGH;
3090eea0d25SJoseph Chen 			wtemp_dvfs_high_temp_adjust(dev, e);
3100eea0d25SJoseph Chen 		}
3110eea0d25SJoseph Chen 	}
3120eea0d25SJoseph Chen 
3130eea0d25SJoseph Chen 	return ret;
3140eea0d25SJoseph Chen }
3150eea0d25SJoseph Chen 
__wtemp_common_ofdata_to_platdata(ofnode node,struct pm_element * e)3160eea0d25SJoseph Chen static int __wtemp_common_ofdata_to_platdata(ofnode node, struct pm_element *e)
3170eea0d25SJoseph Chen {
3180eea0d25SJoseph Chen 	ofnode supply, opp_node;
3190eea0d25SJoseph Chen 	u32 phandle, uv, clock[2];
3200eea0d25SJoseph Chen 	uint64_t hz;
3210eea0d25SJoseph Chen 	int ret;
3220eea0d25SJoseph Chen 
3230eea0d25SJoseph Chen 	/* Get regulator and clk */
3240eea0d25SJoseph Chen 	if (!ofnode_read_u32(node, e->supply_name, &phandle)) {
3250eea0d25SJoseph Chen 		supply = ofnode_get_by_phandle(phandle);
3260eea0d25SJoseph Chen 		ret = regulator_get_by_devname(supply.np->name, &e->supply);
3270eea0d25SJoseph Chen 		if (ret) {
3280eea0d25SJoseph Chen 			printf("DVFS: %s: Get supply(%s) failed, ret=%d",
3290eea0d25SJoseph Chen 			       e->name, supply.np->full_name, ret);
3300eea0d25SJoseph Chen 			return ret;
3310eea0d25SJoseph Chen 		}
3320eea0d25SJoseph Chen 		debug("DVFS: supply: %s\n", supply.np->full_name);
3330eea0d25SJoseph Chen 	}
3340eea0d25SJoseph Chen 
3350eea0d25SJoseph Chen 	if (!ofnode_read_u32_array(node, "clocks", clock, ARRAY_SIZE(clock))) {
3360eea0d25SJoseph Chen 		e->clk.id = clock[1];
3370eea0d25SJoseph Chen 		ret = rockchip_get_clk(&e->clk.dev);
3380eea0d25SJoseph Chen 		if (ret) {
3390eea0d25SJoseph Chen 			printf("DVFS: %s: Get clk failed, ret=%d\n", e->name, ret);
3400eea0d25SJoseph Chen 			return ret;
3410eea0d25SJoseph Chen 		}
3420eea0d25SJoseph Chen 	}
3430eea0d25SJoseph Chen 
3440eea0d25SJoseph Chen 	/* Get opp-table & limit param */
3450eea0d25SJoseph Chen 	if (!ofnode_read_u32(node, "operating-points-v2", &phandle)) {
3460eea0d25SJoseph Chen 		opp_node = ofnode_get_by_phandle(phandle);
3470eea0d25SJoseph Chen 		e->lmt.low_temp = ofnode_read_s32_default(opp_node,
3480eea0d25SJoseph Chen 						"rockchip,low-temp", -ENODATA);
3490eea0d25SJoseph Chen 		e->lmt.high_temp = ofnode_read_s32_default(opp_node,
3500eea0d25SJoseph Chen 						"rockchip,high-temp", -ENODATA);
3510eea0d25SJoseph Chen 		e->lmt.max_volt = ofnode_read_u32_default(opp_node,
3520eea0d25SJoseph Chen 						"rockchip,max-volt", -ENODATA);
3530eea0d25SJoseph Chen 
3540eea0d25SJoseph Chen 		debug("DVFS: %s: low-temp=%d, high-temp=%d, max-volt=%d\n",
3550eea0d25SJoseph Chen 		      e->name, e->lmt.low_temp, e->lmt.high_temp,
3560eea0d25SJoseph Chen 		      e->lmt.max_volt);
3570eea0d25SJoseph Chen 
3580eea0d25SJoseph Chen 		ofnode_for_each_subnode(node, opp_node) {
3590eea0d25SJoseph Chen 			if (e->opp_nr >= OPP_TABLE_MAX) {
3600eea0d25SJoseph Chen 				printf("DVFS: over max(%d) opp table items\n",
3610eea0d25SJoseph Chen 				       OPP_TABLE_MAX);
3620eea0d25SJoseph Chen 				break;
3630eea0d25SJoseph Chen 			}
3640eea0d25SJoseph Chen 			ofnode_read_u64(node, "opp-hz", &hz);
3650eea0d25SJoseph Chen 			ofnode_read_u32_array(node, "opp-microvolt", &uv, 1);
3660eea0d25SJoseph Chen 			e->opp[e->opp_nr].hz = hz;
3670eea0d25SJoseph Chen 			e->opp[e->opp_nr].uv = uv;
3680eea0d25SJoseph Chen 			e->opp_nr++;
3690eea0d25SJoseph Chen 			debug("DVFS: %s: opp[%d]: hz=%lld, uv=%d, %s\n",
3700eea0d25SJoseph Chen 			      e->name, e->opp_nr - 1,
3710eea0d25SJoseph Chen 			      hz, uv, ofnode_get_name(node));
3720eea0d25SJoseph Chen 		}
3730eea0d25SJoseph Chen 	}
3740eea0d25SJoseph Chen 	if (!e->opp_nr) {
3750eea0d25SJoseph Chen 		printf("DVFS: %s: Can't find opp table\n", e->name);
3760eea0d25SJoseph Chen 		return -EINVAL;
3770eea0d25SJoseph Chen 	}
3780eea0d25SJoseph Chen 
3790eea0d25SJoseph Chen 	if (e->lmt.max_volt == -ENODATA)
3800eea0d25SJoseph Chen 		e->lmt.max_volt = e->opp[e->opp_nr - 1].uv;
3810eea0d25SJoseph Chen 	if (e->lmt.low_temp != -ENODATA)
3820eea0d25SJoseph Chen 		e->lmt.ltemp_limit = true;
3830eea0d25SJoseph Chen 	if (e->lmt.high_temp != -ENODATA)
3840eea0d25SJoseph Chen 		e->lmt.htemp_limit = true;
3850eea0d25SJoseph Chen 
3860eea0d25SJoseph Chen 	return 0;
3870eea0d25SJoseph Chen }
3880eea0d25SJoseph Chen 
wtemp_dvfs_apply(struct udevice * dev)3890eea0d25SJoseph Chen static int wtemp_dvfs_apply(struct udevice *dev)
3900eea0d25SJoseph Chen {
3910eea0d25SJoseph Chen 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
3920eea0d25SJoseph Chen 	struct list_head *node;
3930eea0d25SJoseph Chen 	struct pm_element *e;
3940eea0d25SJoseph Chen 	char s_temp[TEMP_STRING_LEN];
3950eea0d25SJoseph Chen 	int temp, ret;
3960eea0d25SJoseph Chen 
3970eea0d25SJoseph Chen 	ret = thermal_get_temp(priv->thermal, &temp);
3980eea0d25SJoseph Chen 	if (ret) {
3990eea0d25SJoseph Chen 		printf("DVFS: Get temperature failed, ret=%d\n", ret);
4000eea0d25SJoseph Chen 		return ret;
4010eea0d25SJoseph Chen 	}
4020eea0d25SJoseph Chen 
4030eea0d25SJoseph Chen 	temp2string(temp, s_temp, TEMP_STRING_LEN);
4040eea0d25SJoseph Chen 	printf("DVFS: %s'c\n", s_temp);
4050eea0d25SJoseph Chen 
4060eea0d25SJoseph Chen 	/* Apply dvfs policy for all pm element */
4070eea0d25SJoseph Chen 	list_for_each(node, &pm_e_head) {
4080eea0d25SJoseph Chen 		e = list_entry(node, struct pm_element, node);
4090eea0d25SJoseph Chen 		__wtemp_dvfs_apply(dev, e, temp, PM_EVT_BOTH);
4100eea0d25SJoseph Chen 	}
4110eea0d25SJoseph Chen 
4120eea0d25SJoseph Chen 	return 0;
4130eea0d25SJoseph Chen }
4140eea0d25SJoseph Chen 
wtemp_dvfs_repeat_apply(struct udevice * dev)4150eea0d25SJoseph Chen static int wtemp_dvfs_repeat_apply(struct udevice *dev)
4160eea0d25SJoseph Chen {
4170eea0d25SJoseph Chen 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
4180eea0d25SJoseph Chen 	struct list_head *node;
4190eea0d25SJoseph Chen 	struct pm_element *e;
4200eea0d25SJoseph Chen 	enum pm_event applied;
4210eea0d25SJoseph Chen 	char s_temp[TEMP_STRING_LEN];
4220eea0d25SJoseph Chen 	int temp, ret;
4230eea0d25SJoseph Chen 
4240eea0d25SJoseph Chen repeat:
4250eea0d25SJoseph Chen 	ret = thermal_get_temp(priv->thermal, &temp);
4260eea0d25SJoseph Chen 	if (ret) {
4270eea0d25SJoseph Chen 		printf("DVFS: Get thermal temperature failed, ret=%d\n", ret);
4280eea0d25SJoseph Chen 		return false;
4290eea0d25SJoseph Chen 	}
4300eea0d25SJoseph Chen 
4310eea0d25SJoseph Chen 	/* Apply dvfs policy for all pm element if there is repeat request */
4320eea0d25SJoseph Chen 	applied = PM_EVT_NONE;
4330eea0d25SJoseph Chen 	list_for_each(node, &pm_e_head) {
4340eea0d25SJoseph Chen 		e = list_entry(node, struct pm_element, node);
4350eea0d25SJoseph Chen 		if (e->lmt.ltemp_repeat)
4360eea0d25SJoseph Chen 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_LOW);
4370eea0d25SJoseph Chen 		if (e->lmt.htemp_repeat)
4380eea0d25SJoseph Chen 			applied |= __wtemp_dvfs_apply(dev, e, temp, PM_EVT_HIGH);
4390eea0d25SJoseph Chen 	}
4400eea0d25SJoseph Chen 
4410eea0d25SJoseph Chen 	/* Everything is fine, exit */
4420eea0d25SJoseph Chen 	if (applied == PM_EVT_NONE)
4430eea0d25SJoseph Chen 		goto finish;
4440eea0d25SJoseph Chen 
4450eea0d25SJoseph Chen 	/* Check repeat result */
4460eea0d25SJoseph Chen 	udelay(REPEAT_PERIOD_US);
4470eea0d25SJoseph Chen 	list_for_each(node, &pm_e_head) {
4480eea0d25SJoseph Chen 		e = list_entry(node, struct pm_element, node);
4490eea0d25SJoseph Chen 		if (e->lmt.ltemp_repeat &&
4500eea0d25SJoseph Chen 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_LOW))
4510eea0d25SJoseph Chen 			goto repeat;
4520eea0d25SJoseph Chen 		if (e->lmt.htemp_repeat &&
4530eea0d25SJoseph Chen 		    !wtemp_dvfs_is_effect(e, temp, PM_EVT_HIGH))
4540eea0d25SJoseph Chen 			goto repeat;
4550eea0d25SJoseph Chen 	}
4560eea0d25SJoseph Chen 
4570eea0d25SJoseph Chen finish:
4580eea0d25SJoseph Chen 	list_for_each(node, &pm_e_head) {
4590eea0d25SJoseph Chen 		e = list_entry(node, struct pm_element, node);
4600eea0d25SJoseph Chen 		temp2string(temp, s_temp, TEMP_STRING_LEN);
4610eea0d25SJoseph Chen 		printf("DVFS: %s %s'c, %ld Hz, %d uV\n", e->name,
4620eea0d25SJoseph Chen 		       s_temp, __wtemp_clk_get_rate(e),
4630eea0d25SJoseph Chen 		       __wtemp_regulator_get_value(e));
4640eea0d25SJoseph Chen 	}
4650eea0d25SJoseph Chen 
4660eea0d25SJoseph Chen 	return 0;
4670eea0d25SJoseph Chen }
4680eea0d25SJoseph Chen 
print_e_state(void)4690eea0d25SJoseph Chen static void print_e_state(void)
4700eea0d25SJoseph Chen {
4710eea0d25SJoseph Chen 	struct pm_element *e;
4720eea0d25SJoseph Chen 	struct list_head *node;
4730eea0d25SJoseph Chen 	char s_low[TEMP_STRING_LEN];
4740eea0d25SJoseph Chen 	char s_high[TEMP_STRING_LEN];
4750eea0d25SJoseph Chen 	char s_tz[TEMP_STRING_LEN];
4760eea0d25SJoseph Chen 
4770eea0d25SJoseph Chen 	list_for_each(node, &pm_e_head) {
4780eea0d25SJoseph Chen 		e = list_entry(node, struct pm_element, node);
4790eea0d25SJoseph Chen 		if (!e->lmt.ltemp_limit &&
4800eea0d25SJoseph Chen 		    !e->lmt.htemp_limit && !e->lmt.tztemp_limit)
4810eea0d25SJoseph Chen 			return;
4820eea0d25SJoseph Chen 
4830eea0d25SJoseph Chen 		temp2string(e->lmt.tz_temp, s_tz, TEMP_STRING_LEN);
4840eea0d25SJoseph Chen 		temp2string(e->lmt.low_temp, s_low, TEMP_STRING_LEN);
4850eea0d25SJoseph Chen 		temp2string(e->lmt.high_temp, s_high, TEMP_STRING_LEN);
4860eea0d25SJoseph Chen 		printf("DVFS: %s: low=%s'c, high=%s'c, Vmax=%duV, tz_temp=%s'c, "
4870eea0d25SJoseph Chen 			  "h_repeat=%d, l_repeat=%d\n",
4880eea0d25SJoseph Chen 			  e->name, e->lmt.ltemp_limit ? s_low : NULL,
4890eea0d25SJoseph Chen 			  e->lmt.htemp_limit ? s_high : NULL,
4900eea0d25SJoseph Chen 			  e->lmt.max_volt,
4910eea0d25SJoseph Chen 			  e->lmt.tztemp_limit ? s_tz : NULL,
4920eea0d25SJoseph Chen 			  e->lmt.htemp_repeat, e->lmt.ltemp_repeat);
4930eea0d25SJoseph Chen 	}
4940eea0d25SJoseph Chen }
4950eea0d25SJoseph Chen 
wtemp_dvfs_ofdata_to_platdata(struct udevice * dev)4960eea0d25SJoseph Chen static int wtemp_dvfs_ofdata_to_platdata(struct udevice *dev)
4970eea0d25SJoseph Chen {
4980eea0d25SJoseph Chen 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
4990eea0d25SJoseph Chen 	ofnode tz_trip0, cooling_maps, node;
5000eea0d25SJoseph Chen 	ofnode cpus, cpu, dmc;
5010eea0d25SJoseph Chen 	const char *name;
5020eea0d25SJoseph Chen 	int ret, tz_temp;
5030eea0d25SJoseph Chen 	u32 phandle;
5040eea0d25SJoseph Chen 
5050eea0d25SJoseph Chen 	INIT_LIST_HEAD(&pm_e_head);
5060eea0d25SJoseph Chen 
5070eea0d25SJoseph Chen 	/* 1. Parse cpu node */
5080eea0d25SJoseph Chen 	priv->cpu = &pm_cpu;
5090eea0d25SJoseph Chen 	cpus = ofnode_path(FDT_PATH_CPUS);
5100eea0d25SJoseph Chen 	if (!ofnode_valid(cpus)) {
5110eea0d25SJoseph Chen 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
5120eea0d25SJoseph Chen 		goto parse_dmc;
5130eea0d25SJoseph Chen 	}
5140eea0d25SJoseph Chen 
5150eea0d25SJoseph Chen 	ofnode_for_each_subnode(cpu, cpus) {
5160eea0d25SJoseph Chen 		name = ofnode_get_property(cpu, "device_type", NULL);
5170eea0d25SJoseph Chen 		if (!name)
5180eea0d25SJoseph Chen 			continue;
5190eea0d25SJoseph Chen 		if (!strcmp(name, "cpu")) {
5200eea0d25SJoseph Chen 			ret = __wtemp_common_ofdata_to_platdata(cpu, priv->cpu);
5210eea0d25SJoseph Chen 			if (ret)
5220eea0d25SJoseph Chen 				return ret;
5230eea0d25SJoseph Chen 			break;
5240eea0d25SJoseph Chen 		}
5250eea0d25SJoseph Chen 	}
5260eea0d25SJoseph Chen 
5270eea0d25SJoseph Chen 	priv->cpu->lmt.ltemp_repeat =
5280eea0d25SJoseph Chen 		dev_read_bool(dev, "cpu,low-temp-repeat");
5290eea0d25SJoseph Chen 	priv->cpu->lmt.htemp_repeat =
5300eea0d25SJoseph Chen 		dev_read_bool(dev, "cpu,high-temp-repeat");
5310eea0d25SJoseph Chen 
5320eea0d25SJoseph Chen 	list_add_tail(&priv->cpu->node, &pm_e_head);
5330eea0d25SJoseph Chen 
5340eea0d25SJoseph Chen 	/* 2. Parse dmc node */
5350eea0d25SJoseph Chen parse_dmc:
5360eea0d25SJoseph Chen 	priv->dmc = &pm_dmc;
5370eea0d25SJoseph Chen 	dmc = ofnode_path(FDT_PATH_DMC);
5380eea0d25SJoseph Chen 	if (!ofnode_valid(dmc)) {
5390eea0d25SJoseph Chen 		debug("DVFS: Can't find %s\n", FDT_PATH_CPUS);
5400eea0d25SJoseph Chen 		goto parse_tz;
5410eea0d25SJoseph Chen 	}
5420eea0d25SJoseph Chen 	if (!IS_ENABLED(CONFIG_ROCKCHIP_DMC)) {
5430eea0d25SJoseph Chen 		debug("DVFS: CONFIG_ROCKCHIP_DMC is disabled\n");
5440eea0d25SJoseph Chen 		goto parse_tz;
5450eea0d25SJoseph Chen 	}
5460eea0d25SJoseph Chen 
5470eea0d25SJoseph Chen 	ret = __wtemp_common_ofdata_to_platdata(dmc, priv->dmc);
5480eea0d25SJoseph Chen 	if (ret)
5490eea0d25SJoseph Chen 		return ret;
5500eea0d25SJoseph Chen 
5510eea0d25SJoseph Chen 	priv->dmc->lmt.ltemp_repeat =
5520eea0d25SJoseph Chen 		dev_read_bool(dev, "dmc,low-temp-repeat");
5530eea0d25SJoseph Chen 	priv->dmc->lmt.htemp_repeat =
5540eea0d25SJoseph Chen 		dev_read_bool(dev, "dmc,high-temp-repeat");
5550eea0d25SJoseph Chen 
5560eea0d25SJoseph Chen 	list_add_tail(&priv->dmc->node, &pm_e_head);
5570eea0d25SJoseph Chen 
5580eea0d25SJoseph Chen 	/* 3. Parse thermal zone node */
5590eea0d25SJoseph Chen parse_tz:
5600eea0d25SJoseph Chen 	tz_trip0 = ofnode_path(FDT_PATH_THREMAL_TRIP_POINT0);
5610eea0d25SJoseph Chen 	if (!ofnode_valid(tz_trip0)) {
5620eea0d25SJoseph Chen 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_TRIP_POINT0);
5630eea0d25SJoseph Chen 		goto finish;
5640eea0d25SJoseph Chen 	}
5650eea0d25SJoseph Chen 
5660eea0d25SJoseph Chen 	tz_temp = ofnode_read_s32_default(tz_trip0, "temperature", -ENODATA);
5670eea0d25SJoseph Chen 	if (tz_temp == -ENODATA) {
5680eea0d25SJoseph Chen 		debug("DVFS: Can't get thermal zone trip0 temperature\n");
5690eea0d25SJoseph Chen 		goto finish;
5700eea0d25SJoseph Chen 	}
5710eea0d25SJoseph Chen 
5720eea0d25SJoseph Chen 	cooling_maps = ofnode_path(FDT_PATH_THREMAL_COOLING_MAPS);
5730eea0d25SJoseph Chen 	if (!ofnode_valid(cooling_maps)) {
5740eea0d25SJoseph Chen 		debug("DVFS: Can't find %s\n", FDT_PATH_THREMAL_COOLING_MAPS);
5750eea0d25SJoseph Chen 		goto finish;
5760eea0d25SJoseph Chen 	}
5770eea0d25SJoseph Chen 
5780eea0d25SJoseph Chen 	ofnode_for_each_subnode(node, cooling_maps) {
5790eea0d25SJoseph Chen 		ofnode_read_u32_array(node, "cooling-device", &phandle, 1);
5800eea0d25SJoseph Chen 		name = ofnode_get_name(ofnode_get_by_phandle(phandle));
5810eea0d25SJoseph Chen 		if (!name)
5820eea0d25SJoseph Chen 			continue;
5830eea0d25SJoseph Chen 		if (strstr(name, "cpu")) {
5840eea0d25SJoseph Chen 			priv->cpu->lmt.tztemp_limit = true;
5850eea0d25SJoseph Chen 			priv->cpu->lmt.tz_temp = tz_temp;
5860eea0d25SJoseph Chen 		} else if (strstr(name, "dmc")) {
5870eea0d25SJoseph Chen 			priv->dmc->lmt.tztemp_limit = true;
5880eea0d25SJoseph Chen 			priv->dmc->lmt.tz_temp = tz_temp;
5890eea0d25SJoseph Chen 		}
5900eea0d25SJoseph Chen 	}
5910eea0d25SJoseph Chen 
5920eea0d25SJoseph Chen finish:
5930eea0d25SJoseph Chen 	print_e_state();
5940eea0d25SJoseph Chen 
5950eea0d25SJoseph Chen 	return 0;
5960eea0d25SJoseph Chen }
5970eea0d25SJoseph Chen 
5980eea0d25SJoseph Chen static const struct dm_dvfs_ops wtemp_dvfs_ops = {
5990eea0d25SJoseph Chen 	.apply = wtemp_dvfs_apply,
6000eea0d25SJoseph Chen 	.repeat_apply = wtemp_dvfs_repeat_apply,
6010eea0d25SJoseph Chen };
6020eea0d25SJoseph Chen 
wtemp_dvfs_probe(struct udevice * dev)6030eea0d25SJoseph Chen static int wtemp_dvfs_probe(struct udevice *dev)
6040eea0d25SJoseph Chen {
6050eea0d25SJoseph Chen 	struct wtemp_dvfs_priv *priv = dev_get_priv(dev);
6060eea0d25SJoseph Chen 	int ret;
6070eea0d25SJoseph Chen 
6080eea0d25SJoseph Chen #ifdef CONFIG_ROCKCHIP_DMC
6090eea0d25SJoseph Chen 	struct udevice *ram_dev;
6100eea0d25SJoseph Chen 
6110eea0d25SJoseph Chen 	/* Init dmc */
6120eea0d25SJoseph Chen 	ret = uclass_get_device(UCLASS_RAM, 0, &ram_dev);
6130eea0d25SJoseph Chen 	if (ret) {
6140eea0d25SJoseph Chen 		printf("DVFS: Get dmc device failed, ret=%d\n", ret);
6150eea0d25SJoseph Chen 		return ret;
6160eea0d25SJoseph Chen 	}
6170eea0d25SJoseph Chen #endif
6180eea0d25SJoseph Chen 	/* Init thermal */
6190eea0d25SJoseph Chen 	ret = uclass_get_device(UCLASS_THERMAL, 0, &priv->thermal);
6200eea0d25SJoseph Chen 	if (ret) {
6210eea0d25SJoseph Chen 		printf("DVFS: Get thermal device failed, ret=%d\n", ret);
6220eea0d25SJoseph Chen 		return ret;
6230eea0d25SJoseph Chen 	}
6240eea0d25SJoseph Chen 
6250eea0d25SJoseph Chen 	return 0;
6260eea0d25SJoseph Chen }
6270eea0d25SJoseph Chen 
6280eea0d25SJoseph Chen static const struct udevice_id wtemp_dvfs_match[] = {
6290eea0d25SJoseph Chen 	{ .compatible = "rockchip,uboot-wide-temperature", },
6300eea0d25SJoseph Chen 	{},
6310eea0d25SJoseph Chen };
6320eea0d25SJoseph Chen 
6330eea0d25SJoseph Chen U_BOOT_DRIVER(rockchip_wide_temp_dvfs) = {
6340eea0d25SJoseph Chen 	.name		      = "rockchip_wide_temp_dvfs",
6350eea0d25SJoseph Chen 	.id		      = UCLASS_DVFS,
6360eea0d25SJoseph Chen 	.ops		      = &wtemp_dvfs_ops,
6370eea0d25SJoseph Chen 	.of_match	      = wtemp_dvfs_match,
6380eea0d25SJoseph Chen 	.probe		      = wtemp_dvfs_probe,
6390eea0d25SJoseph Chen 	.ofdata_to_platdata   = wtemp_dvfs_ofdata_to_platdata,
6400eea0d25SJoseph Chen 	.priv_auto_alloc_size = sizeof(struct wtemp_dvfs_priv),
6410eea0d25SJoseph Chen };
642