xref: /OK3568_Linux_fs/kernel/drivers/thermal/ti-soc-thermal/ti-thermal-common.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * OMAP thermal driver interface
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
6*4882a593Smuzhiyun  * Contact:
7*4882a593Smuzhiyun  *   Eduardo Valentin <eduardo.valentin@ti.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun #include <linux/err.h>
12*4882a593Smuzhiyun #include <linux/mutex.h>
13*4882a593Smuzhiyun #include <linux/gfp.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/workqueue.h>
16*4882a593Smuzhiyun #include <linux/thermal.h>
17*4882a593Smuzhiyun #include <linux/cpufreq.h>
18*4882a593Smuzhiyun #include <linux/cpumask.h>
19*4882a593Smuzhiyun #include <linux/cpu_cooling.h>
20*4882a593Smuzhiyun #include <linux/of.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include "ti-thermal.h"
23*4882a593Smuzhiyun #include "ti-bandgap.h"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* common data structures */
26*4882a593Smuzhiyun struct ti_thermal_data {
27*4882a593Smuzhiyun 	struct cpufreq_policy *policy;
28*4882a593Smuzhiyun 	struct thermal_zone_device *ti_thermal;
29*4882a593Smuzhiyun 	struct thermal_zone_device *pcb_tz;
30*4882a593Smuzhiyun 	struct thermal_cooling_device *cool_dev;
31*4882a593Smuzhiyun 	struct ti_bandgap *bgp;
32*4882a593Smuzhiyun 	enum thermal_device_mode mode;
33*4882a593Smuzhiyun 	struct work_struct thermal_wq;
34*4882a593Smuzhiyun 	int sensor_id;
35*4882a593Smuzhiyun 	bool our_zone;
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun 
ti_thermal_work(struct work_struct * work)38*4882a593Smuzhiyun static void ti_thermal_work(struct work_struct *work)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	struct ti_thermal_data *data = container_of(work,
41*4882a593Smuzhiyun 					struct ti_thermal_data, thermal_wq);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
46*4882a593Smuzhiyun 		data->ti_thermal->type);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun /**
50*4882a593Smuzhiyun  * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature
51*4882a593Smuzhiyun  * @t:	omap sensor temperature
52*4882a593Smuzhiyun  * @s:	omap sensor slope value
53*4882a593Smuzhiyun  * @c:	omap sensor const value
54*4882a593Smuzhiyun  */
ti_thermal_hotspot_temperature(int t,int s,int c)55*4882a593Smuzhiyun static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	int delta = t * s / 1000 + c;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	if (delta < 0)
60*4882a593Smuzhiyun 		delta = 0;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	return t + delta;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun /* thermal zone ops */
66*4882a593Smuzhiyun /* Get temperature callback function for thermal zone */
__ti_thermal_get_temp(void * devdata,int * temp)67*4882a593Smuzhiyun static inline int __ti_thermal_get_temp(void *devdata, int *temp)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	struct thermal_zone_device *pcb_tz = NULL;
70*4882a593Smuzhiyun 	struct ti_thermal_data *data = devdata;
71*4882a593Smuzhiyun 	struct ti_bandgap *bgp;
72*4882a593Smuzhiyun 	const struct ti_temp_sensor *s;
73*4882a593Smuzhiyun 	int ret, tmp, slope, constant;
74*4882a593Smuzhiyun 	int pcb_temp;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	if (!data)
77*4882a593Smuzhiyun 		return 0;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	bgp = data->bgp;
80*4882a593Smuzhiyun 	s = &bgp->conf->sensors[data->sensor_id];
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp);
83*4882a593Smuzhiyun 	if (ret)
84*4882a593Smuzhiyun 		return ret;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	/* Default constants */
87*4882a593Smuzhiyun 	slope = thermal_zone_get_slope(data->ti_thermal);
88*4882a593Smuzhiyun 	constant = thermal_zone_get_offset(data->ti_thermal);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	pcb_tz = data->pcb_tz;
91*4882a593Smuzhiyun 	/* In case pcb zone is available, use the extrapolation rule with it */
92*4882a593Smuzhiyun 	if (!IS_ERR(pcb_tz)) {
93*4882a593Smuzhiyun 		ret = thermal_zone_get_temp(pcb_tz, &pcb_temp);
94*4882a593Smuzhiyun 		if (!ret) {
95*4882a593Smuzhiyun 			tmp -= pcb_temp; /* got a valid PCB temp */
96*4882a593Smuzhiyun 			slope = s->slope_pcb;
97*4882a593Smuzhiyun 			constant = s->constant_pcb;
98*4882a593Smuzhiyun 		} else {
99*4882a593Smuzhiyun 			dev_err(bgp->dev,
100*4882a593Smuzhiyun 				"Failed to read PCB state. Using defaults\n");
101*4882a593Smuzhiyun 			ret = 0;
102*4882a593Smuzhiyun 		}
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun 	*temp = ti_thermal_hotspot_temperature(tmp, slope, constant);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	return ret;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
ti_thermal_get_temp(struct thermal_zone_device * thermal,int * temp)109*4882a593Smuzhiyun static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
110*4882a593Smuzhiyun 				      int *temp)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	struct ti_thermal_data *data = thermal->devdata;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	return __ti_thermal_get_temp(data, temp);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
__ti_thermal_get_trend(void * p,int trip,enum thermal_trend * trend)117*4882a593Smuzhiyun static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	struct ti_thermal_data *data = p;
120*4882a593Smuzhiyun 	struct ti_bandgap *bgp;
121*4882a593Smuzhiyun 	int id, tr, ret = 0;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	bgp = data->bgp;
124*4882a593Smuzhiyun 	id = data->sensor_id;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	ret = ti_bandgap_get_trend(bgp, id, &tr);
127*4882a593Smuzhiyun 	if (ret)
128*4882a593Smuzhiyun 		return ret;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	if (tr > 0)
131*4882a593Smuzhiyun 		*trend = THERMAL_TREND_RAISING;
132*4882a593Smuzhiyun 	else if (tr < 0)
133*4882a593Smuzhiyun 		*trend = THERMAL_TREND_DROPPING;
134*4882a593Smuzhiyun 	else
135*4882a593Smuzhiyun 		*trend = THERMAL_TREND_STABLE;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	return 0;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun static const struct thermal_zone_of_device_ops ti_of_thermal_ops = {
141*4882a593Smuzhiyun 	.get_temp = __ti_thermal_get_temp,
142*4882a593Smuzhiyun 	.get_trend = __ti_thermal_get_trend,
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun static struct ti_thermal_data
ti_thermal_build_data(struct ti_bandgap * bgp,int id)146*4882a593Smuzhiyun *ti_thermal_build_data(struct ti_bandgap *bgp, int id)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	struct ti_thermal_data *data;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL);
151*4882a593Smuzhiyun 	if (!data) {
152*4882a593Smuzhiyun 		dev_err(bgp->dev, "kzalloc fail\n");
153*4882a593Smuzhiyun 		return NULL;
154*4882a593Smuzhiyun 	}
155*4882a593Smuzhiyun 	data->sensor_id = id;
156*4882a593Smuzhiyun 	data->bgp = bgp;
157*4882a593Smuzhiyun 	data->mode = THERMAL_DEVICE_ENABLED;
158*4882a593Smuzhiyun 	/* pcb_tz will be either valid or PTR_ERR() */
159*4882a593Smuzhiyun 	data->pcb_tz = thermal_zone_get_zone_by_name("pcb");
160*4882a593Smuzhiyun 	INIT_WORK(&data->thermal_wq, ti_thermal_work);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return data;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
ti_thermal_expose_sensor(struct ti_bandgap * bgp,int id,char * domain)165*4882a593Smuzhiyun int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
166*4882a593Smuzhiyun 			     char *domain)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	struct ti_thermal_data *data;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	data = ti_bandgap_get_sensor_data(bgp, id);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(data))
173*4882a593Smuzhiyun 		data = ti_thermal_build_data(bgp, id);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (!data)
176*4882a593Smuzhiyun 		return -EINVAL;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	/* in case this is specified by DT */
179*4882a593Smuzhiyun 	data->ti_thermal = devm_thermal_zone_of_sensor_register(bgp->dev, id,
180*4882a593Smuzhiyun 					data, &ti_of_thermal_ops);
181*4882a593Smuzhiyun 	if (IS_ERR(data->ti_thermal)) {
182*4882a593Smuzhiyun 		dev_err(bgp->dev, "thermal zone device is NULL\n");
183*4882a593Smuzhiyun 		return PTR_ERR(data->ti_thermal);
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	ti_bandgap_set_sensor_data(bgp, id, data);
187*4882a593Smuzhiyun 	ti_bandgap_write_update_interval(bgp, data->sensor_id,
188*4882a593Smuzhiyun 					data->ti_thermal->polling_delay);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	return 0;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
ti_thermal_remove_sensor(struct ti_bandgap * bgp,int id)193*4882a593Smuzhiyun int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	struct ti_thermal_data *data;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	data = ti_bandgap_get_sensor_data(bgp, id);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	if (!IS_ERR_OR_NULL(data) && data->ti_thermal) {
200*4882a593Smuzhiyun 		if (data->our_zone)
201*4882a593Smuzhiyun 			thermal_zone_device_unregister(data->ti_thermal);
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	return 0;
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
ti_thermal_report_sensor_temperature(struct ti_bandgap * bgp,int id)207*4882a593Smuzhiyun int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct ti_thermal_data *data;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	data = ti_bandgap_get_sensor_data(bgp, id);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	schedule_work(&data->thermal_wq);
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	return 0;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
ti_thermal_register_cpu_cooling(struct ti_bandgap * bgp,int id)218*4882a593Smuzhiyun int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun 	struct ti_thermal_data *data;
221*4882a593Smuzhiyun 	struct device_node *np = bgp->dev->of_node;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	/*
224*4882a593Smuzhiyun 	 * We are assuming here that if one deploys the zone
225*4882a593Smuzhiyun 	 * using DT, then it must be aware that the cooling device
226*4882a593Smuzhiyun 	 * loading has to happen via cpufreq driver.
227*4882a593Smuzhiyun 	 */
228*4882a593Smuzhiyun 	if (of_find_property(np, "#thermal-sensor-cells", NULL))
229*4882a593Smuzhiyun 		return 0;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	data = ti_bandgap_get_sensor_data(bgp, id);
232*4882a593Smuzhiyun 	if (!data || IS_ERR(data))
233*4882a593Smuzhiyun 		data = ti_thermal_build_data(bgp, id);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	if (!data)
236*4882a593Smuzhiyun 		return -EINVAL;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	data->policy = cpufreq_cpu_get(0);
239*4882a593Smuzhiyun 	if (!data->policy) {
240*4882a593Smuzhiyun 		pr_debug("%s: CPUFreq policy not found\n", __func__);
241*4882a593Smuzhiyun 		return -EPROBE_DEFER;
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	/* Register cooling device */
245*4882a593Smuzhiyun 	data->cool_dev = cpufreq_cooling_register(data->policy);
246*4882a593Smuzhiyun 	if (IS_ERR(data->cool_dev)) {
247*4882a593Smuzhiyun 		int ret = PTR_ERR(data->cool_dev);
248*4882a593Smuzhiyun 		dev_err(bgp->dev, "Failed to register cpu cooling device %d\n",
249*4882a593Smuzhiyun 			ret);
250*4882a593Smuzhiyun 		cpufreq_cpu_put(data->policy);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 		return ret;
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 	ti_bandgap_set_sensor_data(bgp, id, data);
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	return 0;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
ti_thermal_unregister_cpu_cooling(struct ti_bandgap * bgp,int id)259*4882a593Smuzhiyun int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	struct ti_thermal_data *data;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	data = ti_bandgap_get_sensor_data(bgp, id);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (!IS_ERR_OR_NULL(data)) {
266*4882a593Smuzhiyun 		cpufreq_cooling_unregister(data->cool_dev);
267*4882a593Smuzhiyun 		if (data->policy)
268*4882a593Smuzhiyun 			cpufreq_cpu_put(data->policy);
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	return 0;
272*4882a593Smuzhiyun }
273