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