]>
Commit | Line | Data |
---|---|---|
dccc5c3b YL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Thermal sensor driver for Allwinner SOC | |
4 | * Copyright (C) 2019 Yangtao Li | |
5 | * | |
6 | * Based on the work of Icenowy Zheng <icenowy@aosc.io> | |
7 | * Based on the work of Ondrej Jirman <megous@megous.com> | |
8 | * Based on the work of Josef Gajdusek <atx@atx.name> | |
9 | */ | |
10 | ||
11 | #include <linux/clk.h> | |
12 | #include <linux/device.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/nvmem-consumer.h> | |
16 | #include <linux/of_device.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/regmap.h> | |
19 | #include <linux/reset.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/thermal.h> | |
22 | ||
85f0ad22 YL |
23 | #include "thermal_hwmon.h" |
24 | ||
dccc5c3b YL |
25 | #define MAX_SENSOR_NUM 4 |
26 | ||
27 | #define FT_TEMP_MASK GENMASK(11, 0) | |
28 | #define TEMP_CALIB_MASK GENMASK(11, 0) | |
29 | #define CALIBRATE_DEFAULT 0x800 | |
30 | ||
31 | #define SUN8I_THS_CTRL0 0x00 | |
32 | #define SUN8I_THS_CTRL2 0x40 | |
33 | #define SUN8I_THS_IC 0x44 | |
34 | #define SUN8I_THS_IS 0x48 | |
35 | #define SUN8I_THS_MFC 0x70 | |
36 | #define SUN8I_THS_TEMP_CALIB 0x74 | |
37 | #define SUN8I_THS_TEMP_DATA 0x80 | |
38 | ||
39 | #define SUN50I_THS_CTRL0 0x00 | |
40 | #define SUN50I_H6_THS_ENABLE 0x04 | |
41 | #define SUN50I_H6_THS_PC 0x08 | |
42 | #define SUN50I_H6_THS_DIC 0x10 | |
43 | #define SUN50I_H6_THS_DIS 0x20 | |
44 | #define SUN50I_H6_THS_MFC 0x30 | |
45 | #define SUN50I_H6_THS_TEMP_CALIB 0xa0 | |
46 | #define SUN50I_H6_THS_TEMP_DATA 0xc0 | |
47 | ||
48 | #define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x)) | |
49 | #define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16) | |
50 | #define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8) | |
51 | ||
52 | #define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16) | |
53 | #define SUN50I_THS_FILTER_EN BIT(2) | |
54 | #define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x)) | |
55 | #define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) | |
56 | #define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x) | |
57 | ||
58 | /* millidegree celsius */ | |
dccc5c3b YL |
59 | |
60 | struct tsensor { | |
61 | struct ths_device *tmdev; | |
62 | struct thermal_zone_device *tzd; | |
63 | int id; | |
64 | }; | |
65 | ||
66 | struct ths_thermal_chip { | |
67 | bool has_mod_clk; | |
68 | bool has_bus_clk_reset; | |
69 | int sensor_num; | |
70 | int offset; | |
71 | int scale; | |
72 | int ft_deviation; | |
73 | int temp_data_base; | |
74 | int (*calibrate)(struct ths_device *tmdev, | |
75 | u16 *caldata, int callen); | |
76 | int (*init)(struct ths_device *tmdev); | |
77 | int (*irq_ack)(struct ths_device *tmdev); | |
78 | int (*calc_temp)(struct ths_device *tmdev, | |
79 | int id, int reg); | |
80 | }; | |
81 | ||
82 | struct ths_device { | |
83 | const struct ths_thermal_chip *chip; | |
84 | struct device *dev; | |
85 | struct regmap *regmap; | |
86 | struct reset_control *reset; | |
87 | struct clk *bus_clk; | |
88 | struct clk *mod_clk; | |
89 | struct tsensor sensor[MAX_SENSOR_NUM]; | |
dccc5c3b YL |
90 | }; |
91 | ||
92 | /* Temp Unit: millidegree Celsius */ | |
93 | static int sun8i_ths_calc_temp(struct ths_device *tmdev, | |
94 | int id, int reg) | |
95 | { | |
96 | return tmdev->chip->offset - (reg * tmdev->chip->scale / 10); | |
97 | } | |
98 | ||
99 | static int sun50i_h5_calc_temp(struct ths_device *tmdev, | |
100 | int id, int reg) | |
101 | { | |
102 | if (reg >= 0x500) | |
103 | return -1191 * reg / 10 + 223000; | |
104 | else if (!id) | |
105 | return -1452 * reg / 10 + 259000; | |
106 | else | |
107 | return -1590 * reg / 10 + 276000; | |
108 | } | |
109 | ||
110 | static int sun8i_ths_get_temp(void *data, int *temp) | |
111 | { | |
112 | struct tsensor *s = data; | |
113 | struct ths_device *tmdev = s->tmdev; | |
114 | int val = 0; | |
115 | ||
116 | regmap_read(tmdev->regmap, tmdev->chip->temp_data_base + | |
117 | 0x4 * s->id, &val); | |
118 | ||
119 | /* ths have no data yet */ | |
120 | if (!val) | |
121 | return -EAGAIN; | |
122 | ||
123 | *temp = tmdev->chip->calc_temp(tmdev, s->id, val); | |
124 | /* | |
125 | * According to the original sdk, there are some platforms(rarely) | |
126 | * that add a fixed offset value after calculating the temperature | |
127 | * value. We can't simply put it on the formula for calculating the | |
128 | * temperature above, because the formula for calculating the | |
129 | * temperature above is also used when the sensor is calibrated. If | |
130 | * do this, the correct calibration formula is hard to know. | |
131 | */ | |
132 | *temp += tmdev->chip->ft_deviation; | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static const struct thermal_zone_of_device_ops ths_ops = { | |
138 | .get_temp = sun8i_ths_get_temp, | |
139 | }; | |
140 | ||
141 | static const struct regmap_config config = { | |
142 | .reg_bits = 32, | |
143 | .val_bits = 32, | |
144 | .reg_stride = 4, | |
145 | .fast_io = true, | |
146 | .max_register = 0xfc, | |
147 | }; | |
148 | ||
149 | static int sun8i_h3_irq_ack(struct ths_device *tmdev) | |
150 | { | |
151 | int i, state, ret = 0; | |
152 | ||
153 | regmap_read(tmdev->regmap, SUN8I_THS_IS, &state); | |
154 | ||
155 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
156 | if (state & SUN8I_THS_DATA_IRQ_STS(i)) { | |
157 | regmap_write(tmdev->regmap, SUN8I_THS_IS, | |
158 | SUN8I_THS_DATA_IRQ_STS(i)); | |
159 | ret |= BIT(i); | |
160 | } | |
161 | } | |
162 | ||
163 | return ret; | |
164 | } | |
165 | ||
166 | static int sun50i_h6_irq_ack(struct ths_device *tmdev) | |
167 | { | |
168 | int i, state, ret = 0; | |
169 | ||
170 | regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state); | |
171 | ||
172 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
173 | if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) { | |
174 | regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS, | |
175 | SUN50I_H6_THS_DATA_IRQ_STS(i)); | |
176 | ret |= BIT(i); | |
177 | } | |
178 | } | |
179 | ||
180 | return ret; | |
181 | } | |
182 | ||
183 | static irqreturn_t sun8i_irq_thread(int irq, void *data) | |
184 | { | |
185 | struct ths_device *tmdev = data; | |
186 | int i, state; | |
187 | ||
188 | state = tmdev->chip->irq_ack(tmdev); | |
189 | ||
190 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
191 | if (state & BIT(i)) | |
192 | thermal_zone_device_update(tmdev->sensor[i].tzd, | |
193 | THERMAL_EVENT_UNSPECIFIED); | |
194 | } | |
195 | ||
196 | return IRQ_HANDLED; | |
197 | } | |
198 | ||
199 | static int sun8i_h3_ths_calibrate(struct ths_device *tmdev, | |
200 | u16 *caldata, int callen) | |
201 | { | |
202 | int i; | |
203 | ||
204 | if (!caldata[0] || callen < 2 * tmdev->chip->sensor_num) | |
205 | return -EINVAL; | |
206 | ||
207 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
208 | int offset = (i % 2) << 4; | |
209 | ||
210 | regmap_update_bits(tmdev->regmap, | |
211 | SUN8I_THS_TEMP_CALIB + (4 * (i >> 1)), | |
212 | 0xfff << offset, | |
213 | caldata[i] << offset); | |
214 | } | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
219 | static int sun50i_h6_ths_calibrate(struct ths_device *tmdev, | |
220 | u16 *caldata, int callen) | |
221 | { | |
222 | struct device *dev = tmdev->dev; | |
223 | int i, ft_temp; | |
224 | ||
225 | if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num) | |
226 | return -EINVAL; | |
227 | ||
228 | /* | |
229 | * efuse layout: | |
230 | * | |
231 | * 0 11 16 32 | |
232 | * +-------+-------+-------+ | |
233 | * |temp| |sensor0|sensor1| | |
234 | * +-------+-------+-------+ | |
235 | * | |
236 | * The calibration data on the H6 is the ambient temperature and | |
237 | * sensor values that are filled during the factory test stage. | |
238 | * | |
239 | * The unit of stored FT temperature is 0.1 degreee celusis. | |
240 | * | |
241 | * We need to calculate a delta between measured and caluclated | |
242 | * register values and this will become a calibration offset. | |
243 | */ | |
244 | ft_temp = (caldata[0] & FT_TEMP_MASK) * 100; | |
dccc5c3b YL |
245 | |
246 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
771151be | 247 | int sensor_reg = caldata[i + 1] & TEMP_CALIB_MASK; |
dccc5c3b YL |
248 | int cdata, offset; |
249 | int sensor_temp = tmdev->chip->calc_temp(tmdev, i, sensor_reg); | |
250 | ||
251 | /* | |
252 | * Calibration data is CALIBRATE_DEFAULT - (calculated | |
253 | * temperature from sensor reading at factory temperature | |
254 | * minus actual factory temperature) * 14.88 (scale from | |
255 | * temperature to register values) | |
256 | */ | |
257 | cdata = CALIBRATE_DEFAULT - | |
258 | ((sensor_temp - ft_temp) * 10 / tmdev->chip->scale); | |
259 | if (cdata & ~TEMP_CALIB_MASK) { | |
260 | /* | |
261 | * Calibration value more than 12-bit, but calibration | |
262 | * register is 12-bit. In this case, ths hardware can | |
263 | * still work without calibration, although the data | |
264 | * won't be so accurate. | |
265 | */ | |
266 | dev_warn(dev, "sensor%d is not calibrated.\n", i); | |
267 | continue; | |
268 | } | |
269 | ||
270 | offset = (i % 2) * 16; | |
271 | regmap_update_bits(tmdev->regmap, | |
272 | SUN50I_H6_THS_TEMP_CALIB + (i / 2 * 4), | |
273 | 0xfff << offset, | |
274 | cdata << offset); | |
275 | } | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static int sun8i_ths_calibrate(struct ths_device *tmdev) | |
281 | { | |
282 | struct nvmem_cell *calcell; | |
283 | struct device *dev = tmdev->dev; | |
284 | u16 *caldata; | |
285 | size_t callen; | |
286 | int ret = 0; | |
287 | ||
288 | calcell = devm_nvmem_cell_get(dev, "calibration"); | |
289 | if (IS_ERR(calcell)) { | |
290 | if (PTR_ERR(calcell) == -EPROBE_DEFER) | |
291 | return -EPROBE_DEFER; | |
292 | /* | |
293 | * Even if the external calibration data stored in sid is | |
294 | * not accessible, the THS hardware can still work, although | |
295 | * the data won't be so accurate. | |
296 | * | |
297 | * The default value of calibration register is 0x800 for | |
298 | * every sensor, and the calibration value is usually 0x7xx | |
299 | * or 0x8xx, so they won't be away from the default value | |
300 | * for a lot. | |
301 | * | |
302 | * So here we do not return error if the calibartion data is | |
303 | * not available, except the probe needs deferring. | |
304 | */ | |
305 | goto out; | |
306 | } | |
307 | ||
308 | caldata = nvmem_cell_read(calcell, &callen); | |
309 | if (IS_ERR(caldata)) { | |
310 | ret = PTR_ERR(caldata); | |
311 | goto out; | |
312 | } | |
313 | ||
314 | tmdev->chip->calibrate(tmdev, caldata, callen); | |
315 | ||
316 | kfree(caldata); | |
317 | out: | |
318 | return ret; | |
319 | } | |
320 | ||
321 | static int sun8i_ths_resource_init(struct ths_device *tmdev) | |
322 | { | |
323 | struct device *dev = tmdev->dev; | |
324 | struct platform_device *pdev = to_platform_device(dev); | |
325 | void __iomem *base; | |
326 | int ret; | |
327 | ||
328 | base = devm_platform_ioremap_resource(pdev, 0); | |
329 | if (IS_ERR(base)) | |
330 | return PTR_ERR(base); | |
331 | ||
332 | tmdev->regmap = devm_regmap_init_mmio(dev, base, &config); | |
333 | if (IS_ERR(tmdev->regmap)) | |
334 | return PTR_ERR(tmdev->regmap); | |
335 | ||
336 | if (tmdev->chip->has_bus_clk_reset) { | |
69d5f3a9 | 337 | tmdev->reset = devm_reset_control_get(dev, NULL); |
dccc5c3b YL |
338 | if (IS_ERR(tmdev->reset)) |
339 | return PTR_ERR(tmdev->reset); | |
340 | ||
341 | tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); | |
342 | if (IS_ERR(tmdev->bus_clk)) | |
343 | return PTR_ERR(tmdev->bus_clk); | |
344 | } | |
345 | ||
346 | if (tmdev->chip->has_mod_clk) { | |
347 | tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); | |
348 | if (IS_ERR(tmdev->mod_clk)) | |
349 | return PTR_ERR(tmdev->mod_clk); | |
350 | } | |
351 | ||
352 | ret = reset_control_deassert(tmdev->reset); | |
353 | if (ret) | |
354 | return ret; | |
355 | ||
356 | ret = clk_prepare_enable(tmdev->bus_clk); | |
357 | if (ret) | |
358 | goto assert_reset; | |
359 | ||
360 | ret = clk_set_rate(tmdev->mod_clk, 24000000); | |
361 | if (ret) | |
362 | goto bus_disable; | |
363 | ||
364 | ret = clk_prepare_enable(tmdev->mod_clk); | |
365 | if (ret) | |
366 | goto bus_disable; | |
367 | ||
368 | ret = sun8i_ths_calibrate(tmdev); | |
369 | if (ret) | |
370 | goto mod_disable; | |
371 | ||
372 | return 0; | |
373 | ||
374 | mod_disable: | |
375 | clk_disable_unprepare(tmdev->mod_clk); | |
376 | bus_disable: | |
377 | clk_disable_unprepare(tmdev->bus_clk); | |
378 | assert_reset: | |
379 | reset_control_assert(tmdev->reset); | |
380 | ||
381 | return ret; | |
382 | } | |
383 | ||
384 | static int sun8i_h3_thermal_init(struct ths_device *tmdev) | |
385 | { | |
386 | int val; | |
387 | ||
388 | /* average over 4 samples */ | |
389 | regmap_write(tmdev->regmap, SUN8I_THS_MFC, | |
390 | SUN50I_THS_FILTER_EN | | |
391 | SUN50I_THS_FILTER_TYPE(1)); | |
392 | /* | |
393 | * clkin = 24MHz | |
394 | * filter_samples = 4 | |
395 | * period = 0.25s | |
396 | * | |
397 | * x = period * clkin / 4096 / filter_samples - 1 | |
398 | * = 365 | |
399 | */ | |
400 | val = GENMASK(7 + tmdev->chip->sensor_num, 8); | |
401 | regmap_write(tmdev->regmap, SUN8I_THS_IC, | |
402 | SUN50I_H6_THS_PC_TEMP_PERIOD(365) | val); | |
403 | /* | |
404 | * T_acq = 20us | |
405 | * clkin = 24MHz | |
406 | * | |
407 | * x = T_acq * clkin - 1 | |
408 | * = 479 | |
409 | */ | |
410 | regmap_write(tmdev->regmap, SUN8I_THS_CTRL0, | |
411 | SUN8I_THS_CTRL0_T_ACQ0(479)); | |
412 | val = GENMASK(tmdev->chip->sensor_num - 1, 0); | |
413 | regmap_write(tmdev->regmap, SUN8I_THS_CTRL2, | |
414 | SUN8I_THS_CTRL2_T_ACQ1(479) | val); | |
415 | ||
416 | return 0; | |
417 | } | |
418 | ||
419 | /* | |
420 | * Without this undocummented value, the returned temperatures would | |
421 | * be higher than real ones by about 20C. | |
422 | */ | |
423 | #define SUN50I_H6_CTRL0_UNK 0x0000002f | |
424 | ||
425 | static int sun50i_h6_thermal_init(struct ths_device *tmdev) | |
426 | { | |
427 | int val; | |
428 | ||
429 | /* | |
430 | * T_acq = 20us | |
431 | * clkin = 24MHz | |
432 | * | |
433 | * x = T_acq * clkin - 1 | |
434 | * = 479 | |
435 | */ | |
436 | regmap_write(tmdev->regmap, SUN50I_THS_CTRL0, | |
437 | SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479)); | |
438 | /* average over 4 samples */ | |
439 | regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC, | |
440 | SUN50I_THS_FILTER_EN | | |
441 | SUN50I_THS_FILTER_TYPE(1)); | |
442 | /* | |
443 | * clkin = 24MHz | |
444 | * filter_samples = 4 | |
445 | * period = 0.25s | |
446 | * | |
447 | * x = period * clkin / 4096 / filter_samples - 1 | |
448 | * = 365 | |
449 | */ | |
450 | regmap_write(tmdev->regmap, SUN50I_H6_THS_PC, | |
451 | SUN50I_H6_THS_PC_TEMP_PERIOD(365)); | |
452 | /* enable sensor */ | |
453 | val = GENMASK(tmdev->chip->sensor_num - 1, 0); | |
454 | regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val); | |
455 | /* thermal data interrupt enable */ | |
456 | val = GENMASK(tmdev->chip->sensor_num - 1, 0); | |
457 | regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | static int sun8i_ths_register(struct ths_device *tmdev) | |
463 | { | |
464 | int i; | |
465 | ||
466 | for (i = 0; i < tmdev->chip->sensor_num; i++) { | |
467 | tmdev->sensor[i].tmdev = tmdev; | |
468 | tmdev->sensor[i].id = i; | |
469 | tmdev->sensor[i].tzd = | |
470 | devm_thermal_zone_of_sensor_register(tmdev->dev, | |
471 | i, | |
472 | &tmdev->sensor[i], | |
473 | &ths_ops); | |
474 | if (IS_ERR(tmdev->sensor[i].tzd)) | |
475 | return PTR_ERR(tmdev->sensor[i].tzd); | |
85f0ad22 YL |
476 | |
477 | if (devm_thermal_add_hwmon_sysfs(tmdev->sensor[i].tzd)) | |
478 | dev_warn(tmdev->dev, | |
479 | "Failed to add hwmon sysfs attributes\n"); | |
dccc5c3b YL |
480 | } |
481 | ||
482 | return 0; | |
483 | } | |
484 | ||
485 | static int sun8i_ths_probe(struct platform_device *pdev) | |
486 | { | |
487 | struct ths_device *tmdev; | |
488 | struct device *dev = &pdev->dev; | |
489 | int ret, irq; | |
490 | ||
491 | tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL); | |
492 | if (!tmdev) | |
493 | return -ENOMEM; | |
494 | ||
495 | tmdev->dev = dev; | |
496 | tmdev->chip = of_device_get_match_data(&pdev->dev); | |
497 | if (!tmdev->chip) | |
498 | return -EINVAL; | |
499 | ||
500 | platform_set_drvdata(pdev, tmdev); | |
501 | ||
502 | ret = sun8i_ths_resource_init(tmdev); | |
503 | if (ret) | |
504 | return ret; | |
505 | ||
506 | irq = platform_get_irq(pdev, 0); | |
507 | if (irq < 0) | |
508 | return irq; | |
509 | ||
510 | ret = tmdev->chip->init(tmdev); | |
511 | if (ret) | |
512 | return ret; | |
513 | ||
514 | ret = sun8i_ths_register(tmdev); | |
515 | if (ret) | |
516 | return ret; | |
517 | ||
518 | /* | |
519 | * Avoid entering the interrupt handler, the thermal device is not | |
520 | * registered yet, we deffer the registration of the interrupt to | |
521 | * the end. | |
522 | */ | |
523 | ret = devm_request_threaded_irq(dev, irq, NULL, | |
524 | sun8i_irq_thread, | |
525 | IRQF_ONESHOT, "ths", tmdev); | |
526 | if (ret) | |
527 | return ret; | |
528 | ||
529 | return 0; | |
530 | } | |
531 | ||
532 | static int sun8i_ths_remove(struct platform_device *pdev) | |
533 | { | |
534 | struct ths_device *tmdev = platform_get_drvdata(pdev); | |
535 | ||
536 | clk_disable_unprepare(tmdev->mod_clk); | |
537 | clk_disable_unprepare(tmdev->bus_clk); | |
538 | reset_control_assert(tmdev->reset); | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
543 | static const struct ths_thermal_chip sun8i_a83t_ths = { | |
544 | .sensor_num = 3, | |
545 | .scale = 705, | |
546 | .offset = 191668, | |
547 | .temp_data_base = SUN8I_THS_TEMP_DATA, | |
548 | .calibrate = sun8i_h3_ths_calibrate, | |
549 | .init = sun8i_h3_thermal_init, | |
550 | .irq_ack = sun8i_h3_irq_ack, | |
551 | .calc_temp = sun8i_ths_calc_temp, | |
552 | }; | |
553 | ||
554 | static const struct ths_thermal_chip sun8i_h3_ths = { | |
555 | .sensor_num = 1, | |
556 | .scale = 1211, | |
557 | .offset = 217000, | |
558 | .has_mod_clk = true, | |
559 | .has_bus_clk_reset = true, | |
560 | .temp_data_base = SUN8I_THS_TEMP_DATA, | |
561 | .calibrate = sun8i_h3_ths_calibrate, | |
562 | .init = sun8i_h3_thermal_init, | |
563 | .irq_ack = sun8i_h3_irq_ack, | |
564 | .calc_temp = sun8i_ths_calc_temp, | |
565 | }; | |
566 | ||
567 | static const struct ths_thermal_chip sun8i_r40_ths = { | |
d8186285 | 568 | .sensor_num = 2, |
dccc5c3b YL |
569 | .offset = 251086, |
570 | .scale = 1130, | |
571 | .has_mod_clk = true, | |
572 | .has_bus_clk_reset = true, | |
573 | .temp_data_base = SUN8I_THS_TEMP_DATA, | |
574 | .calibrate = sun8i_h3_ths_calibrate, | |
575 | .init = sun8i_h3_thermal_init, | |
576 | .irq_ack = sun8i_h3_irq_ack, | |
577 | .calc_temp = sun8i_ths_calc_temp, | |
578 | }; | |
579 | ||
580 | static const struct ths_thermal_chip sun50i_a64_ths = { | |
581 | .sensor_num = 3, | |
582 | .offset = 260890, | |
583 | .scale = 1170, | |
584 | .has_mod_clk = true, | |
585 | .has_bus_clk_reset = true, | |
586 | .temp_data_base = SUN8I_THS_TEMP_DATA, | |
587 | .calibrate = sun8i_h3_ths_calibrate, | |
588 | .init = sun8i_h3_thermal_init, | |
589 | .irq_ack = sun8i_h3_irq_ack, | |
590 | .calc_temp = sun8i_ths_calc_temp, | |
591 | }; | |
592 | ||
593 | static const struct ths_thermal_chip sun50i_h5_ths = { | |
594 | .sensor_num = 2, | |
595 | .has_mod_clk = true, | |
596 | .has_bus_clk_reset = true, | |
597 | .temp_data_base = SUN8I_THS_TEMP_DATA, | |
598 | .calibrate = sun8i_h3_ths_calibrate, | |
599 | .init = sun8i_h3_thermal_init, | |
600 | .irq_ack = sun8i_h3_irq_ack, | |
601 | .calc_temp = sun50i_h5_calc_temp, | |
602 | }; | |
603 | ||
604 | static const struct ths_thermal_chip sun50i_h6_ths = { | |
605 | .sensor_num = 2, | |
606 | .has_bus_clk_reset = true, | |
607 | .ft_deviation = 7000, | |
608 | .offset = 187744, | |
609 | .scale = 672, | |
610 | .temp_data_base = SUN50I_H6_THS_TEMP_DATA, | |
611 | .calibrate = sun50i_h6_ths_calibrate, | |
612 | .init = sun50i_h6_thermal_init, | |
613 | .irq_ack = sun50i_h6_irq_ack, | |
614 | .calc_temp = sun8i_ths_calc_temp, | |
615 | }; | |
616 | ||
617 | static const struct of_device_id of_ths_match[] = { | |
618 | { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths }, | |
619 | { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths }, | |
620 | { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths }, | |
621 | { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths }, | |
622 | { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths }, | |
623 | { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths }, | |
624 | { /* sentinel */ }, | |
625 | }; | |
626 | MODULE_DEVICE_TABLE(of, of_ths_match); | |
627 | ||
628 | static struct platform_driver ths_driver = { | |
629 | .probe = sun8i_ths_probe, | |
630 | .remove = sun8i_ths_remove, | |
631 | .driver = { | |
632 | .name = "sun8i-thermal", | |
633 | .of_match_table = of_ths_match, | |
634 | }, | |
635 | }; | |
636 | module_platform_driver(ths_driver); | |
637 | ||
638 | MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC"); | |
639 | MODULE_LICENSE("GPL v2"); |