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