thermal/drivers/hisi: Prepare to add support for other hisi platforms
[linux-block.git] / drivers / thermal / hisi_thermal.c
CommitLineData
9a5238a9 1/*
2 * Hisilicon thermal sensor driver
3 *
4 * Copyright (c) 2014-2015 Hisilicon Limited.
5 * Copyright (c) 2014-2015 Linaro Limited.
6 *
7 * Xinwei Kong <kong.kongxinwei@hisilicon.com>
8 * Leo Yan <leo.yan@linaro.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
15 * kind, whether express or implied; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/cpufreq.h>
21#include <linux/delay.h>
22#include <linux/interrupt.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/io.h>
a160a465 26#include <linux/of_device.h>
9a5238a9 27
28#include "thermal_core.h"
29
5ed82b79
KW
30#define HI6220_TEMP0_LAG (0x0)
31#define HI6220_TEMP0_TH (0x4)
32#define HI6220_TEMP0_RST_TH (0x8)
33#define HI6220_TEMP0_CFG (0xC)
a160a465 34#define HI6220_TEMP0_CFG_SS_MSK (0xF000)
5ed82b79
KW
35#define HI6220_TEMP0_CFG_HDAK_MSK (0x30)
36#define HI6220_TEMP0_EN (0x10)
37#define HI6220_TEMP0_INT_EN (0x14)
38#define HI6220_TEMP0_INT_CLR (0x18)
39#define HI6220_TEMP0_RST_MSK (0x1C)
40#define HI6220_TEMP0_VALUE (0x28)
41
42#define HI6220_TEMP_BASE (-60000)
43#define HI6220_TEMP_RESET (100000)
44#define HI6220_TEMP_STEP (785)
a160a465 45#define HI6220_TEMP_LAG (3500)
5ed82b79
KW
46
47#define HI6220_DEFAULT_SENSOR 2
9a5238a9 48
49struct hisi_thermal_sensor {
9a5238a9 50 struct thermal_zone_device *tzd;
9a5238a9 51 uint32_t id;
52 uint32_t thres_temp;
53};
54
55struct hisi_thermal_data {
a160a465
KW
56 int (*get_temp)(struct hisi_thermal_data *data);
57 int (*enable_sensor)(struct hisi_thermal_data *data);
58 int (*disable_sensor)(struct hisi_thermal_data *data);
59 int (*irq_handler)(struct hisi_thermal_data *data);
9a5238a9 60 struct platform_device *pdev;
61 struct clk *clk;
609f26dc 62 struct hisi_thermal_sensor sensor;
9a5238a9 63 void __iomem *regs;
609f26dc 64 int irq;
9a5238a9 65};
66
48880b97
DL
67/*
68 * The temperature computation on the tsensor is as follow:
69 * Unit: millidegree Celsius
e42bbe11 70 * Step: 200/255 (0.7843)
48880b97
DL
71 * Temperature base: -60°C
72 *
e42bbe11 73 * The register is programmed in temperature steps, every step is 785
48880b97
DL
74 * millidegree and begins at -60 000 m°C
75 *
76 * The temperature from the steps:
77 *
e42bbe11 78 * Temp = TempBase + (steps x 785)
48880b97
DL
79 *
80 * and the steps from the temperature:
81 *
e42bbe11 82 * steps = (Temp - TempBase) / 785
48880b97
DL
83 *
84 */
5ed82b79 85static inline int hi6220_thermal_step_to_temp(int step)
9a5238a9 86{
5ed82b79 87 return HI6220_TEMP_BASE + (step * HI6220_TEMP_STEP);
9a5238a9 88}
89
5ed82b79 90static inline int hi6220_thermal_temp_to_step(int temp)
9a5238a9 91{
5ed82b79 92 return DIV_ROUND_UP(temp - HI6220_TEMP_BASE, HI6220_TEMP_STEP);
db2b0332
DL
93}
94
10d7e9a9
DL
95/*
96 * The lag register contains 5 bits encoding the temperature in steps.
97 *
98 * Each time the temperature crosses the threshold boundary, an
99 * interrupt is raised. It could be when the temperature is going
100 * above the threshold or below. However, if the temperature is
101 * fluctuating around this value due to the load, we can receive
102 * several interrupts which may not desired.
103 *
104 * We can setup a temperature representing the delta between the
105 * threshold and the current temperature when the temperature is
106 * decreasing.
107 *
108 * For instance: the lag register is 5°C, the threshold is 65°C, when
109 * the temperature reaches 65°C an interrupt is raised and when the
110 * temperature decrease to 65°C - 5°C another interrupt is raised.
111 *
112 * A very short lag can lead to an interrupt storm, a long lag
113 * increase the latency to react to the temperature changes. In our
114 * case, that is not really a problem as we are polling the
115 * temperature.
116 *
117 * [0:4] : lag register
118 *
5ed82b79 119 * The temperature is coded in steps, cf. HI6220_TEMP_STEP.
10d7e9a9
DL
120 *
121 * Min : 0x00 : 0.0 °C
122 * Max : 0x1F : 24.3 °C
123 *
124 * The 'value' parameter is in milliCelsius.
125 */
5ed82b79 126static inline void hi6220_thermal_set_lag(void __iomem *addr, int value)
1e11b014 127{
5ed82b79
KW
128 writel(DIV_ROUND_UP(value, HI6220_TEMP_STEP) & 0x1F,
129 addr + HI6220_TEMP0_LAG);
1e11b014
DL
130}
131
5ed82b79 132static inline void hi6220_thermal_alarm_clear(void __iomem *addr, int value)
1e11b014 133{
5ed82b79 134 writel(value, addr + HI6220_TEMP0_INT_CLR);
1e11b014
DL
135}
136
5ed82b79 137static inline void hi6220_thermal_alarm_enable(void __iomem *addr, int value)
1e11b014 138{
5ed82b79 139 writel(value, addr + HI6220_TEMP0_INT_EN);
1e11b014
DL
140}
141
5ed82b79 142static inline void hi6220_thermal_alarm_set(void __iomem *addr, int temp)
1e11b014 143{
5ed82b79
KW
144 writel(hi6220_thermal_temp_to_step(temp) | 0x0FFFFFF00,
145 addr + HI6220_TEMP0_TH);
1e11b014
DL
146}
147
5ed82b79 148static inline void hi6220_thermal_reset_set(void __iomem *addr, int temp)
1e11b014 149{
5ed82b79 150 writel(hi6220_thermal_temp_to_step(temp), addr + HI6220_TEMP0_RST_TH);
1e11b014
DL
151}
152
5ed82b79 153static inline void hi6220_thermal_reset_enable(void __iomem *addr, int value)
1e11b014 154{
5ed82b79 155 writel(value, addr + HI6220_TEMP0_RST_MSK);
1e11b014
DL
156}
157
5ed82b79 158static inline void hi6220_thermal_enable(void __iomem *addr, int value)
1e11b014 159{
5ed82b79 160 writel(value, addr + HI6220_TEMP0_EN);
1e11b014
DL
161}
162
5ed82b79 163static inline int hi6220_thermal_get_temperature(void __iomem *addr)
1e11b014 164{
5ed82b79 165 return hi6220_thermal_step_to_temp(readl(addr + HI6220_TEMP0_VALUE));
1e11b014
DL
166}
167
b424315a
DL
168/*
169 * Temperature configuration register - Sensor selection
170 *
171 * Bits [19:12]
172 *
173 * 0x0: local sensor (default)
174 * 0x1: remote sensor 1 (ACPU cluster 1)
175 * 0x2: remote sensor 2 (ACPU cluster 0)
176 * 0x3: remote sensor 3 (G3D)
177 */
5ed82b79 178static inline void hi6220_thermal_sensor_select(void __iomem *addr, int sensor)
1e11b014 179{
5ed82b79
KW
180 writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_SS_MSK) |
181 (sensor << 12), addr + HI6220_TEMP0_CFG);
1e11b014
DL
182}
183
b424315a
DL
184/*
185 * Temperature configuration register - Hdak conversion polling interval
186 *
187 * Bits [5:4]
188 *
189 * 0x0 : 0.768 ms
190 * 0x1 : 6.144 ms
191 * 0x2 : 49.152 ms
192 * 0x3 : 393.216 ms
193 */
5ed82b79 194static inline void hi6220_thermal_hdak_set(void __iomem *addr, int value)
1e11b014 195{
5ed82b79
KW
196 writel((readl(addr + HI6220_TEMP0_CFG) & ~HI6220_TEMP0_CFG_HDAK_MSK) |
197 (value << 4), addr + HI6220_TEMP0_CFG);
1e11b014
DL
198}
199
a160a465
KW
200static int hi6220_thermal_irq_handler(struct hisi_thermal_data *data)
201{
202 hi6220_thermal_alarm_clear(data->regs, 1);
203 return 0;
204}
205
206static int hi6220_thermal_get_temp(struct hisi_thermal_data *data)
207{
208 return hi6220_thermal_get_temperature(data->regs);
209}
210
211static int hi6220_thermal_disable_sensor(struct hisi_thermal_data *data)
9a5238a9 212{
9a5238a9 213 /* disable sensor module */
5ed82b79
KW
214 hi6220_thermal_enable(data->regs, 0);
215 hi6220_thermal_alarm_enable(data->regs, 0);
216 hi6220_thermal_reset_enable(data->regs, 0);
943c0f6a
KW
217
218 clk_disable_unprepare(data->clk);
a160a465
KW
219
220 return 0;
9a5238a9 221}
222
5ed82b79 223static int hi6220_thermal_enable_sensor(struct hisi_thermal_data *data)
a0678da8
KW
224{
225 struct hisi_thermal_sensor *sensor = &data->sensor;
226 int ret;
227
228 /* enable clock for tsensor */
229 ret = clk_prepare_enable(data->clk);
230 if (ret)
231 return ret;
232
233 /* disable module firstly */
5ed82b79
KW
234 hi6220_thermal_reset_enable(data->regs, 0);
235 hi6220_thermal_enable(data->regs, 0);
a0678da8
KW
236
237 /* select sensor id */
5ed82b79 238 hi6220_thermal_sensor_select(data->regs, sensor->id);
a0678da8
KW
239
240 /* setting the hdak time */
5ed82b79 241 hi6220_thermal_hdak_set(data->regs, 0);
a0678da8
KW
242
243 /* setting lag value between current temp and the threshold */
5ed82b79 244 hi6220_thermal_set_lag(data->regs, HI6220_TEMP_LAG);
a0678da8
KW
245
246 /* enable for interrupt */
5ed82b79 247 hi6220_thermal_alarm_set(data->regs, sensor->thres_temp);
a0678da8 248
5ed82b79 249 hi6220_thermal_reset_set(data->regs, HI6220_TEMP_RESET);
a0678da8
KW
250
251 /* enable module */
5ed82b79
KW
252 hi6220_thermal_reset_enable(data->regs, 1);
253 hi6220_thermal_enable(data->regs, 1);
a0678da8 254
5ed82b79
KW
255 hi6220_thermal_alarm_clear(data->regs, 0);
256 hi6220_thermal_alarm_enable(data->regs, 1);
a0678da8
KW
257
258 return 0;
259}
260
a160a465
KW
261static int hi6220_thermal_probe(struct hisi_thermal_data *data)
262{
263 struct platform_device *pdev = data->pdev;
264 struct device *dev = &pdev->dev;
265 struct resource *res;
266 int ret;
267
268 data->get_temp = hi6220_thermal_get_temp;
269 data->enable_sensor = hi6220_thermal_enable_sensor;
270 data->disable_sensor = hi6220_thermal_disable_sensor;
271 data->irq_handler = hi6220_thermal_irq_handler;
272
273 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
274 data->regs = devm_ioremap_resource(dev, res);
275 if (IS_ERR(data->regs)) {
276 dev_err(dev, "failed to get io address\n");
277 return PTR_ERR(data->regs);
278 }
279
280 data->clk = devm_clk_get(dev, "thermal_clk");
281 if (IS_ERR(data->clk)) {
282 ret = PTR_ERR(data->clk);
283 if (ret != -EPROBE_DEFER)
284 dev_err(dev, "failed to get thermal clk: %d\n", ret);
285 return ret;
286 }
287
288 data->irq = platform_get_irq(pdev, 0);
289 if (data->irq < 0)
290 return data->irq;
291
292 data->sensor.id = HI6220_DEFAULT_SENSOR;
293
294 return 0;
295}
296
81d7cb79 297static int hisi_thermal_get_temp(void *__data, int *temp)
9a5238a9 298{
81d7cb79
DL
299 struct hisi_thermal_data *data = __data;
300 struct hisi_thermal_sensor *sensor = &data->sensor;
9a5238a9 301
a160a465 302 *temp = data->get_temp(data);
9a5238a9 303
10d7e9a9
DL
304 dev_dbg(&data->pdev->dev, "id=%d, temp=%d, thres=%d\n",
305 sensor->id, *temp, sensor->thres_temp);
9a5238a9 306
307 return 0;
308}
309
3fe156f1 310static const struct thermal_zone_of_device_ops hisi_of_thermal_ops = {
9a5238a9 311 .get_temp = hisi_thermal_get_temp,
312};
313
10d7e9a9 314static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
9a5238a9 315{
316 struct hisi_thermal_data *data = dev;
609f26dc 317 struct hisi_thermal_sensor *sensor = &data->sensor;
5ed82b79 318 int temp = 0;
9a5238a9 319
a160a465 320 data->irq_handler(data);
9a5238a9 321
5ed82b79 322 hisi_thermal_get_temp(data, &temp);
9a5238a9 323
10d7e9a9
DL
324 if (temp >= sensor->thres_temp) {
325 dev_crit(&data->pdev->dev, "THERMAL ALARM: %d > %d\n",
326 temp, sensor->thres_temp);
9a5238a9 327
609f26dc 328 thermal_zone_device_update(data->sensor.tzd,
10d7e9a9 329 THERMAL_EVENT_UNSPECIFIED);
9a5238a9 330
5ed82b79 331 } else {
10d7e9a9
DL
332 dev_crit(&data->pdev->dev, "THERMAL ALARM stopped: %d < %d\n",
333 temp, sensor->thres_temp);
334 }
9a5238a9 335
336 return IRQ_HANDLED;
337}
338
339static int hisi_thermal_register_sensor(struct platform_device *pdev,
340 struct hisi_thermal_data *data,
a160a465 341 struct hisi_thermal_sensor *sensor)
9a5238a9 342{
343 int ret, i;
344 const struct thermal_trip *trip;
345
44a520d8 346 sensor->tzd = devm_thermal_zone_of_sensor_register(&pdev->dev,
81d7cb79
DL
347 sensor->id, data,
348 &hisi_of_thermal_ops);
9a5238a9 349 if (IS_ERR(sensor->tzd)) {
350 ret = PTR_ERR(sensor->tzd);
439dc968 351 sensor->tzd = NULL;
9a5238a9 352 dev_err(&pdev->dev, "failed to register sensor id %d: %d\n",
353 sensor->id, ret);
354 return ret;
355 }
356
357 trip = of_thermal_get_trip_points(sensor->tzd);
358
359 for (i = 0; i < of_thermal_get_ntrips(sensor->tzd); i++) {
360 if (trip[i].type == THERMAL_TRIP_PASSIVE) {
e42bbe11 361 sensor->thres_temp = trip[i].temperature;
9a5238a9 362 break;
363 }
364 }
365
366 return 0;
367}
368
369static const struct of_device_id of_hisi_thermal_match[] = {
a160a465 370 { .compatible = "hisilicon,tsensor", .data = hi6220_thermal_probe },
9a5238a9 371 { /* end */ }
372};
373MODULE_DEVICE_TABLE(of, of_hisi_thermal_match);
374
375static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor,
376 bool on)
377{
378 struct thermal_zone_device *tzd = sensor->tzd;
379
380 tzd->ops->set_mode(tzd,
381 on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED);
382}
383
384static int hisi_thermal_probe(struct platform_device *pdev)
385{
386 struct hisi_thermal_data *data;
a160a465
KW
387 int const (*platform_probe)(struct hisi_thermal_data *);
388 struct device *dev = &pdev->dev;
9a5238a9 389 int ret;
390
a160a465 391 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
9a5238a9 392 if (!data)
393 return -ENOMEM;
394
9a5238a9 395 data->pdev = pdev;
a160a465 396 platform_set_drvdata(pdev, data);
9a5238a9 397
a160a465
KW
398 platform_probe = of_device_get_match_data(dev);
399 if (!platform_probe) {
400 dev_err(dev, "failed to get probe func\n");
401 return -EINVAL;
9a5238a9 402 }
403
a160a465
KW
404 ret = platform_probe(data);
405 if (ret)
9a5238a9 406 return ret;
9a5238a9 407
ff4ec299 408 ret = hisi_thermal_register_sensor(pdev, data,
a160a465 409 &data->sensor);
ff4ec299 410 if (ret) {
a160a465 411 dev_err(dev, "failed to register thermal sensor: %d\n", ret);
ff4ec299 412 return ret;
9a5238a9 413 }
414
a160a465 415 ret = data->enable_sensor(data);
10d7e9a9 416 if (ret) {
a160a465 417 dev_err(dev, "Failed to setup the sensor: %d\n", ret);
10d7e9a9
DL
418 return ret;
419 }
ff4ec299 420
a160a465
KW
421 if (data->irq) {
422 ret = devm_request_threaded_irq(dev, data->irq, NULL,
423 hisi_thermal_alarm_irq_thread,
424 IRQF_ONESHOT, "hisi_thermal", data);
425 if (ret < 0) {
426 dev_err(dev, "failed to request alarm irq: %d\n", ret);
427 return ret;
428 }
2cb4de78
DL
429 }
430
609f26dc 431 hisi_thermal_toggle_sensor(&data->sensor, true);
c176b10b 432
9a5238a9 433 return 0;
9a5238a9 434}
435
436static int hisi_thermal_remove(struct platform_device *pdev)
437{
438 struct hisi_thermal_data *data = platform_get_drvdata(pdev);
609f26dc 439 struct hisi_thermal_sensor *sensor = &data->sensor;
9a5238a9 440
ff4ec299 441 hisi_thermal_toggle_sensor(sensor, false);
a160a465
KW
442
443 data->disable_sensor(data);
9a5238a9 444
445 return 0;
446}
447
448#ifdef CONFIG_PM_SLEEP
449static int hisi_thermal_suspend(struct device *dev)
450{
451 struct hisi_thermal_data *data = dev_get_drvdata(dev);
452
a160a465 453 data->disable_sensor(data);
9a5238a9 454
9a5238a9 455 return 0;
456}
457
458static int hisi_thermal_resume(struct device *dev)
459{
460 struct hisi_thermal_data *data = dev_get_drvdata(dev);
461
a160a465 462 return data->enable_sensor(data);
9a5238a9 463}
464#endif
465
466static SIMPLE_DEV_PM_OPS(hisi_thermal_pm_ops,
467 hisi_thermal_suspend, hisi_thermal_resume);
468
469static struct platform_driver hisi_thermal_driver = {
470 .driver = {
471 .name = "hisi_thermal",
9a5238a9 472 .pm = &hisi_thermal_pm_ops,
473 .of_match_table = of_hisi_thermal_match,
474 },
475 .probe = hisi_thermal_probe,
476 .remove = hisi_thermal_remove,
477};
478
479module_platform_driver(hisi_thermal_driver);
480
481MODULE_AUTHOR("Xinwei Kong <kong.kongxinwei@hisilicon.com>");
482MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
483MODULE_DESCRIPTION("Hisilicon thermal driver");
484MODULE_LICENSE("GPL v2");