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