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