thermal: armada: add multi-channel sensors support
[linux-2.6-block.git] / drivers / thermal / armada_thermal.c
CommitLineData
fa0d654c 1/*
a9d58a1a 2 * Marvell EBU Armada SoCs thermal sensor driver
fa0d654c
EG
3 *
4 * Copyright (C) 2013 Marvell
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/io.h>
19#include <linux/kernel.h>
20#include <linux/of.h>
21#include <linux/module.h>
22#include <linux/delay.h>
23#include <linux/platform_device.h>
24#include <linux/of_device.h>
25#include <linux/thermal.h>
64163681 26#include <linux/iopoll.h>
3d4e5184
MR
27#include <linux/mfd/syscon.h>
28#include <linux/regmap.h>
fa0d654c 29
fa0d654c
EG
30/* Thermal Manager Control and Status Register */
31#define PMU_TDC0_SW_RST_MASK (0x1 << 1)
32#define PMU_TM_DISABLE_OFFS 0
33#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS)
34#define PMU_TDC0_REF_CAL_CNT_OFFS 11
35#define PMU_TDC0_REF_CAL_CNT_MASK (0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)
36#define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
37#define PMU_TDC0_START_CAL_MASK (0x1 << 25)
38
e2d5f05b
EG
39#define A375_UNIT_CONTROL_SHIFT 27
40#define A375_UNIT_CONTROL_MASK 0x7
41#define A375_READOUT_INVERT BIT(15)
42#define A375_HW_RESETn BIT(8)
43
8c0b888f
MR
44/* Errata fields */
45#define CONTROL0_TSEN_TC_TRIM_MASK 0x7
46#define CONTROL0_TSEN_TC_TRIM_VAL 0x3
47
2ff12799
BS
48#define CONTROL0_TSEN_START BIT(0)
49#define CONTROL0_TSEN_RESET BIT(1)
50#define CONTROL0_TSEN_ENABLE BIT(2)
a9fae794 51#define CONTROL0_TSEN_AVG_BYPASS BIT(6)
f7c2068a
MR
52#define CONTROL0_TSEN_CHAN_SHIFT 13
53#define CONTROL0_TSEN_CHAN_MASK 0xF
a9fae794
MR
54#define CONTROL0_TSEN_OSR_SHIFT 24
55#define CONTROL0_TSEN_OSR_MAX 0x3
f7c2068a
MR
56#define CONTROL0_TSEN_MODE_SHIFT 30
57#define CONTROL0_TSEN_MODE_EXTERNAL 0x2
58#define CONTROL0_TSEN_MODE_MASK 0x3
2ff12799 59
a9fae794
MR
60#define CONTROL1_TSEN_AVG_SHIFT 0
61#define CONTROL1_TSEN_AVG_MASK 0x7
ccf8f522
BS
62#define CONTROL1_EXT_TSEN_SW_RESET BIT(7)
63#define CONTROL1_EXT_TSEN_HW_RESETn BIT(8)
64
64163681
MR
65#define STATUS_POLL_PERIOD_US 1000
66#define STATUS_POLL_TIMEOUT_US 100000
67
66fdb7b6 68struct armada_thermal_data;
fa0d654c
EG
69
70/* Marvell EBU Thermal Sensor Dev Structure */
71struct armada_thermal_priv {
c9899c18 72 struct device *dev;
3d4e5184 73 struct regmap *syscon;
8d98761a 74 char zone_name[THERMAL_NAME_LENGTH];
f7c2068a
MR
75 /* serialize temperature reads/updates */
76 struct mutex update_lock;
66fdb7b6 77 struct armada_thermal_data *data;
f7c2068a 78 int current_channel;
fa0d654c
EG
79};
80
66fdb7b6 81struct armada_thermal_data {
8b4c2712
MR
82 /* Initialize the thermal IC */
83 void (*init)(struct platform_device *pdev,
84 struct armada_thermal_priv *priv);
fa0d654c
EG
85
86 /* Test for a valid sensor value (optional) */
87 bool (*is_valid)(struct armada_thermal_priv *);
9484bc62 88
0cf3a1ac 89 /* Formula coeficients: temp = (b - m * reg) / div */
2ff12799
BS
90 s64 coef_b;
91 s64 coef_m;
92 u32 coef_div;
fd2c94d5 93 bool inverted;
2ff12799 94 bool signed_sample;
1fcacca4
EG
95
96 /* Register shift and mask to access the sensor temperature */
97 unsigned int temp_shift;
98 unsigned int temp_mask;
27d92f27 99 u32 is_valid_bit;
3d4e5184
MR
100
101 /* Syscon access */
102 unsigned int syscon_control0_off;
103 unsigned int syscon_control1_off;
104 unsigned int syscon_status_off;
f7c2068a
MR
105
106 /* One sensor is in the thermal IC, the others are in the CPUs if any */
107 unsigned int cpu_nr;
fa0d654c
EG
108};
109
c9899c18
MR
110struct armada_drvdata {
111 enum drvtype {
112 LEGACY,
113 SYSCON
114 } type;
115 union {
116 struct armada_thermal_priv *priv;
117 struct thermal_zone_device *tz;
118 } data;
119};
120
121/*
122 * struct armada_thermal_sensor - hold the information of one thermal sensor
123 * @thermal: pointer to the local private structure
124 * @tzd: pointer to the thermal zone device
f7c2068a 125 * @id: identifier of the thermal sensor
c9899c18
MR
126 */
127struct armada_thermal_sensor {
128 struct armada_thermal_priv *priv;
f7c2068a 129 int id;
c9899c18
MR
130};
131
8b4c2712
MR
132static void armadaxp_init(struct platform_device *pdev,
133 struct armada_thermal_priv *priv)
fa0d654c 134{
3d4e5184 135 struct armada_thermal_data *data = priv->data;
2f28e4c2 136 u32 reg;
fa0d654c 137
3d4e5184 138 regmap_read(priv->syscon, data->syscon_control1_off, &reg);
fa0d654c 139 reg |= PMU_TDC0_OTF_CAL_MASK;
fa0d654c
EG
140
141 /* Reference calibration value */
142 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
143 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
fa0d654c
EG
144
145 /* Reset the sensor */
931d3c5d 146 reg |= PMU_TDC0_SW_RST_MASK;
fa0d654c 147
3d4e5184 148 regmap_write(priv->syscon, data->syscon_control1_off, reg);
fa0d654c
EG
149
150 /* Enable the sensor */
3d4e5184 151 regmap_read(priv->syscon, data->syscon_status_off, &reg);
fa0d654c 152 reg &= ~PMU_TM_DISABLE_MASK;
3d4e5184 153 regmap_write(priv->syscon, data->syscon_status_off, reg);
fa0d654c
EG
154}
155
8b4c2712
MR
156static void armada370_init(struct platform_device *pdev,
157 struct armada_thermal_priv *priv)
fa0d654c 158{
3d4e5184 159 struct armada_thermal_data *data = priv->data;
2f28e4c2 160 u32 reg;
fa0d654c 161
3d4e5184 162 regmap_read(priv->syscon, data->syscon_control1_off, &reg);
fa0d654c 163 reg |= PMU_TDC0_OTF_CAL_MASK;
fa0d654c
EG
164
165 /* Reference calibration value */
166 reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;
167 reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);
fa0d654c 168
3d4e5184 169 /* Reset the sensor */
fa0d654c 170 reg &= ~PMU_TDC0_START_CAL_MASK;
931d3c5d 171
3d4e5184 172 regmap_write(priv->syscon, data->syscon_control1_off, reg);
fa0d654c 173
7f3be017 174 msleep(10);
fa0d654c
EG
175}
176
8b4c2712
MR
177static void armada375_init(struct platform_device *pdev,
178 struct armada_thermal_priv *priv)
e2d5f05b 179{
3d4e5184 180 struct armada_thermal_data *data = priv->data;
2f28e4c2 181 u32 reg;
e2d5f05b 182
3d4e5184 183 regmap_read(priv->syscon, data->syscon_control1_off, &reg);
e2d5f05b
EG
184 reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT);
185 reg &= ~A375_READOUT_INVERT;
186 reg &= ~A375_HW_RESETn;
3d4e5184 187 regmap_write(priv->syscon, data->syscon_control1_off, reg);
e2d5f05b 188
7f3be017 189 msleep(20);
e2d5f05b
EG
190
191 reg |= A375_HW_RESETn;
3d4e5184
MR
192 regmap_write(priv->syscon, data->syscon_control1_off, reg);
193
7f3be017 194 msleep(50);
e2d5f05b
EG
195}
196
f7c2068a 197static int armada_wait_sensor_validity(struct armada_thermal_priv *priv)
64163681
MR
198{
199 u32 reg;
200
f7c2068a
MR
201 return regmap_read_poll_timeout(priv->syscon,
202 priv->data->syscon_status_off, reg,
203 reg & priv->data->is_valid_bit,
204 STATUS_POLL_PERIOD_US,
205 STATUS_POLL_TIMEOUT_US);
64163681
MR
206}
207
8b4c2712
MR
208static void armada380_init(struct platform_device *pdev,
209 struct armada_thermal_priv *priv)
e6e0a68c 210{
3d4e5184
MR
211 struct armada_thermal_data *data = priv->data;
212 u32 reg;
e6e0a68c 213
ccf8f522 214 /* Disable the HW/SW reset */
3d4e5184 215 regmap_read(priv->syscon, data->syscon_control1_off, &reg);
ccf8f522
BS
216 reg |= CONTROL1_EXT_TSEN_HW_RESETn;
217 reg &= ~CONTROL1_EXT_TSEN_SW_RESET;
3d4e5184 218 regmap_write(priv->syscon, data->syscon_control1_off, reg);
8c0b888f
MR
219
220 /* Set Tsen Tc Trim to correct default value (errata #132698) */
3d4e5184
MR
221 regmap_read(priv->syscon, data->syscon_control0_off, &reg);
222 reg &= ~CONTROL0_TSEN_TC_TRIM_MASK;
223 reg |= CONTROL0_TSEN_TC_TRIM_VAL;
224 regmap_write(priv->syscon, data->syscon_control0_off, reg);
64163681
MR
225
226 /* Wait the sensors to be valid or the core will warn the user */
227 armada_wait_sensor_validity(priv);
e6e0a68c
EG
228}
229
8b4c2712
MR
230static void armada_ap806_init(struct platform_device *pdev,
231 struct armada_thermal_priv *priv)
2ff12799 232{
3d4e5184 233 struct armada_thermal_data *data = priv->data;
2ff12799
BS
234 u32 reg;
235
3d4e5184 236 regmap_read(priv->syscon, data->syscon_control0_off, &reg);
2ff12799
BS
237 reg &= ~CONTROL0_TSEN_RESET;
238 reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_ENABLE;
a9fae794
MR
239
240 /* Sample every ~2ms */
241 reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;
242
243 /* Enable average (2 samples by default) */
244 reg &= ~CONTROL0_TSEN_AVG_BYPASS;
245
3d4e5184 246 regmap_write(priv->syscon, data->syscon_control0_off, reg);
64163681
MR
247
248 /* Wait the sensors to be valid or the core will warn the user */
249 armada_wait_sensor_validity(priv);
2ff12799
BS
250}
251
5b5e17a1
MR
252static void armada_cp110_init(struct platform_device *pdev,
253 struct armada_thermal_priv *priv)
254{
3d4e5184 255 struct armada_thermal_data *data = priv->data;
a9fae794
MR
256 u32 reg;
257
5b5e17a1 258 armada380_init(pdev, priv);
a9fae794
MR
259
260 /* Sample every ~2ms */
3d4e5184 261 regmap_read(priv->syscon, data->syscon_control0_off, &reg);
a9fae794 262 reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;
3d4e5184 263 regmap_write(priv->syscon, data->syscon_control0_off, reg);
a9fae794
MR
264
265 /* Average the output value over 2^1 = 2 samples */
3d4e5184 266 regmap_read(priv->syscon, data->syscon_control1_off, &reg);
a9fae794
MR
267 reg &= ~CONTROL1_TSEN_AVG_MASK << CONTROL1_TSEN_AVG_SHIFT;
268 reg |= 1 << CONTROL1_TSEN_AVG_SHIFT;
3d4e5184 269 regmap_write(priv->syscon, data->syscon_control1_off, reg);
5b5e17a1
MR
270}
271
fa0d654c
EG
272static bool armada_is_valid(struct armada_thermal_priv *priv)
273{
3d4e5184
MR
274 u32 reg;
275
276 regmap_read(priv->syscon, priv->data->syscon_status_off, &reg);
fa0d654c 277
27d92f27 278 return reg & priv->data->is_valid_bit;
fa0d654c
EG
279}
280
f7c2068a
MR
281/* There is currently no board with more than one sensor per channel */
282static int armada_select_channel(struct armada_thermal_priv *priv, int channel)
283{
284 struct armada_thermal_data *data = priv->data;
285 u32 ctrl0;
286
287 if (channel < 0 || channel > priv->data->cpu_nr)
288 return -EINVAL;
289
290 if (priv->current_channel == channel)
291 return 0;
292
293 /* Stop the measurements */
294 regmap_read(priv->syscon, data->syscon_control0_off, &ctrl0);
295 ctrl0 &= ~CONTROL0_TSEN_START;
296 regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);
297
298 /* Reset the mode, internal sensor will be automatically selected */
299 ctrl0 &= ~(CONTROL0_TSEN_MODE_MASK << CONTROL0_TSEN_MODE_SHIFT);
300
301 /* Other channels are external and should be selected accordingly */
302 if (channel) {
303 /* Change the mode to external */
304 ctrl0 |= CONTROL0_TSEN_MODE_EXTERNAL <<
305 CONTROL0_TSEN_MODE_SHIFT;
306 /* Select the sensor */
307 ctrl0 &= ~(CONTROL0_TSEN_CHAN_MASK << CONTROL0_TSEN_CHAN_SHIFT);
308 ctrl0 |= (channel - 1) << CONTROL0_TSEN_CHAN_SHIFT;
309 }
310
311 /* Actually set the mode/channel */
312 regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);
313 priv->current_channel = channel;
314
315 /* Re-start the measurements */
316 ctrl0 |= CONTROL0_TSEN_START;
317 regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);
318
319 /*
320 * The IP has a latency of ~15ms, so after updating the selected source,
321 * we must absolutely wait for the sensor validity bit to ensure we read
322 * actual data.
323 */
324 if (armada_wait_sensor_validity(priv)) {
325 dev_err(priv->dev,
326 "Temperature sensor reading not valid\n");
327 return -EIO;
328 }
329
330 return 0;
331}
332
c9899c18 333static int armada_read_sensor(struct armada_thermal_priv *priv, int *temp)
fa0d654c 334{
2ff12799
BS
335 u32 reg, div;
336 s64 sample, b, m;
fa0d654c
EG
337
338 /* Valid check */
66fdb7b6 339 if (priv->data->is_valid && !priv->data->is_valid(priv)) {
c9899c18 340 dev_err(priv->dev,
fa0d654c
EG
341 "Temperature sensor reading not valid\n");
342 return -EIO;
343 }
344
3d4e5184 345 regmap_read(priv->syscon, priv->data->syscon_status_off, &reg);
1fcacca4 346 reg = (reg >> priv->data->temp_shift) & priv->data->temp_mask;
2ff12799
BS
347 if (priv->data->signed_sample)
348 /* The most significant bit is the sign bit */
349 sample = sign_extend32(reg, fls(priv->data->temp_mask) - 1);
350 else
351 sample = reg;
9484bc62
EG
352
353 /* Get formula coeficients */
354 b = priv->data->coef_b;
355 m = priv->data->coef_m;
356 div = priv->data->coef_div;
357
fd2c94d5 358 if (priv->data->inverted)
2ff12799 359 *temp = div_s64((m * sample) - b, div);
fd2c94d5 360 else
2ff12799
BS
361 *temp = div_s64(b - (m * sample), div);
362
fa0d654c
EG
363 return 0;
364}
365
c9899c18
MR
366static int armada_get_temp_legacy(struct thermal_zone_device *thermal,
367 int *temp)
368{
369 struct armada_thermal_priv *priv = thermal->devdata;
370 int ret;
371
372 /* Do the actual reading */
373 ret = armada_read_sensor(priv, temp);
374
375 return ret;
376}
377
378static struct thermal_zone_device_ops legacy_ops = {
379 .get_temp = armada_get_temp_legacy,
380};
381
382static int armada_get_temp(void *_sensor, int *temp)
383{
384 struct armada_thermal_sensor *sensor = _sensor;
385 struct armada_thermal_priv *priv = sensor->priv;
f7c2068a
MR
386 int ret;
387
388 mutex_lock(&priv->update_lock);
389
390 /* Select the desired channel */
391 ret = armada_select_channel(priv, sensor->id);
392 if (ret)
393 goto unlock_mutex;
c9899c18
MR
394
395 /* Do the actual reading */
f7c2068a
MR
396 ret = armada_read_sensor(priv, temp);
397
398unlock_mutex:
399 mutex_unlock(&priv->update_lock);
400
401 return ret;
c9899c18
MR
402}
403
404static struct thermal_zone_of_device_ops of_ops = {
fa0d654c
EG
405 .get_temp = armada_get_temp,
406};
407
66fdb7b6 408static const struct armada_thermal_data armadaxp_data = {
8b4c2712 409 .init = armadaxp_init,
1fcacca4
EG
410 .temp_shift = 10,
411 .temp_mask = 0x1ff,
2ff12799
BS
412 .coef_b = 3153000000ULL,
413 .coef_m = 10000000ULL,
9484bc62 414 .coef_div = 13825,
3d4e5184
MR
415 .syscon_status_off = 0xb0,
416 .syscon_control1_off = 0xd0,
fa0d654c
EG
417};
418
66fdb7b6 419static const struct armada_thermal_data armada370_data = {
fa0d654c 420 .is_valid = armada_is_valid,
8b4c2712 421 .init = armada370_init,
27d92f27 422 .is_valid_bit = BIT(9),
1fcacca4
EG
423 .temp_shift = 10,
424 .temp_mask = 0x1ff,
2ff12799
BS
425 .coef_b = 3153000000ULL,
426 .coef_m = 10000000ULL,
9484bc62 427 .coef_div = 13825,
3d4e5184
MR
428 .syscon_status_off = 0x0,
429 .syscon_control1_off = 0x4,
fa0d654c
EG
430};
431
e2d5f05b
EG
432static const struct armada_thermal_data armada375_data = {
433 .is_valid = armada_is_valid,
8b4c2712 434 .init = armada375_init,
27d92f27 435 .is_valid_bit = BIT(10),
e2d5f05b
EG
436 .temp_shift = 0,
437 .temp_mask = 0x1ff,
2ff12799
BS
438 .coef_b = 3171900000ULL,
439 .coef_m = 10000000ULL,
e2d5f05b 440 .coef_div = 13616,
3d4e5184
MR
441 .syscon_status_off = 0x78,
442 .syscon_control0_off = 0x7c,
443 .syscon_control1_off = 0x80,
e2d5f05b
EG
444};
445
e6e0a68c
EG
446static const struct armada_thermal_data armada380_data = {
447 .is_valid = armada_is_valid,
8b4c2712 448 .init = armada380_init,
27d92f27 449 .is_valid_bit = BIT(10),
e6e0a68c
EG
450 .temp_shift = 0,
451 .temp_mask = 0x3ff,
2ff12799
BS
452 .coef_b = 1172499100ULL,
453 .coef_m = 2000096ULL,
b56100db 454 .coef_div = 4201,
e6e0a68c 455 .inverted = true,
3d4e5184
MR
456 .syscon_control0_off = 0x70,
457 .syscon_control1_off = 0x74,
458 .syscon_status_off = 0x78,
e6e0a68c
EG
459};
460
2ff12799
BS
461static const struct armada_thermal_data armada_ap806_data = {
462 .is_valid = armada_is_valid,
8b4c2712 463 .init = armada_ap806_init,
2ff12799
BS
464 .is_valid_bit = BIT(16),
465 .temp_shift = 0,
466 .temp_mask = 0x3ff,
467 .coef_b = -150000LL,
468 .coef_m = 423ULL,
469 .coef_div = 1,
470 .inverted = true,
471 .signed_sample = true,
3d4e5184
MR
472 .syscon_control0_off = 0x84,
473 .syscon_control1_off = 0x88,
474 .syscon_status_off = 0x8C,
f7c2068a 475 .cpu_nr = 4,
2ff12799
BS
476};
477
ccf8f522
BS
478static const struct armada_thermal_data armada_cp110_data = {
479 .is_valid = armada_is_valid,
5b5e17a1 480 .init = armada_cp110_init,
ccf8f522
BS
481 .is_valid_bit = BIT(10),
482 .temp_shift = 0,
483 .temp_mask = 0x3ff,
484 .coef_b = 1172499100ULL,
485 .coef_m = 2000096ULL,
486 .coef_div = 4201,
487 .inverted = true,
3d4e5184
MR
488 .syscon_control0_off = 0x70,
489 .syscon_control1_off = 0x74,
490 .syscon_status_off = 0x78,
ccf8f522
BS
491};
492
fa0d654c
EG
493static const struct of_device_id armada_thermal_id_table[] = {
494 {
495 .compatible = "marvell,armadaxp-thermal",
66fdb7b6 496 .data = &armadaxp_data,
fa0d654c
EG
497 },
498 {
499 .compatible = "marvell,armada370-thermal",
66fdb7b6 500 .data = &armada370_data,
fa0d654c 501 },
e2d5f05b
EG
502 {
503 .compatible = "marvell,armada375-thermal",
504 .data = &armada375_data,
505 },
e6e0a68c
EG
506 {
507 .compatible = "marvell,armada380-thermal",
508 .data = &armada380_data,
509 },
2ff12799
BS
510 {
511 .compatible = "marvell,armada-ap806-thermal",
512 .data = &armada_ap806_data,
513 },
ccf8f522
BS
514 {
515 .compatible = "marvell,armada-cp110-thermal",
516 .data = &armada_cp110_data,
517 },
fa0d654c
EG
518 {
519 /* sentinel */
520 },
521};
522MODULE_DEVICE_TABLE(of, armada_thermal_id_table);
523
3d4e5184
MR
524static const struct regmap_config armada_thermal_regmap_config = {
525 .reg_bits = 32,
526 .reg_stride = 4,
527 .val_bits = 32,
528 .fast_io = true,
529};
530
531static int armada_thermal_probe_legacy(struct platform_device *pdev,
532 struct armada_thermal_priv *priv)
533{
534 struct armada_thermal_data *data = priv->data;
535 struct resource *res;
536 void __iomem *base;
537
538 /* First memory region points towards the status register */
539 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
540 if (IS_ERR(res))
541 return PTR_ERR(res);
542
543 /*
544 * Edit the resource start address and length to map over all the
545 * registers, instead of pointing at them one by one.
546 */
547 res->start -= data->syscon_status_off;
548 res->end = res->start + max(data->syscon_status_off,
549 max(data->syscon_control0_off,
550 data->syscon_control1_off)) +
551 sizeof(unsigned int) - 1;
552
553 base = devm_ioremap_resource(&pdev->dev, res);
554 if (IS_ERR(base))
555 return PTR_ERR(base);
556
557 priv->syscon = devm_regmap_init_mmio(&pdev->dev, base,
558 &armada_thermal_regmap_config);
559 if (IS_ERR(priv->syscon))
560 return PTR_ERR(priv->syscon);
561
562 return 0;
563}
564
565static int armada_thermal_probe_syscon(struct platform_device *pdev,
566 struct armada_thermal_priv *priv)
567{
568 priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node);
569 if (IS_ERR(priv->syscon))
570 return PTR_ERR(priv->syscon);
571
572 return 0;
573}
574
8d98761a
MR
575static void armada_set_sane_name(struct platform_device *pdev,
576 struct armada_thermal_priv *priv)
577{
578 const char *name = dev_name(&pdev->dev);
579 char *insane_char;
580
581 if (strlen(name) > THERMAL_NAME_LENGTH) {
582 /*
583 * When inside a system controller, the device name has the
584 * form: f06f8000.system-controller:ap-thermal so stripping
585 * after the ':' should give us a shorter but meaningful name.
586 */
587 name = strrchr(name, ':');
588 if (!name)
589 name = "armada_thermal";
590 else
591 name++;
592 }
593
594 /* Save the name locally */
595 strncpy(priv->zone_name, name, THERMAL_NAME_LENGTH - 1);
596 priv->zone_name[THERMAL_NAME_LENGTH - 1] = '\0';
597
598 /* Then check there are no '-' or hwmon core will complain */
599 do {
600 insane_char = strpbrk(priv->zone_name, "-");
601 if (insane_char)
602 *insane_char = '_';
603 } while (insane_char);
604}
605
fa0d654c
EG
606static int armada_thermal_probe(struct platform_device *pdev)
607{
c9899c18 608 struct thermal_zone_device *tz;
f7c2068a 609 struct armada_thermal_sensor *sensor;
c9899c18 610 struct armada_drvdata *drvdata;
fa0d654c
EG
611 const struct of_device_id *match;
612 struct armada_thermal_priv *priv;
f7c2068a 613 int sensor_id;
3d4e5184 614 int ret;
fa0d654c
EG
615
616 match = of_match_device(armada_thermal_id_table, &pdev->dev);
617 if (!match)
618 return -ENODEV;
619
620 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
621 if (!priv)
622 return -ENOMEM;
623
c9899c18
MR
624 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
625 if (!priv)
626 return -ENOMEM;
2f28e4c2 627
c9899c18
MR
628 priv->dev = &pdev->dev;
629 priv->data = (struct armada_thermal_data *)match->data;
8d98761a 630
f7c2068a
MR
631 mutex_init(&priv->update_lock);
632
2f28e4c2
MR
633 /*
634 * Legacy DT bindings only described "control1" register (also referred
3d4e5184 635 * as "control MSB" on old documentation). Then, bindings moved to cover
2f28e4c2 636 * "control0/control LSB" and "control1/control MSB" registers within
3d4e5184
MR
637 * the same resource, which was then of size 8 instead of 4.
638 *
639 * The logic of defining sporadic registers is broken. For instance, it
640 * blocked the addition of the overheat interrupt feature that needed
641 * another resource somewhere else in the same memory area. One solution
642 * is to define an overall system controller and put the thermal node
643 * into it, which requires the use of regmaps across all the driver.
2f28e4c2 644 */
c9899c18
MR
645 if (IS_ERR(syscon_node_to_regmap(pdev->dev.parent->of_node))) {
646 /* Ensure device name is correct for the thermal core */
647 armada_set_sane_name(pdev, priv);
648
3d4e5184 649 ret = armada_thermal_probe_legacy(pdev, priv);
c9899c18
MR
650 if (ret)
651 return ret;
3d4e5184 652
c9899c18
MR
653 priv->data->init(pdev, priv);
654
655 tz = thermal_zone_device_register(priv->zone_name, 0, 0, priv,
656 &legacy_ops, NULL, 0, 0);
657 if (IS_ERR(tz)) {
658 dev_err(&pdev->dev,
659 "Failed to register thermal zone device\n");
660 return PTR_ERR(tz);
661 }
662
663 drvdata->type = LEGACY;
664 drvdata->data.tz = tz;
665 platform_set_drvdata(pdev, drvdata);
666
667 return 0;
668 }
669
670 ret = armada_thermal_probe_syscon(pdev, priv);
3d4e5184
MR
671 if (ret)
672 return ret;
2f28e4c2 673
f7c2068a 674 priv->current_channel = -1;
8b4c2712 675 priv->data->init(pdev, priv);
c9899c18
MR
676 drvdata->type = SYSCON;
677 drvdata->data.priv = priv;
678 platform_set_drvdata(pdev, drvdata);
fa0d654c 679
f7c2068a
MR
680 /*
681 * There is one channel for the IC and one per CPU (if any), each
682 * channel has one sensor.
683 */
684 for (sensor_id = 0; sensor_id <= priv->data->cpu_nr; sensor_id++) {
685 sensor = devm_kzalloc(&pdev->dev,
686 sizeof(struct armada_thermal_sensor),
687 GFP_KERNEL);
688 if (!sensor)
689 return -ENOMEM;
690
691 /* Register the sensor */
692 sensor->priv = priv;
693 sensor->id = sensor_id;
694 tz = devm_thermal_zone_of_sensor_register(&pdev->dev,
695 sensor->id, sensor,
696 &of_ops);
697 if (IS_ERR(tz)) {
698 dev_info(&pdev->dev, "Thermal sensor %d unavailable\n",
699 sensor_id);
700 devm_kfree(&pdev->dev, sensor);
701 continue;
702 }
fa0d654c
EG
703 }
704
fa0d654c
EG
705 return 0;
706}
707
708static int armada_thermal_exit(struct platform_device *pdev)
709{
c9899c18 710 struct armada_drvdata *drvdata = platform_get_drvdata(pdev);
fa0d654c 711
c9899c18
MR
712 if (drvdata->type == LEGACY)
713 thermal_zone_device_unregister(drvdata->data.tz);
fa0d654c
EG
714
715 return 0;
716}
717
718static struct platform_driver armada_thermal_driver = {
719 .probe = armada_thermal_probe,
720 .remove = armada_thermal_exit,
721 .driver = {
722 .name = "armada_thermal",
1d089e09 723 .of_match_table = armada_thermal_id_table,
fa0d654c
EG
724 },
725};
726
727module_platform_driver(armada_thermal_driver);
728
729MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");
a9d58a1a 730MODULE_DESCRIPTION("Marvell EBU Armada SoCs thermal driver");
fa0d654c 731MODULE_LICENSE("GPL v2");