• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Thermal sensor driver for Allwinner SOC
4  * Copyright (C) 2019 frank@allwinnertech.com
5  */
6 #include <linux/clk.h>
7 #include <linux/device.h>
8 #include <linux/interrupt.h>
9 #include <linux/module.h>
10 #include <linux/nvmem-consumer.h>
11 #include <linux/of_device.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14 #include <linux/slab.h>
15 #include <linux/thermal.h>
16 #include <linux/reset.h>
17 
18 #define MAX_SENSOR_NUM	4
19 
20 #define FT_TEMP_MASK				GENMASK(11, 0)
21 #define TEMP_CALIB_MASK				GENMASK(11, 0)
22 #define CALIBRATE_DEFAULT			0x800
23 
24 #define SUN50I_H616_THS_CTRL0			0x00
25 #define SUN50I_H616_THS_ENABLE			0x04
26 #define SUN50I_H616_THS_PC			0x08
27 #define SUN50I_H616_THS_MFC			0x30
28 #define SUN50I_H616_THS_TEMP_CALIB		0xa0
29 #define SUN50I_H616_THS_TEMP_DATA		0xc0
30 
31 #define SUN50I_THS_CTRL0_T_ACQ(x)		(GENMASK(15, 0) & (x))
32 #define SUN50I_THS_CTRL0_FS_DIV(x)		((GENMASK(15, 0) & (x)) << 16)
33 #define SUN50I_THS_FILTER_EN			BIT(2)
34 #define SUN50I_THS_FILTER_TYPE(x)		(GENMASK(1, 0) & (x))
35 #define SUN50I_H616_THS_PC_TEMP_PERIOD(x)	((GENMASK(19, 0) & (x)) << 12)
36 
37 struct ths_device;
38 
39 struct tsensor {
40 	struct ths_device		*tmdev;
41 	struct thermal_zone_device	*tzd;
42 	int				id;
43 };
44 
45 struct ths_thermal_chip {
46 	bool            has_bus_clk;
47 	int		sensor_num;
48 	int		offset;
49 	int		scale;
50 	int		ft_deviation;
51 	int		temp_data_base;
52 	int		(*calibrate)(struct ths_device *tmdev,
53 				     u16 *caldata, int callen);
54 	int		(*init)(struct ths_device *tmdev);
55 };
56 
57 struct ths_device {
58 	bool					has_calibration;
59 	const struct ths_thermal_chip		*chip;
60 	struct device				*dev;
61 	struct regmap				*regmap;
62 	struct clk				*bus_clk;
63 	struct tsensor				sensor[MAX_SENSOR_NUM];
64 	struct reset_control			*reset;
65 };
66 
67 /* Temp Unit: millidegree Celsius */
sunxi_ths_reg2temp(struct ths_device * tmdev,int reg)68 static int sunxi_ths_reg2temp(struct ths_device *tmdev, int reg)
69 {
70 	return (reg + tmdev->chip->offset) * tmdev->chip->scale;
71 }
72 
sunxi_ths_get_temp(void * data,int * temp)73 static int sunxi_ths_get_temp(void *data, int *temp)
74 {
75 	struct tsensor *s = data;
76 	struct ths_device *tmdev = s->tmdev;
77 	int val = 0;
78 
79 	regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
80 		    0x4 * s->id, &val);
81 
82 	/* ths have no data yet */
83 	if (unlikely(!val))
84 		return -EAGAIN;
85 
86 	*temp = sunxi_ths_reg2temp(tmdev, val);
87 	/*
88 	 * There are problems with the calibration values of some platforms,
89 	 * which makes the temperature calculated by the original temperature
90 	 * calculation formula inaccurate. If the chip is calibrated, this
91 	 * value is added by default.*/
92 	if (tmdev->has_calibration)
93 		*temp += tmdev->chip->ft_deviation;
94 
95 	return 0;
96 }
97 
98 static const struct thermal_zone_of_device_ops ths_ops = {
99 	.get_temp = sunxi_ths_get_temp,
100 };
101 
102 static const struct regmap_config config = {
103 	.reg_bits = 32,
104 	.val_bits = 32,
105 	.reg_stride = 4,
106 	.fast_io = true,
107 };
108 
sun50i_h616_ths_calibrate(struct ths_device * tmdev,u16 * caldata,int callen)109 static int sun50i_h616_ths_calibrate(struct ths_device *tmdev,
110 				   u16 *caldata, int callen)
111 {
112 	struct device *dev = tmdev->dev;
113 	int i, ft_temp;
114 
115 	if (!caldata[0])
116 		return -EINVAL;
117 
118 	/*
119 	 * efuse layout:
120 	 *
121 	 * 0      11  16     27   32     43   48    57
122 	 * +----------+-----------+-----------+-----------+
123 	 * |  temp |  |sensor0|   |sensor1|   |sensor2|   |
124 	 * +----------+-----------+-----------+-----------+
125 	 *                      ^           ^           ^
126 	 *                      |           |           |
127 	 *                      |           |           sensor3[11:8]
128 	 *                      |           sensor3[7:4]
129 	 *                      sensor3[3:0]
130 	 *
131 	 * The calibration data on the H616 is the ambient temperature and
132 	 * sensor values that are filled during the factory test stage.
133 	 *
134 	 * The unit of stored FT temperature is 0.1 degreee celusis.
135 	 *
136 	 * We need to calculate a delta between measured and caluclated
137 	 * register values and this will become a calibration offset.
138 	 */
139 	ft_temp = caldata[0] & FT_TEMP_MASK;
140 
141 	for (i = 0; i < tmdev->chip->sensor_num; i++) {
142 		int delta, cdata, offset, reg;
143 
144 		if (i == 3)
145 			reg = (caldata[1] >> 12)
146 			      | (caldata[2] >> 12 << 4)
147 			      | (caldata[3] >> 12 << 8);
148 		else
149 			reg = (int)caldata[i + 1] & TEMP_CALIB_MASK;
150 
151 		/*
152 		 * Our calculation formula is like this,
153 		 * the temp unit above is Celsius:
154 		 *
155 		 * T = (sensor_data + a) / b
156 		 * cdata = 0x800 - [(ft_temp - T) * b]
157 		 *
158 		 * b is a floating-point number
159 		 * with an absolute value less than 1000.
160 		 *
161 		 * sunxi_ths_reg2temp uses milli-degrees Celsius,
162 		 * with offset and scale parameters.
163 		 * T = (sensor_data + a) * 1000 / b
164 		 *
165 		 * ----------------------------------------------
166 		 *
167 		 * So:
168 		 *
169 		 * offset = a, scale = 1000 / b
170 		 * cdata = 0x800 - [(ft_temp - T) * 1000 / scale]
171 		 */
172 		delta = (ft_temp * 100 - sunxi_ths_reg2temp(tmdev, reg))
173 			/ tmdev->chip->scale;
174 		cdata = CALIBRATE_DEFAULT - delta;
175 		if (cdata & ~TEMP_CALIB_MASK) {
176 			dev_warn(dev, "sensor%d is not calibrated.\n", i);
177 
178 			continue;
179 		}
180 
181 		offset = (i % 2) * 16;
182 		regmap_update_bits(tmdev->regmap,
183 				   SUN50I_H616_THS_TEMP_CALIB + (i / 2 * 4),
184 				   0xfff << offset,
185 				   cdata << offset);
186 	}
187 
188 	tmdev->has_calibration = true;
189 	return 0;
190 }
191 
sunxi_ths_calibrate(struct ths_device * tmdev)192 static int sunxi_ths_calibrate(struct ths_device *tmdev)
193 {
194 	struct nvmem_cell *calcell;
195 	struct device *dev = tmdev->dev;
196 	u16 *caldata;
197 	size_t callen;
198 	int ret = 0;
199 
200 	calcell = nvmem_cell_get(dev, "calibration");
201 	if (IS_ERR(calcell)) {
202 		if (PTR_ERR(calcell) == -EPROBE_DEFER)
203 			return -EPROBE_DEFER;
204 		goto out;
205 	}
206 
207 	caldata = nvmem_cell_read(calcell, &callen);
208 	if (IS_ERR(caldata)) {
209 		ret = PTR_ERR(caldata);
210 		goto out_put;
211 	}
212 
213 	tmdev->chip->calibrate(tmdev, caldata, callen);
214 
215 	kfree(caldata);
216 out_put:
217 	nvmem_cell_put(calcell);
218 out:
219 	return ret;
220 }
221 
sunxi_ths_resource_init(struct ths_device * tmdev)222 static int sunxi_ths_resource_init(struct ths_device *tmdev)
223 {
224 	struct device *dev = tmdev->dev;
225 	struct platform_device *pdev = to_platform_device(dev);
226 	void __iomem *base;
227 	int ret;
228 
229 	base = devm_platform_ioremap_resource(pdev, 0);
230 	if (IS_ERR(base))
231 		return PTR_ERR(base);
232 
233 	tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
234 	if (IS_ERR(tmdev->regmap))
235 		return PTR_ERR(tmdev->regmap);
236 
237 	if (tmdev->chip->has_bus_clk) {
238 		tmdev->reset = devm_reset_control_get(dev, NULL);
239 		if (IS_ERR(tmdev->reset))
240 			return PTR_ERR(tmdev->reset);
241 
242 		tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus");
243 		if (IS_ERR(tmdev->bus_clk))
244 			return PTR_ERR(tmdev->bus_clk);
245 	}
246 
247 	ret = reset_control_deassert(tmdev->reset);
248 	if (ret)
249 		return ret;
250 
251 	ret = clk_prepare_enable(tmdev->bus_clk);
252 	if (ret)
253 		goto assert_reset;
254 
255 	ret = sunxi_ths_calibrate(tmdev);
256 	if (ret)
257 		goto bus_disable;
258 
259 	return 0;
260 
261 bus_disable:
262 	clk_disable_unprepare(tmdev->bus_clk);
263 assert_reset:
264 	reset_control_assert(tmdev->reset);
265 
266 	return ret;
267 }
268 
sun50i_h616_thermal_init(struct ths_device * tmdev)269 static int sun50i_h616_thermal_init(struct ths_device *tmdev)
270 {
271 	int val;
272 
273 	/*
274 	 * For sun50iw9p1:
275 	 * It is necessary that reg[0x03000000] bit[16] is 0.
276 	 */
277 	regmap_write(tmdev->regmap, SUN50I_H616_THS_CTRL0,
278 		     SUN50I_THS_CTRL0_T_ACQ(47) | SUN50I_THS_CTRL0_FS_DIV(479));
279 	regmap_write(tmdev->regmap, SUN50I_H616_THS_MFC,
280 		     SUN50I_THS_FILTER_EN |
281 		     SUN50I_THS_FILTER_TYPE(1));
282 	regmap_write(tmdev->regmap, SUN50I_H616_THS_PC,
283 		     SUN50I_H616_THS_PC_TEMP_PERIOD(365));
284 	val = GENMASK(tmdev->chip->sensor_num - 1, 0);
285 	regmap_write(tmdev->regmap, SUN50I_H616_THS_ENABLE, val);
286 
287 	return 0;
288 }
289 
sunxi_ths_register(struct ths_device * tmdev)290 static int sunxi_ths_register(struct ths_device *tmdev)
291 {
292 	int i;
293 
294 	for (i = 0; i < tmdev->chip->sensor_num; i++) {
295 		tmdev->sensor[i].tmdev = tmdev;
296 		tmdev->sensor[i].id = i;
297 		tmdev->sensor[i].tzd =
298 			devm_thermal_zone_of_sensor_register(tmdev->dev,
299 							     i,
300 							     &tmdev->sensor[i],
301 							     &ths_ops);
302 		if (IS_ERR(tmdev->sensor[i].tzd))
303 			return PTR_ERR(tmdev->sensor[i].tzd);
304 	}
305 
306 	return 0;
307 }
308 
sunxi_ths_probe(struct platform_device * pdev)309 static int sunxi_ths_probe(struct platform_device *pdev)
310 {
311 	struct ths_device *tmdev;
312 	struct device *dev = &pdev->dev;
313 	int ret;
314 
315 	tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
316 	if (!tmdev)
317 		return -ENOMEM;
318 
319 	tmdev->dev = dev;
320 	tmdev->chip = of_device_get_match_data(&pdev->dev);
321 	if (!tmdev->chip)
322 		return -EINVAL;
323 
324 	platform_set_drvdata(pdev, tmdev);
325 
326 	ret = sunxi_ths_resource_init(tmdev);
327 	if (ret)
328 		return ret;
329 
330 	ret = tmdev->chip->init(tmdev);
331 	if (ret)
332 		return ret;
333 
334 	ret = sunxi_ths_register(tmdev);
335 	if (ret)
336 		return ret;
337 
338 	return ret;
339 }
340 
sunxi_ths_remove(struct platform_device * pdev)341 static int sunxi_ths_remove(struct platform_device *pdev)
342 {
343 	struct ths_device *tmdev = platform_get_drvdata(pdev);
344 
345 	clk_disable_unprepare(tmdev->bus_clk);
346 
347 	return 0;
348 }
349 
sunxi_thermal_suspend(struct device * dev)350 static int __maybe_unused sunxi_thermal_suspend(struct device *dev)
351 {
352 	struct ths_device *tmdev = dev_get_drvdata(dev);
353 
354 	clk_disable_unprepare(tmdev->bus_clk);
355 
356 	return 0;
357 }
358 
sunxi_thermal_resume(struct device * dev)359 static int __maybe_unused sunxi_thermal_resume(struct device *dev)
360 {
361 	struct ths_device *tmdev = dev_get_drvdata(dev);
362 
363 	clk_prepare_enable(tmdev->bus_clk);
364 	sunxi_ths_calibrate(tmdev);
365 	tmdev->chip->init(tmdev);
366 
367 	return 0;
368 }
369 
370 static const struct ths_thermal_chip sun50iw9p1_ths = {
371 	.sensor_num = 4,
372 	.has_bus_clk = true,
373 	.offset = -3255,
374 	.scale = -81,
375 	.ft_deviation = 8000,
376 	.temp_data_base = SUN50I_H616_THS_TEMP_DATA,
377 	.calibrate = sun50i_h616_ths_calibrate,
378 	.init = sun50i_h616_thermal_init,
379 };
380 
381 static const struct ths_thermal_chip sun50iw10p1_ths = {
382 	.sensor_num = 3,
383 	.has_bus_clk = true,
384 	.offset = -2794,
385 	.scale = -67,
386 	.ft_deviation = 8000,
387 	.temp_data_base = SUN50I_H616_THS_TEMP_DATA,
388 	.calibrate = sun50i_h616_ths_calibrate,
389 	.init = sun50i_h616_thermal_init,
390 };
391 
392 static const struct ths_thermal_chip sun8iw20p1_ths = {
393 	.sensor_num = 1,
394 	.has_bus_clk = true,
395 	.offset = -2800,
396 	.scale = -67,
397 	.ft_deviation = 0,
398 	.temp_data_base = SUN50I_H616_THS_TEMP_DATA,
399 	.calibrate = sun50i_h616_ths_calibrate,
400 	.init = sun50i_h616_thermal_init,
401 };
402 
403 
404 static const struct of_device_id of_ths_match[] = {
405 	{ .compatible = "allwinner,sun50iw9p1-ths", .data = &sun50iw9p1_ths },
406 	{ .compatible = "allwinner,sun50iw10p1-ths", .data = &sun50iw10p1_ths },
407 	{ .compatible = "allwinner,sun8iw20p1-ths", .data = &sun8iw20p1_ths },
408 	{ .compatible = "allwinner,sun20iw1p1-ths", .data = &sun8iw20p1_ths },
409 	{ /* sentinel */ },
410 };
411 MODULE_DEVICE_TABLE(of, of_ths_match);
412 
413 static SIMPLE_DEV_PM_OPS(sunxi_thermal_pm_ops,
414 			 sunxi_thermal_suspend, sunxi_thermal_resume);
415 
416 static struct platform_driver ths_driver = {
417 	.probe = sunxi_ths_probe,
418 	.remove = sunxi_ths_remove,
419 	.driver = {
420 		.name = "sunxi-thermal",
421 		.pm = &sunxi_thermal_pm_ops,
422 		.of_match_table = of_ths_match,
423 	},
424 };
425 module_platform_driver(ths_driver);
426 
427 MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
428 MODULE_LICENSE("GPL v2");
429