xref: /OK3568_Linux_fs/kernel/drivers/thermal/rk_virtual_thermal.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * rk virtual tsadc driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2017 Rockchip Electronics Co., Ltd
5*4882a593Smuzhiyun  * Author: Rocky Hao <rocky.hao@rock-chips.com>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify it
8*4882a593Smuzhiyun  * under the terms and conditions of the GNU General Public License,
9*4882a593Smuzhiyun  * version 2, as published by the Free Software Foundation.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * This program is distributed in the hope it will be useful, but WITHOUT
12*4882a593Smuzhiyun  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14*4882a593Smuzhiyun  * more details.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  */
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/clk.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/of.h>
21*4882a593Smuzhiyun #include <linux/of_address.h>
22*4882a593Smuzhiyun #include <linux/platform_device.h>
23*4882a593Smuzhiyun #include <linux/thermal.h>
24*4882a593Smuzhiyun #include <linux/timer.h>
25*4882a593Smuzhiyun #include <linux/nvmem-consumer.h>
26*4882a593Smuzhiyun #include <linux/backlight.h>
27*4882a593Smuzhiyun #include <linux/cpufreq.h>
28*4882a593Smuzhiyun #include <linux/power_supply.h>
29*4882a593Smuzhiyun #include <linux/clk-provider.h>
30*4882a593Smuzhiyun #include <dt-bindings/clock/rk3128-cru.h>
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #define GPU_TEMP_COMPENSION			(6000)
33*4882a593Smuzhiyun #define VPU_TEMP_COMPENSION			(3000)
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define LOWEST_TEMP				(-273000)
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define BASE					(1024)
38*4882a593Smuzhiyun #define BASE_SHIFT				(10)
39*4882a593Smuzhiyun #define START_DEBOUNCE_COUNT			(100)
40*4882a593Smuzhiyun #define HIGHER_DEBOUNCE_TEMP			(30000)
41*4882a593Smuzhiyun #define LOWER_DEBOUNCE_TEMP			(15000)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define LEAKAGE_INVALID				(0xff)
44*4882a593Smuzhiyun /*20ms as the unit, 60000 * 20ms = 20mins */
45*4882a593Smuzhiyun #define TEMP_STABLE_TIME			(60000)
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define MINIMAL_DISCHARGE_CURRENT		(-200000)
48*4882a593Smuzhiyun #define LOWEST_WORKING_TEMP			(-40000)
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static unsigned int logout;
51*4882a593Smuzhiyun module_param(logout, int, 0644);
52*4882a593Smuzhiyun MODULE_PARM_DESC(logout, "switch to control logout or not");
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun struct temp_frequency_entry {
55*4882a593Smuzhiyun 	unsigned int frequency;
56*4882a593Smuzhiyun 	s32 time2temp[2];
57*4882a593Smuzhiyun 	int time_bound;
58*4882a593Smuzhiyun 	s32 time2temp2[2];
59*4882a593Smuzhiyun 	int min_temp;
60*4882a593Smuzhiyun 	int stable_temp;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	s32 temp2time[2];
63*4882a593Smuzhiyun 	int temp_bound;
64*4882a593Smuzhiyun 	s32 temp2time2[2];
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static const struct temp_frequency_entry rk3126_table[] = {
68*4882a593Smuzhiyun 	{400000, {18, 446167,}, 6000, {2, 541167,}, 44616, 69000, {555, -23865},
69*4882a593Smuzhiyun 	 56000, {5000, -272785},},
70*4882a593Smuzhiyun 	{816000, {18, 496167,}, 6000, {2, 591167,}, 49616, 74000, {555, -26640},
71*4882a593Smuzhiyun 	 61000, {5000, -297785},},
72*4882a593Smuzhiyun 	{912000, {21, 525167,}, 6000, {2, 639167,}, 52516, 80000, {476, -25007},
73*4882a593Smuzhiyun 	 65000, {5000, -319067},},
74*4882a593Smuzhiyun 	{1008000, {22, 563500,}, 6000, {3, 677500,}, 56350, 100000,
75*4882a593Smuzhiyun 	 {454, -25613}, 70000, {3333, -227143},},
76*4882a593Smuzhiyun 	{1104000, {33, 570000,}, 6000, {5, 738000,}, 57000, 109000,
77*4882a593Smuzhiyun 	 {303, -17272}, 77000, {2000, -147941},},
78*4882a593Smuzhiyun 	{1200000, {35, 620167,}, 6000, {5, 800167,}, 61016, 113000,
79*4882a593Smuzhiyun 	 {285, -17719}, 83000, {2000, -160064},},
80*4882a593Smuzhiyun 	{CPUFREQ_TABLE_END, {0, 0,}, 0, {0, 0,}, 0, 0, {0, 0,}, 0, {0, 0,} },
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun struct thermal_tuning_info {
84*4882a593Smuzhiyun 	int load_slope;
85*4882a593Smuzhiyun 	int load_intercept;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	int lkg_slope;
88*4882a593Smuzhiyun 	int lkg_intercept;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	int cur_slope;
91*4882a593Smuzhiyun 	int cur_intercept;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	int bn_slope;
94*4882a593Smuzhiyun 	int bn_intercept;
95*4882a593Smuzhiyun 	int bn_offsite;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	int vpu_slope;
98*4882a593Smuzhiyun 	int gpu_slope;
99*4882a593Smuzhiyun 	const struct temp_frequency_entry *map_entries;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	int vpu_ajust;
102*4882a593Smuzhiyun 	int gpu_ajust;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	int fusing_step;
105*4882a593Smuzhiyun };
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun static const struct thermal_tuning_info rk3126_tuning_info = {
108*4882a593Smuzhiyun 	.load_slope = 102,
109*4882a593Smuzhiyun 	.load_intercept = 61800,
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	.lkg_slope = 107,
112*4882a593Smuzhiyun 	.lkg_intercept = 4713,
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	.cur_slope = 42,
115*4882a593Smuzhiyun 	.cur_intercept = 32661,
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	.bn_slope = 1517,
118*4882a593Smuzhiyun 	.bn_intercept = 199353,
119*4882a593Smuzhiyun 	.bn_offsite = 262000,
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	.vpu_slope = 5,
122*4882a593Smuzhiyun 	.gpu_slope = 5,
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	.map_entries = rk3126_table,
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	.vpu_ajust = GPU_TEMP_COMPENSION,
127*4882a593Smuzhiyun 	.gpu_ajust = VPU_TEMP_COMPENSION,
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	.fusing_step = 2,
130*4882a593Smuzhiyun };
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun struct virtual_thermal_data {
133*4882a593Smuzhiyun 	struct platform_device *pdev;
134*4882a593Smuzhiyun 	struct device *dev;
135*4882a593Smuzhiyun 	struct thermal_zone_device *tzd;
136*4882a593Smuzhiyun 	struct power_supply *psy_bat;
137*4882a593Smuzhiyun 	struct power_supply *psy_usb;
138*4882a593Smuzhiyun 	struct power_supply *psy_ac;
139*4882a593Smuzhiyun 	struct cpufreq_freqs current_freq;
140*4882a593Smuzhiyun 	const struct temp_frequency_entry *temp_freq;
141*4882a593Smuzhiyun 	int cmp_lkg_temp;
142*4882a593Smuzhiyun 	int sigma_time_20ms;
143*4882a593Smuzhiyun 	struct kobject virtual_thermal_kobj;
144*4882a593Smuzhiyun 	struct thermal_tuning_info *tuning_info;
145*4882a593Smuzhiyun 	struct clk *gpu_clk;
146*4882a593Smuzhiyun 	struct clk *vpu_clk;
147*4882a593Smuzhiyun };
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun static struct platform_device *platform_dev;
150*4882a593Smuzhiyun 
get_temp_by_freq_time(unsigned int freq,int time_20ms)151*4882a593Smuzhiyun static int get_temp_by_freq_time(unsigned int freq, int time_20ms)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	const struct temp_frequency_entry *table = ctx->tuning_info->map_entries;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	int i = 0;
158*4882a593Smuzhiyun 	int milli_deg = 0;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
161*4882a593Smuzhiyun 		if (freq < table[i].frequency) {
162*4882a593Smuzhiyun 			ctx->temp_freq = &table[i];
163*4882a593Smuzhiyun 			break;
164*4882a593Smuzhiyun 		}
165*4882a593Smuzhiyun 	}
166*4882a593Smuzhiyun 	if (table[i].frequency == CPUFREQ_TABLE_END)
167*4882a593Smuzhiyun 		ctx->temp_freq = &table[i - 1];
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (time_20ms > TEMP_STABLE_TIME)
170*4882a593Smuzhiyun 		return ctx->temp_freq->stable_temp;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	if (time_20ms < ctx->temp_freq->time_bound)
173*4882a593Smuzhiyun 		milli_deg =
174*4882a593Smuzhiyun 		    time_20ms * ctx->temp_freq->time2temp[0] +
175*4882a593Smuzhiyun 		    ctx->temp_freq->time2temp[1];
176*4882a593Smuzhiyun 	else
177*4882a593Smuzhiyun 		milli_deg =
178*4882a593Smuzhiyun 		    time_20ms * ctx->temp_freq->time2temp2[0] +
179*4882a593Smuzhiyun 		    ctx->temp_freq->time2temp2[1];
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (logout)
182*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "current freq: %u stable_temp: %d milli_deg %d\n",
183*4882a593Smuzhiyun 			 freq, ctx->temp_freq->stable_temp, milli_deg / 10);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	return milli_deg / 10;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
get_time_by_temp(int milli_deg)188*4882a593Smuzhiyun static int get_time_by_temp(int milli_deg)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	int time_20ms = 0;
191*4882a593Smuzhiyun 	int deg = milli_deg / 1000;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (milli_deg > ctx->temp_freq->stable_temp)
196*4882a593Smuzhiyun 		return TEMP_STABLE_TIME;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	if (milli_deg < ctx->temp_freq->temp_bound) {
199*4882a593Smuzhiyun 		time_20ms =
200*4882a593Smuzhiyun 		    deg * ctx->temp_freq->temp2time[0] +
201*4882a593Smuzhiyun 		    ctx->temp_freq->temp2time[1];
202*4882a593Smuzhiyun 	} else {
203*4882a593Smuzhiyun 		time_20ms =
204*4882a593Smuzhiyun 		    deg * ctx->temp_freq->temp2time2[0] +
205*4882a593Smuzhiyun 		    ctx->temp_freq->temp2time2[1];
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	if (logout)
209*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "estimate time %d, by milli_deg %d\n",
210*4882a593Smuzhiyun 			 time_20ms, milli_deg);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return max(time_20ms, 0);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
get_load(int cpu,int cpu_idx)215*4882a593Smuzhiyun static u32 get_load(int cpu, int cpu_idx)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	static u64 time_in_idle[NR_CPUS] = { 0 };
218*4882a593Smuzhiyun 	static u64 time_in_idle_timestamp[NR_CPUS] = { 0 };
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	u32 load;
221*4882a593Smuzhiyun 	u64 now, now_idle, delta_time, delta_idle;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	now_idle = get_cpu_idle_time(cpu, &now, 0);
224*4882a593Smuzhiyun 	delta_idle = now_idle - time_in_idle[cpu_idx];
225*4882a593Smuzhiyun 	delta_time = now - time_in_idle_timestamp[cpu_idx];
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	if (delta_time <= delta_idle)
228*4882a593Smuzhiyun 		load = 0;
229*4882a593Smuzhiyun 	else
230*4882a593Smuzhiyun 		load = div64_u64(100 * (delta_time - delta_idle), delta_time);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	time_in_idle[cpu_idx] = now_idle;
233*4882a593Smuzhiyun 	time_in_idle_timestamp[cpu_idx] = now;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	return load;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
get_all_load(void)238*4882a593Smuzhiyun static int get_all_load(void)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	u32 total_load = 0;
241*4882a593Smuzhiyun 	int cpu;
242*4882a593Smuzhiyun 	int i = 0;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	for_each_online_cpu(cpu) {
245*4882a593Smuzhiyun 		u32 load;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 		load = get_load(cpu, i);
248*4882a593Smuzhiyun 		total_load += load;
249*4882a593Smuzhiyun 		if (logout)
250*4882a593Smuzhiyun 			dev_info(&platform_dev->dev, "cpu %d, load %d\n", i,
251*4882a593Smuzhiyun 				 load);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 		i++;
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 	if (logout)
256*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "total cpu load %d\n", total_load);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	return total_load;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
predict_normal_temp(int milli_deg)261*4882a593Smuzhiyun static int predict_normal_temp(int milli_deg)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	int cov_q = 18;
264*4882a593Smuzhiyun 	int cov_r = 542;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	int gain;
267*4882a593Smuzhiyun 	int temp_mid;
268*4882a593Smuzhiyun 	int temp_now;
269*4882a593Smuzhiyun 	int prob_mid;
270*4882a593Smuzhiyun 	int prob_now;
271*4882a593Smuzhiyun 	static int temp_last = 50000;
272*4882a593Smuzhiyun 	static int prob_last = 20;
273*4882a593Smuzhiyun 	static int bounding_cnt;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	if (bounding_cnt++ > START_DEBOUNCE_COUNT) {
276*4882a593Smuzhiyun 		bounding_cnt = START_DEBOUNCE_COUNT;
277*4882a593Smuzhiyun 		if (milli_deg - temp_last > HIGHER_DEBOUNCE_TEMP)
278*4882a593Smuzhiyun 			milli_deg = temp_last + HIGHER_DEBOUNCE_TEMP / 3;
279*4882a593Smuzhiyun 		if (temp_last - milli_deg > LOWER_DEBOUNCE_TEMP)
280*4882a593Smuzhiyun 			milli_deg = temp_last - LOWER_DEBOUNCE_TEMP / 3;
281*4882a593Smuzhiyun 	}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	temp_mid = temp_last;
284*4882a593Smuzhiyun 	prob_mid = prob_last + cov_q;
285*4882a593Smuzhiyun 	gain = (prob_mid * BASE) / (prob_mid + cov_r);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	temp_now = temp_mid + (gain * (milli_deg - temp_mid) >> BASE_SHIFT);
288*4882a593Smuzhiyun 	prob_now = ((BASE - gain) * prob_mid) >> BASE_SHIFT;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	prob_last = prob_now;
291*4882a593Smuzhiyun 	temp_last = temp_now;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	return temp_last;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun 
predict_cur_temp(int milli_cur_temp)296*4882a593Smuzhiyun static int predict_cur_temp(int milli_cur_temp)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun 	int cov_q = 18;
299*4882a593Smuzhiyun 	int cov_r = 542;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	int gain;
302*4882a593Smuzhiyun 	int temp_mid;
303*4882a593Smuzhiyun 	int temp_now;
304*4882a593Smuzhiyun 	int prob_mid;
305*4882a593Smuzhiyun 	int prob_now;
306*4882a593Smuzhiyun 	static int cur_last = 50000;
307*4882a593Smuzhiyun 	static int prob_last = 20;
308*4882a593Smuzhiyun 	static int bounding_cnt;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (bounding_cnt++ > START_DEBOUNCE_COUNT) {
311*4882a593Smuzhiyun 		bounding_cnt = START_DEBOUNCE_COUNT;
312*4882a593Smuzhiyun 		if (milli_cur_temp - cur_last > HIGHER_DEBOUNCE_TEMP)
313*4882a593Smuzhiyun 			milli_cur_temp = cur_last + HIGHER_DEBOUNCE_TEMP / 3;
314*4882a593Smuzhiyun 		if (cur_last - milli_cur_temp > LOWER_DEBOUNCE_TEMP)
315*4882a593Smuzhiyun 			milli_cur_temp = cur_last - LOWER_DEBOUNCE_TEMP / 3;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	temp_mid = cur_last;
319*4882a593Smuzhiyun 	prob_mid = prob_last + cov_q;
320*4882a593Smuzhiyun 	gain = (prob_mid * BASE) / (prob_mid + cov_r);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	temp_now =
323*4882a593Smuzhiyun 	    temp_mid + (gain * (milli_cur_temp - temp_mid) >> BASE_SHIFT);
324*4882a593Smuzhiyun 	prob_now = ((BASE - gain) * prob_mid) >> BASE_SHIFT;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	prob_last = prob_now;
327*4882a593Smuzhiyun 	cur_last = temp_now;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	return cur_last;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun 
update_counting_time(void)332*4882a593Smuzhiyun static void update_counting_time(void)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
335*4882a593Smuzhiyun 	static ktime_t delta_last;
336*4882a593Smuzhiyun 	ktime_t delta;
337*4882a593Smuzhiyun 	unsigned long long duration;
338*4882a593Smuzhiyun 	ktime_t timestamp = ktime_get();
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	delta = ktime_sub(timestamp, delta_last);
341*4882a593Smuzhiyun 	duration = (unsigned long long)ktime_to_ns(delta) >> 20;
342*4882a593Smuzhiyun 	delta_last = timestamp;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	if (duration < TEMP_STABLE_TIME)
345*4882a593Smuzhiyun 		ctx->sigma_time_20ms += div64_u64(duration, 20);
346*4882a593Smuzhiyun 	else
347*4882a593Smuzhiyun 		ctx->sigma_time_20ms = 0;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (logout)
350*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "sigma heating time %d\n",
351*4882a593Smuzhiyun 			 ctx->sigma_time_20ms);
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
update_working_time_for_gpu_vpu(void)354*4882a593Smuzhiyun static s64 update_working_time_for_gpu_vpu(void)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	static ktime_t last_timestamp;
357*4882a593Smuzhiyun 	ktime_t delta;
358*4882a593Smuzhiyun 	s64 duration;
359*4882a593Smuzhiyun 	ktime_t timestamp = ktime_get();
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	delta = ktime_sub(timestamp, last_timestamp);
362*4882a593Smuzhiyun 	duration = (long long)ktime_to_ns(delta) >> 20;
363*4882a593Smuzhiyun 	last_timestamp = timestamp;
364*4882a593Smuzhiyun 	duration = div64_s64(duration, 20);
365*4882a593Smuzhiyun 	return duration;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun 
clk_get_by_name(const char * clk_name)368*4882a593Smuzhiyun static struct clk *clk_get_by_name(const char *clk_name)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	const char *name;
371*4882a593Smuzhiyun 	struct clk *clk;
372*4882a593Smuzhiyun 	struct device_node *np;
373*4882a593Smuzhiyun 	struct of_phandle_args clkspec;
374*4882a593Smuzhiyun 	int i;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	np = of_find_node_by_name(NULL, "clock-controller");
377*4882a593Smuzhiyun 	if (!np)
378*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	clkspec.np = np;
381*4882a593Smuzhiyun 	clkspec.args_count = 1;
382*4882a593Smuzhiyun 	for (i = 1; i < CLK_NR_CLKS; i++) {
383*4882a593Smuzhiyun 		clkspec.args[0] = i;
384*4882a593Smuzhiyun 		clk = of_clk_get_from_provider(&clkspec);
385*4882a593Smuzhiyun 		if (IS_ERR_OR_NULL(clk))
386*4882a593Smuzhiyun 			continue;
387*4882a593Smuzhiyun 		name = __clk_get_name(clk);
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 		if (strlen(name) != strlen(clk_name)) {
390*4882a593Smuzhiyun 			clk_put(clk);
391*4882a593Smuzhiyun 			continue;
392*4882a593Smuzhiyun 		}
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 		if (!strncmp(name, clk_name, strlen(clk_name)))
395*4882a593Smuzhiyun 			break;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 		clk_put(clk);
398*4882a593Smuzhiyun 	}
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	of_node_put(np);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	if (i == CLK_NR_CLKS)
403*4882a593Smuzhiyun 		clk = NULL;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	return clk;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
get_actual_brightness(void)408*4882a593Smuzhiyun static int get_actual_brightness(void)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	struct backlight_device *bd;
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	struct device_node *np;
413*4882a593Smuzhiyun 	int brightness = 0;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	np = of_find_node_by_name(NULL, "backlight");
416*4882a593Smuzhiyun 	if (!np)
417*4882a593Smuzhiyun 		return 0;
418*4882a593Smuzhiyun 	bd = of_find_backlight_by_node(np);
419*4882a593Smuzhiyun 	if (!bd)
420*4882a593Smuzhiyun 		goto exit;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	mutex_lock(&bd->ops_lock);
423*4882a593Smuzhiyun 	if (bd->ops && bd->ops->get_brightness)
424*4882a593Smuzhiyun 		brightness = bd->ops->get_brightness(bd);
425*4882a593Smuzhiyun 	else
426*4882a593Smuzhiyun 		brightness = bd->props.brightness;
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	mutex_unlock(&bd->ops_lock);
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun exit:
431*4882a593Smuzhiyun 	of_node_put(np);
432*4882a593Smuzhiyun 	return brightness;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
compensate_brightness(int cur)435*4882a593Smuzhiyun static int compensate_brightness(int cur)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	int slope = ctx->tuning_info->bn_slope;
440*4882a593Smuzhiyun 	int intercept = ctx->tuning_info->bn_slope;
441*4882a593Smuzhiyun 	int offsite = ctx->tuning_info->bn_offsite;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	int brightness;
444*4882a593Smuzhiyun 	int cur_ajust = 0;
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	brightness = get_actual_brightness();
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	if (brightness == 0)
449*4882a593Smuzhiyun 		cur_ajust = cur - offsite;
450*4882a593Smuzhiyun 	else if (brightness > 0)
451*4882a593Smuzhiyun 		cur_ajust = cur - intercept + brightness * slope;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	if (logout)
454*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "brightness %d cur %d cur_ajust %d\n",
455*4882a593Smuzhiyun 			 brightness, cur, cur_ajust);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	return cur_ajust;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
rockchip_get_efuse_value(struct device_node * np,char * porp_name,int * value)460*4882a593Smuzhiyun static int rockchip_get_efuse_value(struct device_node *np, char *porp_name,
461*4882a593Smuzhiyun 				    int *value)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	struct nvmem_cell *cell;
464*4882a593Smuzhiyun 	unsigned char *buf;
465*4882a593Smuzhiyun 	size_t len;
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	cell = of_nvmem_cell_get(np, porp_name);
468*4882a593Smuzhiyun 	if (IS_ERR(cell))
469*4882a593Smuzhiyun 		return PTR_ERR(cell);
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	buf = (unsigned char *)nvmem_cell_read(cell, &len);
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	nvmem_cell_put(cell);
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	if (IS_ERR(buf))
476*4882a593Smuzhiyun 		return PTR_ERR(buf);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	if (buf[0] == LEAKAGE_INVALID) {
479*4882a593Smuzhiyun 		kfree(buf);
480*4882a593Smuzhiyun 		return -EINVAL;
481*4882a593Smuzhiyun 	}
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	*value = buf[0];
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	kfree(buf);
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	return 0;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun 
ajust_temp_on_gpu_vpu(int temp)490*4882a593Smuzhiyun static int ajust_temp_on_gpu_vpu(int temp)
491*4882a593Smuzhiyun {
492*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	int vpu_slope = ctx->tuning_info->vpu_slope;
495*4882a593Smuzhiyun 	int gpu_slope = ctx->tuning_info->gpu_slope;
496*4882a593Smuzhiyun 	int vpu_ajust = ctx->tuning_info->vpu_ajust;
497*4882a593Smuzhiyun 	int gpu_ajust = ctx->tuning_info->gpu_ajust;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	int delta_gpu_temp = 0;
500*4882a593Smuzhiyun 	int delta_vpu_temp = 0;
501*4882a593Smuzhiyun 	int gpu_enabled = 0;
502*4882a593Smuzhiyun 	int vpu_enabled = 0;
503*4882a593Smuzhiyun 	int delta;
504*4882a593Smuzhiyun 	static int sigma_vpu_20ms;
505*4882a593Smuzhiyun 	static int sigma_gpu_20ms;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	delta = (int)update_working_time_for_gpu_vpu();
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	if (__clk_is_enabled(ctx->gpu_clk)) {
510*4882a593Smuzhiyun 		gpu_enabled = 1;
511*4882a593Smuzhiyun 		sigma_gpu_20ms -= delta;
512*4882a593Smuzhiyun 		sigma_gpu_20ms = max(sigma_gpu_20ms, 0);
513*4882a593Smuzhiyun 	} else {
514*4882a593Smuzhiyun 		sigma_gpu_20ms += delta;
515*4882a593Smuzhiyun 	}
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	if (__clk_is_enabled(ctx->vpu_clk)) {
518*4882a593Smuzhiyun 		vpu_enabled = 1;
519*4882a593Smuzhiyun 		sigma_vpu_20ms -= delta;
520*4882a593Smuzhiyun 		sigma_vpu_20ms = max(sigma_vpu_20ms, 0);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	} else {
523*4882a593Smuzhiyun 		sigma_vpu_20ms += delta;
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	delta_gpu_temp = sigma_gpu_20ms * gpu_slope;
527*4882a593Smuzhiyun 	delta_vpu_temp = sigma_vpu_20ms * vpu_slope;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	if (delta_gpu_temp > gpu_ajust) {
530*4882a593Smuzhiyun 		delta_gpu_temp = gpu_ajust;
531*4882a593Smuzhiyun 		sigma_gpu_20ms = gpu_ajust / gpu_slope;
532*4882a593Smuzhiyun 	}
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	if (delta_vpu_temp > vpu_ajust) {
535*4882a593Smuzhiyun 		delta_vpu_temp = vpu_ajust;
536*4882a593Smuzhiyun 		sigma_vpu_20ms = vpu_ajust / vpu_slope;
537*4882a593Smuzhiyun 	}
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	if (logout)
540*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "temp %d delta_vpu_temp %d delta_vpu_temp %d\n",
541*4882a593Smuzhiyun 			 temp, delta_vpu_temp, delta_vpu_temp);
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	temp = temp - delta_gpu_temp - delta_vpu_temp;
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	return temp;
546*4882a593Smuzhiyun }
547*4882a593Smuzhiyun 
ps_get_cur_current(struct power_supply * psy,int * power_cur)548*4882a593Smuzhiyun static int ps_get_cur_current(struct power_supply *psy, int *power_cur)
549*4882a593Smuzhiyun {
550*4882a593Smuzhiyun 	union power_supply_propval val;
551*4882a593Smuzhiyun 	int ret;
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &val);
554*4882a593Smuzhiyun 	if (!ret)
555*4882a593Smuzhiyun 		*power_cur = val.intval;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	return ret;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun 
map_temp_from_current(int cur)560*4882a593Smuzhiyun static int map_temp_from_current(int cur)
561*4882a593Smuzhiyun {
562*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	int slope = ctx->tuning_info->cur_slope;
565*4882a593Smuzhiyun 	int intercept = ctx->tuning_info->cur_intercept;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	int milli_degree = cur * slope + intercept;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	milli_degree = predict_cur_temp(milli_degree);
570*4882a593Smuzhiyun 	return milli_degree;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
get_temp_by_current(void)573*4882a593Smuzhiyun static int get_temp_by_current(void)
574*4882a593Smuzhiyun {
575*4882a593Smuzhiyun 	int cur = 0;
576*4882a593Smuzhiyun 	int temp = LOWEST_TEMP;
577*4882a593Smuzhiyun 	int ret = -1;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if (ctx->psy_bat)
582*4882a593Smuzhiyun 		ret = ps_get_cur_current(ctx->psy_bat, &cur);
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	if (ret)
585*4882a593Smuzhiyun 		return temp;
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	cur = compensate_brightness(cur);
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	if (cur < MINIMAL_DISCHARGE_CURRENT) {
590*4882a593Smuzhiyun 		cur = -cur;
591*4882a593Smuzhiyun 		temp = map_temp_from_current(cur / 1000);
592*4882a593Smuzhiyun 	}
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	return temp;
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
ajudt_temp_by_load(int temp_delta)597*4882a593Smuzhiyun static int ajudt_temp_by_load(int temp_delta)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	int slope = ctx->tuning_info->load_slope;
602*4882a593Smuzhiyun 	int intercept = ctx->tuning_info->load_intercept;
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	int load_rate;
605*4882a593Smuzhiyun 	int total_load = 0;
606*4882a593Smuzhiyun 	int temp_delta_ajust;
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	total_load = get_all_load();
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	load_rate = (total_load * slope + intercept) / 1000;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	load_rate = min(load_rate, 100);
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	if (temp_delta > 0)
615*4882a593Smuzhiyun 		temp_delta_ajust = temp_delta * load_rate / 100;
616*4882a593Smuzhiyun 	else
617*4882a593Smuzhiyun 		temp_delta_ajust = temp_delta * 100 / load_rate;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	if (logout)
620*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "temp_delta %d load_rate %d temp_delta_ajust %d\n",
621*4882a593Smuzhiyun 			 temp_delta, load_rate, temp_delta_ajust);
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	return temp_delta_ajust;
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun 
is_charger_pluged_in(void)626*4882a593Smuzhiyun static int is_charger_pluged_in(void)
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun 	union power_supply_propval val;
629*4882a593Smuzhiyun 	int ret = 0;
630*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	struct power_supply *psy_usb = ctx->psy_usb;
633*4882a593Smuzhiyun 	struct power_supply *psy_ac = ctx->psy_ac;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	if (psy_usb && psy_usb->desc && psy_usb->desc->get_property) {
636*4882a593Smuzhiyun 		ret = psy_usb->desc->get_property(psy_usb,
637*4882a593Smuzhiyun 						  POWER_SUPPLY_PROP_ONLINE,
638*4882a593Smuzhiyun 						  &val);
639*4882a593Smuzhiyun 		if (!ret && val.intval)
640*4882a593Smuzhiyun 			return 1;
641*4882a593Smuzhiyun 	}
642*4882a593Smuzhiyun 	if (psy_ac && psy_ac->desc && psy_ac->desc->get_property) {
643*4882a593Smuzhiyun 		ret = psy_ac->desc->get_property(psy_ac,
644*4882a593Smuzhiyun 						 POWER_SUPPLY_PROP_ONLINE,
645*4882a593Smuzhiyun 						 &val);
646*4882a593Smuzhiyun 		if (!ret && val.intval)
647*4882a593Smuzhiyun 			return 1;
648*4882a593Smuzhiyun 	}
649*4882a593Smuzhiyun 	return 0;
650*4882a593Smuzhiyun }
651*4882a593Smuzhiyun 
estimate_temp_internal(void)652*4882a593Smuzhiyun static int estimate_temp_internal(void)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun 	int temp = 0;
655*4882a593Smuzhiyun 	static int last_temp = LOWEST_TEMP;
656*4882a593Smuzhiyun 	int temp_delta;
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	struct cpufreq_freqs *current_freq = &ctx->current_freq;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	update_counting_time();
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	temp = get_temp_by_freq_time(current_freq->new, ctx->sigma_time_20ms);
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	temp = ajust_temp_on_gpu_vpu(temp);
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	if (last_temp == LOWEST_TEMP)
669*4882a593Smuzhiyun 		temp_delta = 0;
670*4882a593Smuzhiyun 	else
671*4882a593Smuzhiyun 		temp_delta = temp - last_temp;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	temp_delta = ajudt_temp_by_load(temp_delta);
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	if (last_temp != LOWEST_TEMP)
676*4882a593Smuzhiyun 		temp = last_temp + temp_delta;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	last_temp = temp;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	temp = clamp(temp, ctx->temp_freq->min_temp, ctx->temp_freq->stable_temp);
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	temp += ctx->cmp_lkg_temp;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	temp = predict_normal_temp(temp);
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	ctx->sigma_time_20ms = get_time_by_temp(temp);
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	if (logout)
689*4882a593Smuzhiyun 		dev_info(&platform_dev->dev, "Temp1 %d cmp_lkg_temp %d sigma %d\n",
690*4882a593Smuzhiyun 			 temp, ctx->cmp_lkg_temp, ctx->sigma_time_20ms);
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	if (!is_charger_pluged_in()) {
693*4882a593Smuzhiyun 		int temp_from_current = 0;
694*4882a593Smuzhiyun 		int fusion_diff = 0;
695*4882a593Smuzhiyun 		int fusing_step = ctx->tuning_info->fusing_step;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 		temp_from_current = get_temp_by_current();
698*4882a593Smuzhiyun 		if (temp_from_current > LOWEST_WORKING_TEMP) {
699*4882a593Smuzhiyun 			fusion_diff = temp_from_current - temp;
700*4882a593Smuzhiyun 			temp = temp + fusion_diff / fusing_step;
701*4882a593Smuzhiyun 			ctx->sigma_time_20ms = get_time_by_temp(temp);
702*4882a593Smuzhiyun 			if (logout)
703*4882a593Smuzhiyun 				dev_info(&platform_dev->dev, "Temp2 %d temp_from_current %d sigma %d\n",
704*4882a593Smuzhiyun 					 temp, temp_from_current,
705*4882a593Smuzhiyun 					 ctx->sigma_time_20ms);
706*4882a593Smuzhiyun 		}
707*4882a593Smuzhiyun 	}
708*4882a593Smuzhiyun 	return temp;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun 
virtual_thermal_set_trips(void * _sensor,int low,int high)711*4882a593Smuzhiyun static int virtual_thermal_set_trips(void *_sensor, int low, int high)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun 	return 0;
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun 
virtual_thermal_get_temp(void * _sensor,int * out_temp)716*4882a593Smuzhiyun static int virtual_thermal_get_temp(void *_sensor, int *out_temp)
717*4882a593Smuzhiyun {
718*4882a593Smuzhiyun 	*out_temp = estimate_temp_internal();
719*4882a593Smuzhiyun 	return 0;
720*4882a593Smuzhiyun }
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun static const struct thermal_zone_of_device_ops virtual_of_thermal_ops = {
723*4882a593Smuzhiyun 	.get_temp = virtual_thermal_get_temp,
724*4882a593Smuzhiyun 	.set_trips = virtual_thermal_set_trips,
725*4882a593Smuzhiyun };
726*4882a593Smuzhiyun 
727*4882a593Smuzhiyun static const struct of_device_id of_virtual_thermal_match[] = {
728*4882a593Smuzhiyun 	{
729*4882a593Smuzhiyun 	 .compatible = "rockchip,rk3126-tsadc-virtual",
730*4882a593Smuzhiyun 	 .data = (void *)&rk3126_tuning_info,
731*4882a593Smuzhiyun 	 },
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	{ /* end */ },
734*4882a593Smuzhiyun };
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, of_virtual_thermal_match);
737*4882a593Smuzhiyun 
temp_interactive_notifier(struct notifier_block * nb,unsigned long val,void * data)738*4882a593Smuzhiyun static int temp_interactive_notifier(struct notifier_block *nb,
739*4882a593Smuzhiyun 				     unsigned long val, void *data)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun 	struct cpufreq_freqs *freq = data;
742*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	if (!ctx)
745*4882a593Smuzhiyun 		return 0;
746*4882a593Smuzhiyun 	if (val == CPUFREQ_POSTCHANGE) {
747*4882a593Smuzhiyun 		ctx->current_freq.new = freq->new;
748*4882a593Smuzhiyun 		ctx->current_freq.old = freq->old;
749*4882a593Smuzhiyun 	}
750*4882a593Smuzhiyun 	return 0;
751*4882a593Smuzhiyun }
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun static struct notifier_block temp_notifier_block = {
754*4882a593Smuzhiyun 	.notifier_call = temp_interactive_notifier,
755*4882a593Smuzhiyun };
756*4882a593Smuzhiyun 
compensate_leakage(int lkg)757*4882a593Smuzhiyun static int compensate_leakage(int lkg)
758*4882a593Smuzhiyun {
759*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 	int slope = ctx->tuning_info->lkg_slope;
762*4882a593Smuzhiyun 	int intercept = ctx->tuning_info->lkg_slope;
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	int milli_degree = 0;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	if (lkg == 0)
767*4882a593Smuzhiyun 		milli_degree = 0;
768*4882a593Smuzhiyun 	else
769*4882a593Smuzhiyun 		milli_degree = slope * lkg - intercept;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	return milli_degree;
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun 
dump_virtual_temperature(void)774*4882a593Smuzhiyun void dump_virtual_temperature(void)
775*4882a593Smuzhiyun {
776*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(platform_dev);
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	struct thermal_zone_device *tz = ctx->tzd;
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	if (tz->temperature != THERMAL_TEMP_INVALID)
781*4882a593Smuzhiyun 		dev_warn(&platform_dev->dev, "virtual temperature(%d C)\n",
782*4882a593Smuzhiyun 			 tz->temperature / 1000);
783*4882a593Smuzhiyun }
784*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(dump_virtual_temperature);
785*4882a593Smuzhiyun 
virtual_thermal_panic(struct notifier_block * this,unsigned long ev,void * ptr)786*4882a593Smuzhiyun static int virtual_thermal_panic(struct notifier_block *this,
787*4882a593Smuzhiyun 				 unsigned long ev, void *ptr)
788*4882a593Smuzhiyun {
789*4882a593Smuzhiyun 	dump_virtual_temperature();
790*4882a593Smuzhiyun 	return NOTIFY_DONE;
791*4882a593Smuzhiyun }
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun static struct notifier_block virtual_thermal_panic_block = {
794*4882a593Smuzhiyun 	.notifier_call = virtual_thermal_panic,
795*4882a593Smuzhiyun };
796*4882a593Smuzhiyun 
virtual_thermal_probe(struct platform_device * pdev)797*4882a593Smuzhiyun static int virtual_thermal_probe(struct platform_device *pdev)
798*4882a593Smuzhiyun {
799*4882a593Smuzhiyun 	struct device_node *np = pdev->dev.of_node;
800*4882a593Smuzhiyun 	int ret;
801*4882a593Smuzhiyun 	int leakage = 0;
802*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx;
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	const struct of_device_id *match;
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	match = of_match_node(of_virtual_thermal_match, np);
807*4882a593Smuzhiyun 	if (!match)
808*4882a593Smuzhiyun 		return -ENXIO;
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	ctx = devm_kzalloc(&pdev->dev, sizeof(struct virtual_thermal_data),
811*4882a593Smuzhiyun 			   GFP_KERNEL);
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	ctx->pdev = pdev;
814*4882a593Smuzhiyun 	ctx->dev = &pdev->dev;
815*4882a593Smuzhiyun 	platform_set_drvdata(pdev, ctx);
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 	platform_dev = pdev;
818*4882a593Smuzhiyun 
819*4882a593Smuzhiyun 	ctx->tuning_info = (struct thermal_tuning_info *)match->data;
820*4882a593Smuzhiyun 	if (!ctx->tuning_info) {
821*4882a593Smuzhiyun 		dev_err(&pdev->dev,
822*4882a593Smuzhiyun 			"failed to allocate memory for tuning info.\n");
823*4882a593Smuzhiyun 		return -EINVAL;
824*4882a593Smuzhiyun 	}
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	ret = rockchip_get_efuse_value(np, "cpu_leakage", &leakage);
827*4882a593Smuzhiyun 	if (!ret)
828*4882a593Smuzhiyun 		dev_info(&pdev->dev, "leakage=%d\n", leakage);
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	ctx->cmp_lkg_temp = compensate_leakage(leakage);
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	ctx->psy_bat = power_supply_get_by_name("battery");
833*4882a593Smuzhiyun 	ctx->psy_usb = power_supply_get_by_name("usb");
834*4882a593Smuzhiyun 	ctx->psy_ac = power_supply_get_by_name("ac");
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun 	ret = cpufreq_register_notifier(&temp_notifier_block,
837*4882a593Smuzhiyun 					CPUFREQ_TRANSITION_NOTIFIER);
838*4882a593Smuzhiyun 	if (ret) {
839*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to register cpufreq notifier: %d\n",
840*4882a593Smuzhiyun 			ret);
841*4882a593Smuzhiyun 		return ret;
842*4882a593Smuzhiyun 	}
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	ctx->gpu_clk = clk_get_by_name("aclk_gpu");
845*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(ctx->gpu_clk)) {
846*4882a593Smuzhiyun 		ret = PTR_ERR(ctx->gpu_clk);
847*4882a593Smuzhiyun 		ctx->gpu_clk = NULL;
848*4882a593Smuzhiyun 		dev_warn(&pdev->dev, "failed to get gpu's clock: %d\n", ret);
849*4882a593Smuzhiyun 	}
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	ctx->vpu_clk = clk_get_by_name("aclk_vdpu");
852*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(ctx->vpu_clk)) {
853*4882a593Smuzhiyun 		ret = PTR_ERR(ctx->vpu_clk);
854*4882a593Smuzhiyun 		ctx->vpu_clk = NULL;
855*4882a593Smuzhiyun 		dev_warn(&pdev->dev, "failed to get vpu's clock: %d\n", ret);
856*4882a593Smuzhiyun 	}
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	ctx->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
859*4882a593Smuzhiyun 						NULL,
860*4882a593Smuzhiyun 						&virtual_of_thermal_ops);
861*4882a593Smuzhiyun 	if (IS_ERR(ctx->tzd)) {
862*4882a593Smuzhiyun 		ret = PTR_ERR(ctx->tzd);
863*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to register sensor 0: %d\n", ret);
864*4882a593Smuzhiyun 		goto err_unreg_cpufreq_notifier;
865*4882a593Smuzhiyun 	}
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun 	ret = atomic_notifier_chain_register(&panic_notifier_list,
868*4882a593Smuzhiyun 					     &virtual_thermal_panic_block);
869*4882a593Smuzhiyun 	if (ret) {
870*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to register panic notifier: %d\n",
871*4882a593Smuzhiyun 			ret);
872*4882a593Smuzhiyun 		goto err_unreg_cpufreq_notifier;
873*4882a593Smuzhiyun 	}
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	dev_info(&pdev->dev, "virtual tsadc probed successfully\n");
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun 	return 0;
878*4882a593Smuzhiyun 
879*4882a593Smuzhiyun err_unreg_cpufreq_notifier:
880*4882a593Smuzhiyun 	cpufreq_unregister_notifier(&temp_notifier_block,
881*4882a593Smuzhiyun 				    CPUFREQ_TRANSITION_NOTIFIER);
882*4882a593Smuzhiyun 	if (ctx->gpu_clk)
883*4882a593Smuzhiyun 		clk_put(ctx->gpu_clk);
884*4882a593Smuzhiyun 	if (ctx->vpu_clk)
885*4882a593Smuzhiyun 		clk_put(ctx->vpu_clk);
886*4882a593Smuzhiyun 
887*4882a593Smuzhiyun 	return ret;
888*4882a593Smuzhiyun }
889*4882a593Smuzhiyun 
virtual_thermal_remove(struct platform_device * pdev)890*4882a593Smuzhiyun static int virtual_thermal_remove(struct platform_device *pdev)
891*4882a593Smuzhiyun {
892*4882a593Smuzhiyun 	struct virtual_thermal_data *ctx = platform_get_drvdata(pdev);
893*4882a593Smuzhiyun 	atomic_notifier_chain_unregister(&panic_notifier_list,
894*4882a593Smuzhiyun 					 &virtual_thermal_panic_block);
895*4882a593Smuzhiyun 	cpufreq_unregister_notifier(&temp_notifier_block,
896*4882a593Smuzhiyun 				    CPUFREQ_TRANSITION_NOTIFIER);
897*4882a593Smuzhiyun 	if (ctx->gpu_clk)
898*4882a593Smuzhiyun 		clk_put(ctx->gpu_clk);
899*4882a593Smuzhiyun 	if (ctx->vpu_clk)
900*4882a593Smuzhiyun 		clk_put(ctx->vpu_clk);
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun 	return 0;
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun 
905*4882a593Smuzhiyun static struct platform_driver virtual_thermal_driver = {
906*4882a593Smuzhiyun 	.driver = {
907*4882a593Smuzhiyun 		   .name = "virtual-thermal",
908*4882a593Smuzhiyun 		   .of_match_table = of_virtual_thermal_match,
909*4882a593Smuzhiyun 		   },
910*4882a593Smuzhiyun 	.probe = virtual_thermal_probe,
911*4882a593Smuzhiyun 	.remove = virtual_thermal_remove,
912*4882a593Smuzhiyun };
913*4882a593Smuzhiyun 
virtual_thermal_init_driver(void)914*4882a593Smuzhiyun static int __init virtual_thermal_init_driver(void)
915*4882a593Smuzhiyun {
916*4882a593Smuzhiyun 	return platform_driver_register(&virtual_thermal_driver);
917*4882a593Smuzhiyun }
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun late_initcall(virtual_thermal_init_driver);
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun MODULE_DESCRIPTION("ROCKCHIP THERMAL Driver");
922*4882a593Smuzhiyun MODULE_AUTHOR("Rockchip, Inc.");
923*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
924*4882a593Smuzhiyun MODULE_ALIAS("platform:virtual-thermal");
925