xref: /openbmc/linux/drivers/thermal/imx_thermal.c (revision 017e51420cc44098308b00dffd9d4e514ddf40f3)
1 /*
2  * Copyright 2013 Freescale Semiconductor, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  */
9 
10 #include <linux/cpu_cooling.h>
11 #include <linux/cpufreq.h>
12 #include <linux/delay.h>
13 #include <linux/device.h>
14 #include <linux/init.h>
15 #include <linux/io.h>
16 #include <linux/kernel.h>
17 #include <linux/mfd/syscon.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 #include <linux/slab.h>
23 #include <linux/thermal.h>
24 #include <linux/types.h>
25 
26 #define REG_SET		0x4
27 #define REG_CLR		0x8
28 #define REG_TOG		0xc
29 
30 #define MISC0				0x0150
31 #define MISC0_REFTOP_SELBIASOFF		(1 << 3)
32 
33 #define TEMPSENSE0			0x0180
34 #define TEMPSENSE0_TEMP_CNT_SHIFT	8
35 #define TEMPSENSE0_TEMP_CNT_MASK	(0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
36 #define TEMPSENSE0_FINISHED		(1 << 2)
37 #define TEMPSENSE0_MEASURE_TEMP		(1 << 1)
38 #define TEMPSENSE0_POWER_DOWN		(1 << 0)
39 
40 #define TEMPSENSE1			0x0190
41 #define TEMPSENSE1_MEASURE_FREQ		0xffff
42 
43 #define OCOTP_ANA1			0x04e0
44 
45 /* The driver supports 1 passive trip point and 1 critical trip point */
46 enum imx_thermal_trip {
47 	IMX_TRIP_PASSIVE,
48 	IMX_TRIP_CRITICAL,
49 	IMX_TRIP_NUM,
50 };
51 
52 /*
53  * It defines the temperature in millicelsius for passive trip point
54  * that will trigger cooling action when crossed.
55  */
56 #define IMX_TEMP_PASSIVE		85000
57 
58 #define IMX_POLLING_DELAY		2000 /* millisecond */
59 #define IMX_PASSIVE_DELAY		1000
60 
61 struct imx_thermal_data {
62 	struct thermal_zone_device *tz;
63 	struct thermal_cooling_device *cdev;
64 	enum thermal_device_mode mode;
65 	struct regmap *tempmon;
66 	int c1, c2; /* See formula in imx_get_sensor_data() */
67 	unsigned long temp_passive;
68 	unsigned long temp_critical;
69 };
70 
71 static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
72 {
73 	struct imx_thermal_data *data = tz->devdata;
74 	struct regmap *map = data->tempmon;
75 	static unsigned long last_temp;
76 	unsigned int n_meas;
77 	u32 val;
78 
79 	/*
80 	 * Every time we measure the temperature, we will power on the
81 	 * temperature sensor, enable measurements, take a reading,
82 	 * disable measurements, power off the temperature sensor.
83 	 */
84 	regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
85 	regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
86 
87 	/*
88 	 * According to the temp sensor designers, it may require up to ~17us
89 	 * to complete a measurement.
90 	 */
91 	usleep_range(20, 50);
92 
93 	regmap_read(map, TEMPSENSE0, &val);
94 	regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
95 	regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
96 
97 	if ((val & TEMPSENSE0_FINISHED) == 0) {
98 		dev_dbg(&tz->device, "temp measurement never finished\n");
99 		return -EAGAIN;
100 	}
101 
102 	n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT;
103 
104 	/* See imx_get_sensor_data() for formula derivation */
105 	*temp = data->c2 + data->c1 * n_meas;
106 
107 	if (*temp != last_temp) {
108 		dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
109 		last_temp = *temp;
110 	}
111 
112 	return 0;
113 }
114 
115 static int imx_get_mode(struct thermal_zone_device *tz,
116 			enum thermal_device_mode *mode)
117 {
118 	struct imx_thermal_data *data = tz->devdata;
119 
120 	*mode = data->mode;
121 
122 	return 0;
123 }
124 
125 static int imx_set_mode(struct thermal_zone_device *tz,
126 			enum thermal_device_mode mode)
127 {
128 	struct imx_thermal_data *data = tz->devdata;
129 
130 	if (mode == THERMAL_DEVICE_ENABLED) {
131 		tz->polling_delay = IMX_POLLING_DELAY;
132 		tz->passive_delay = IMX_PASSIVE_DELAY;
133 	} else {
134 		tz->polling_delay = 0;
135 		tz->passive_delay = 0;
136 	}
137 
138 	data->mode = mode;
139 	thermal_zone_device_update(tz);
140 
141 	return 0;
142 }
143 
144 static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
145 			     enum thermal_trip_type *type)
146 {
147 	*type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
148 					     THERMAL_TRIP_CRITICAL;
149 	return 0;
150 }
151 
152 static int imx_get_crit_temp(struct thermal_zone_device *tz,
153 			     unsigned long *temp)
154 {
155 	struct imx_thermal_data *data = tz->devdata;
156 
157 	*temp = data->temp_critical;
158 	return 0;
159 }
160 
161 static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
162 			     unsigned long *temp)
163 {
164 	struct imx_thermal_data *data = tz->devdata;
165 
166 	*temp = (trip == IMX_TRIP_PASSIVE) ? data->temp_passive :
167 					     data->temp_critical;
168 	return 0;
169 }
170 
171 static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
172 			     unsigned long temp)
173 {
174 	struct imx_thermal_data *data = tz->devdata;
175 
176 	if (trip == IMX_TRIP_CRITICAL)
177 		return -EPERM;
178 
179 	if (temp > IMX_TEMP_PASSIVE)
180 		return -EINVAL;
181 
182 	data->temp_passive = temp;
183 
184 	return 0;
185 }
186 
187 static int imx_bind(struct thermal_zone_device *tz,
188 		    struct thermal_cooling_device *cdev)
189 {
190 	int ret;
191 
192 	ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
193 					       THERMAL_NO_LIMIT,
194 					       THERMAL_NO_LIMIT);
195 	if (ret) {
196 		dev_err(&tz->device,
197 			"binding zone %s with cdev %s failed:%d\n",
198 			tz->type, cdev->type, ret);
199 		return ret;
200 	}
201 
202 	return 0;
203 }
204 
205 static int imx_unbind(struct thermal_zone_device *tz,
206 		      struct thermal_cooling_device *cdev)
207 {
208 	int ret;
209 
210 	ret = thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev);
211 	if (ret) {
212 		dev_err(&tz->device,
213 			"unbinding zone %s with cdev %s failed:%d\n",
214 			tz->type, cdev->type, ret);
215 		return ret;
216 	}
217 
218 	return 0;
219 }
220 
221 static const struct thermal_zone_device_ops imx_tz_ops = {
222 	.bind = imx_bind,
223 	.unbind = imx_unbind,
224 	.get_temp = imx_get_temp,
225 	.get_mode = imx_get_mode,
226 	.set_mode = imx_set_mode,
227 	.get_trip_type = imx_get_trip_type,
228 	.get_trip_temp = imx_get_trip_temp,
229 	.get_crit_temp = imx_get_crit_temp,
230 	.set_trip_temp = imx_set_trip_temp,
231 };
232 
233 static int imx_get_sensor_data(struct platform_device *pdev)
234 {
235 	struct imx_thermal_data *data = platform_get_drvdata(pdev);
236 	struct regmap *map;
237 	int t1, t2, n1, n2;
238 	int ret;
239 	u32 val;
240 
241 	map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
242 					      "fsl,tempmon-data");
243 	if (IS_ERR(map)) {
244 		ret = PTR_ERR(map);
245 		dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret);
246 		return ret;
247 	}
248 
249 	ret = regmap_read(map, OCOTP_ANA1, &val);
250 	if (ret) {
251 		dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret);
252 		return ret;
253 	}
254 
255 	if (val == 0 || val == ~0) {
256 		dev_err(&pdev->dev, "invalid sensor calibration data\n");
257 		return -EINVAL;
258 	}
259 
260 	/*
261 	 * Sensor data layout:
262 	 *   [31:20] - sensor value @ 25C
263 	 *    [19:8] - sensor value of hot
264 	 *     [7:0] - hot temperature value
265 	 */
266 	n1 = val >> 20;
267 	n2 = (val & 0xfff00) >> 8;
268 	t2 = val & 0xff;
269 	t1 = 25; /* t1 always 25C */
270 
271 	/*
272 	 * Derived from linear interpolation,
273 	 * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
274 	 * We want to reduce this down to the minimum computation necessary
275 	 * for each temperature read.  Also, we want Tmeas in millicelsius
276 	 * and we don't want to lose precision from integer division. So...
277 	 * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
278 	 * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
279 	 * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
280 	 * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
281 	 * Let constant c2 = (1000 * T2) - (c1 * N2)
282 	 * milli_Tmeas = c2 + (c1 * Nmeas)
283 	 */
284 	data->c1 = 1000 * (t1 - t2) / (n1 - n2);
285 	data->c2 = 1000 * t2 - data->c1 * n2;
286 
287 	/*
288 	 * Set the default passive cooling trip point to 20 °C below the
289 	 * maximum die temperature. Can be changed from userspace.
290 	 */
291 	data->temp_passive = 1000 * (t2 - 20);
292 
293 	/*
294 	 * The maximum die temperature is t2, let's give 5 °C cushion
295 	 * for noise and possible temperature rise between measurements.
296 	 */
297 	data->temp_critical = 1000 * (t2 - 5);
298 
299 	return 0;
300 }
301 
302 static int imx_thermal_probe(struct platform_device *pdev)
303 {
304 	struct imx_thermal_data *data;
305 	struct cpumask clip_cpus;
306 	struct regmap *map;
307 	int ret;
308 
309 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
310 	if (!data)
311 		return -ENOMEM;
312 
313 	map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon");
314 	if (IS_ERR(map)) {
315 		ret = PTR_ERR(map);
316 		dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret);
317 		return ret;
318 	}
319 	data->tempmon = map;
320 
321 	platform_set_drvdata(pdev, data);
322 
323 	ret = imx_get_sensor_data(pdev);
324 	if (ret) {
325 		dev_err(&pdev->dev, "failed to get sensor data\n");
326 		return ret;
327 	}
328 
329 	/* Make sure sensor is in known good state for measurements */
330 	regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
331 	regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
332 	regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
333 	regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
334 	regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
335 
336 	cpumask_set_cpu(0, &clip_cpus);
337 	data->cdev = cpufreq_cooling_register(&clip_cpus);
338 	if (IS_ERR(data->cdev)) {
339 		ret = PTR_ERR(data->cdev);
340 		dev_err(&pdev->dev,
341 			"failed to register cpufreq cooling device: %d\n", ret);
342 		return ret;
343 	}
344 
345 	data->tz = thermal_zone_device_register("imx_thermal_zone",
346 						IMX_TRIP_NUM,
347 						BIT(IMX_TRIP_PASSIVE), data,
348 						&imx_tz_ops, NULL,
349 						IMX_PASSIVE_DELAY,
350 						IMX_POLLING_DELAY);
351 	if (IS_ERR(data->tz)) {
352 		ret = PTR_ERR(data->tz);
353 		dev_err(&pdev->dev,
354 			"failed to register thermal zone device %d\n", ret);
355 		cpufreq_cooling_unregister(data->cdev);
356 		return ret;
357 	}
358 
359 	data->mode = THERMAL_DEVICE_ENABLED;
360 
361 	return 0;
362 }
363 
364 static int imx_thermal_remove(struct platform_device *pdev)
365 {
366 	struct imx_thermal_data *data = platform_get_drvdata(pdev);
367 
368 	thermal_zone_device_unregister(data->tz);
369 	cpufreq_cooling_unregister(data->cdev);
370 
371 	return 0;
372 }
373 
374 #ifdef CONFIG_PM_SLEEP
375 static int imx_thermal_suspend(struct device *dev)
376 {
377 	struct imx_thermal_data *data = dev_get_drvdata(dev);
378 	struct regmap *map = data->tempmon;
379 	u32 val;
380 
381 	regmap_read(map, TEMPSENSE0, &val);
382 	if ((val & TEMPSENSE0_POWER_DOWN) == 0) {
383 		/*
384 		 * If a measurement is taking place, wait for a long enough
385 		 * time for it to finish, and then check again.  If it still
386 		 * does not finish, something must go wrong.
387 		 */
388 		udelay(50);
389 		regmap_read(map, TEMPSENSE0, &val);
390 		if ((val & TEMPSENSE0_POWER_DOWN) == 0)
391 			return -ETIMEDOUT;
392 	}
393 
394 	return 0;
395 }
396 
397 static int imx_thermal_resume(struct device *dev)
398 {
399 	/* Nothing to do for now */
400 	return 0;
401 }
402 #endif
403 
404 static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
405 			 imx_thermal_suspend, imx_thermal_resume);
406 
407 static const struct of_device_id of_imx_thermal_match[] = {
408 	{ .compatible = "fsl,imx6q-tempmon", },
409 	{ /* end */ }
410 };
411 
412 static struct platform_driver imx_thermal = {
413 	.driver = {
414 		.name	= "imx_thermal",
415 		.owner  = THIS_MODULE,
416 		.pm	= &imx_thermal_pm_ops,
417 		.of_match_table = of_imx_thermal_match,
418 	},
419 	.probe		= imx_thermal_probe,
420 	.remove		= imx_thermal_remove,
421 };
422 module_platform_driver(imx_thermal);
423 
424 MODULE_AUTHOR("Freescale Semiconductor, Inc.");
425 MODULE_DESCRIPTION("Thermal driver for Freescale i.MX SoCs");
426 MODULE_LICENSE("GPL v2");
427 MODULE_ALIAS("platform:imx-thermal");
428