Merge tag 'for-linus' of git://git.armlinux.org.uk/~rmk/linux-arm
[platform/kernel/linux-starfive.git] / drivers / thermal / uniphier_thermal.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * uniphier_thermal.c - Socionext UniPhier thermal driver
4  * Copyright 2014      Panasonic Corporation
5  * Copyright 2016-2017 Socionext Inc.
6  * Author:
7  *      Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
8  */
9
10 #include <linux/bitops.h>
11 #include <linux/interrupt.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_device.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/thermal.h>
19
20 /*
21  * block registers
22  * addresses are the offset from .block_base
23  */
24 #define PVTCTLEN                        0x0000
25 #define PVTCTLEN_EN                     BIT(0)
26
27 #define PVTCTLMODE                      0x0004
28 #define PVTCTLMODE_MASK                 0xf
29 #define PVTCTLMODE_TEMPMON              0x5
30
31 #define EMONREPEAT                      0x0040
32 #define EMONREPEAT_ENDLESS              BIT(24)
33 #define EMONREPEAT_PERIOD               GENMASK(3, 0)
34 #define EMONREPEAT_PERIOD_1000000       0x9
35
36 /*
37  * common registers
38  * addresses are the offset from .map_base
39  */
40 #define PVTCTLSEL                       0x0900
41 #define PVTCTLSEL_MASK                  GENMASK(2, 0)
42 #define PVTCTLSEL_MONITOR               0
43
44 #define SETALERT0                       0x0910
45 #define SETALERT1                       0x0914
46 #define SETALERT2                       0x0918
47 #define SETALERT_TEMP_OVF               (GENMASK(7, 0) << 16)
48 #define SETALERT_TEMP_OVF_VALUE(val)    (((val) & GENMASK(7, 0)) << 16)
49 #define SETALERT_EN                     BIT(0)
50
51 #define PMALERTINTCTL                   0x0920
52 #define PMALERTINTCTL_CLR(ch)           BIT(4 * (ch) + 2)
53 #define PMALERTINTCTL_SET(ch)           BIT(4 * (ch) + 1)
54 #define PMALERTINTCTL_EN(ch)            BIT(4 * (ch) + 0)
55 #define PMALERTINTCTL_MASK              (GENMASK(10, 8) | GENMASK(6, 4) | \
56                                          GENMASK(2, 0))
57
58 #define TMOD                            0x0928
59 #define TMOD_WIDTH                      9
60
61 #define TMODCOEF                        0x0e5c
62
63 #define TMODSETUP0_EN                   BIT(30)
64 #define TMODSETUP0_VAL(val)             (((val) & GENMASK(13, 0)) << 16)
65 #define TMODSETUP1_EN                   BIT(15)
66 #define TMODSETUP1_VAL(val)             ((val) & GENMASK(14, 0))
67
68 /* SoC critical temperature */
69 #define CRITICAL_TEMP_LIMIT             (120 * 1000)
70
71 /* Max # of alert channels */
72 #define ALERT_CH_NUM                    3
73
74 /* SoC specific thermal sensor data */
75 struct uniphier_tm_soc_data {
76         u32 map_base;
77         u32 block_base;
78         u32 tmod_setup_addr;
79 };
80
81 struct uniphier_tm_dev {
82         struct regmap *regmap;
83         struct device *dev;
84         bool alert_en[ALERT_CH_NUM];
85         struct thermal_zone_device *tz_dev;
86         const struct uniphier_tm_soc_data *data;
87 };
88
89 static int uniphier_tm_initialize_sensor(struct uniphier_tm_dev *tdev)
90 {
91         struct regmap *map = tdev->regmap;
92         u32 val;
93         u32 tmod_calib[2];
94         int ret;
95
96         /* stop PVT */
97         regmap_write_bits(map, tdev->data->block_base + PVTCTLEN,
98                           PVTCTLEN_EN, 0);
99
100         /*
101          * Since SoC has a calibrated value that was set in advance,
102          * TMODCOEF shows non-zero and PVT refers the value internally.
103          *
104          * If TMODCOEF shows zero, the boards don't have the calibrated
105          * value, and the driver has to set default value from DT.
106          */
107         ret = regmap_read(map, tdev->data->map_base + TMODCOEF, &val);
108         if (ret)
109                 return ret;
110         if (!val) {
111                 /* look for the default values in DT */
112                 ret = of_property_read_u32_array(tdev->dev->of_node,
113                                                  "socionext,tmod-calibration",
114                                                  tmod_calib,
115                                                  ARRAY_SIZE(tmod_calib));
116                 if (ret)
117                         return ret;
118
119                 regmap_write(map, tdev->data->tmod_setup_addr,
120                         TMODSETUP0_EN | TMODSETUP0_VAL(tmod_calib[0]) |
121                         TMODSETUP1_EN | TMODSETUP1_VAL(tmod_calib[1]));
122         }
123
124         /* select temperature mode */
125         regmap_write_bits(map, tdev->data->block_base + PVTCTLMODE,
126                           PVTCTLMODE_MASK, PVTCTLMODE_TEMPMON);
127
128         /* set monitoring period */
129         regmap_write_bits(map, tdev->data->block_base + EMONREPEAT,
130                           EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD,
131                           EMONREPEAT_ENDLESS | EMONREPEAT_PERIOD_1000000);
132
133         /* set monitor mode */
134         regmap_write_bits(map, tdev->data->map_base + PVTCTLSEL,
135                           PVTCTLSEL_MASK, PVTCTLSEL_MONITOR);
136
137         return 0;
138 }
139
140 static void uniphier_tm_set_alert(struct uniphier_tm_dev *tdev, u32 ch,
141                                   u32 temp)
142 {
143         struct regmap *map = tdev->regmap;
144
145         /* set alert temperature */
146         regmap_write_bits(map, tdev->data->map_base + SETALERT0 + (ch << 2),
147                           SETALERT_EN | SETALERT_TEMP_OVF,
148                           SETALERT_EN |
149                           SETALERT_TEMP_OVF_VALUE(temp / 1000));
150 }
151
152 static void uniphier_tm_enable_sensor(struct uniphier_tm_dev *tdev)
153 {
154         struct regmap *map = tdev->regmap;
155         int i;
156         u32 bits = 0;
157
158         for (i = 0; i < ALERT_CH_NUM; i++)
159                 if (tdev->alert_en[i])
160                         bits |= PMALERTINTCTL_EN(i);
161
162         /* enable alert interrupt */
163         regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL,
164                           PMALERTINTCTL_MASK, bits);
165
166         /* start PVT */
167         regmap_write_bits(map, tdev->data->block_base + PVTCTLEN,
168                           PVTCTLEN_EN, PVTCTLEN_EN);
169
170         usleep_range(700, 1500);        /* The spec note says at least 700us */
171 }
172
173 static void uniphier_tm_disable_sensor(struct uniphier_tm_dev *tdev)
174 {
175         struct regmap *map = tdev->regmap;
176
177         /* disable alert interrupt */
178         regmap_write_bits(map, tdev->data->map_base + PMALERTINTCTL,
179                           PMALERTINTCTL_MASK, 0);
180
181         /* stop PVT */
182         regmap_write_bits(map, tdev->data->block_base + PVTCTLEN,
183                           PVTCTLEN_EN, 0);
184
185         usleep_range(1000, 2000);       /* The spec note says at least 1ms */
186 }
187
188 static int uniphier_tm_get_temp(struct thermal_zone_device *tz, int *out_temp)
189 {
190         struct uniphier_tm_dev *tdev = thermal_zone_device_priv(tz);
191         struct regmap *map = tdev->regmap;
192         int ret;
193         u32 temp;
194
195         ret = regmap_read(map, tdev->data->map_base + TMOD, &temp);
196         if (ret)
197                 return ret;
198
199         /* MSB of the TMOD field is a sign bit */
200         *out_temp = sign_extend32(temp, TMOD_WIDTH - 1) * 1000;
201
202         return 0;
203 }
204
205 static const struct thermal_zone_device_ops uniphier_of_thermal_ops = {
206         .get_temp = uniphier_tm_get_temp,
207 };
208
209 static void uniphier_tm_irq_clear(struct uniphier_tm_dev *tdev)
210 {
211         u32 mask = 0, bits = 0;
212         int i;
213
214         for (i = 0; i < ALERT_CH_NUM; i++) {
215                 mask |= (PMALERTINTCTL_CLR(i) | PMALERTINTCTL_SET(i));
216                 bits |= PMALERTINTCTL_CLR(i);
217         }
218
219         /* clear alert interrupt */
220         regmap_write_bits(tdev->regmap,
221                           tdev->data->map_base + PMALERTINTCTL, mask, bits);
222 }
223
224 static irqreturn_t uniphier_tm_alarm_irq(int irq, void *_tdev)
225 {
226         struct uniphier_tm_dev *tdev = _tdev;
227
228         disable_irq_nosync(irq);
229         uniphier_tm_irq_clear(tdev);
230
231         return IRQ_WAKE_THREAD;
232 }
233
234 static irqreturn_t uniphier_tm_alarm_irq_thread(int irq, void *_tdev)
235 {
236         struct uniphier_tm_dev *tdev = _tdev;
237
238         thermal_zone_device_update(tdev->tz_dev, THERMAL_EVENT_UNSPECIFIED);
239
240         return IRQ_HANDLED;
241 }
242
243 static int uniphier_tm_probe(struct platform_device *pdev)
244 {
245         struct device *dev = &pdev->dev;
246         struct regmap *regmap;
247         struct device_node *parent;
248         struct uniphier_tm_dev *tdev;
249         int i, ret, irq, crit_temp = INT_MAX;
250
251         tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL);
252         if (!tdev)
253                 return -ENOMEM;
254         tdev->dev = dev;
255
256         tdev->data = of_device_get_match_data(dev);
257         if (WARN_ON(!tdev->data))
258                 return -EINVAL;
259
260         irq = platform_get_irq(pdev, 0);
261         if (irq < 0)
262                 return irq;
263
264         /* get regmap from syscon node */
265         parent = of_get_parent(dev->of_node); /* parent should be syscon node */
266         regmap = syscon_node_to_regmap(parent);
267         of_node_put(parent);
268         if (IS_ERR(regmap)) {
269                 dev_err(dev, "failed to get regmap (error %ld)\n",
270                         PTR_ERR(regmap));
271                 return PTR_ERR(regmap);
272         }
273         tdev->regmap = regmap;
274
275         ret = uniphier_tm_initialize_sensor(tdev);
276         if (ret) {
277                 dev_err(dev, "failed to initialize sensor\n");
278                 return ret;
279         }
280
281         ret = devm_request_threaded_irq(dev, irq, uniphier_tm_alarm_irq,
282                                         uniphier_tm_alarm_irq_thread,
283                                         0, "thermal", tdev);
284         if (ret)
285                 return ret;
286
287         platform_set_drvdata(pdev, tdev);
288
289         tdev->tz_dev = devm_thermal_of_zone_register(dev, 0, tdev,
290                                                      &uniphier_of_thermal_ops);
291         if (IS_ERR(tdev->tz_dev)) {
292                 dev_err(dev, "failed to register sensor device\n");
293                 return PTR_ERR(tdev->tz_dev);
294         }
295
296         /* set alert temperatures */
297         for (i = 0; i < thermal_zone_get_num_trips(tdev->tz_dev); i++) {
298                 struct thermal_trip trip;
299
300                 ret = thermal_zone_get_trip(tdev->tz_dev, i, &trip);
301                 if (ret)
302                         return ret;
303
304                 if (trip.type == THERMAL_TRIP_CRITICAL &&
305                     trip.temperature < crit_temp)
306                         crit_temp = trip.temperature;
307                 uniphier_tm_set_alert(tdev, i, trip.temperature);
308                 tdev->alert_en[i] = true;
309         }
310         if (crit_temp > CRITICAL_TEMP_LIMIT) {
311                 dev_err(dev, "critical trip is over limit(>%d), or not set\n",
312                         CRITICAL_TEMP_LIMIT);
313                 return -EINVAL;
314         }
315
316         uniphier_tm_enable_sensor(tdev);
317
318         return 0;
319 }
320
321 static int uniphier_tm_remove(struct platform_device *pdev)
322 {
323         struct uniphier_tm_dev *tdev = platform_get_drvdata(pdev);
324
325         /* disable sensor */
326         uniphier_tm_disable_sensor(tdev);
327
328         return 0;
329 }
330
331 static const struct uniphier_tm_soc_data uniphier_pxs2_tm_data = {
332         .map_base        = 0xe000,
333         .block_base      = 0xe000,
334         .tmod_setup_addr = 0xe904,
335 };
336
337 static const struct uniphier_tm_soc_data uniphier_ld20_tm_data = {
338         .map_base        = 0xe000,
339         .block_base      = 0xe800,
340         .tmod_setup_addr = 0xe938,
341 };
342
343 static const struct of_device_id uniphier_tm_dt_ids[] = {
344         {
345                 .compatible = "socionext,uniphier-pxs2-thermal",
346                 .data       = &uniphier_pxs2_tm_data,
347         },
348         {
349                 .compatible = "socionext,uniphier-ld20-thermal",
350                 .data       = &uniphier_ld20_tm_data,
351         },
352         {
353                 .compatible = "socionext,uniphier-pxs3-thermal",
354                 .data       = &uniphier_ld20_tm_data,
355         },
356         {
357                 .compatible = "socionext,uniphier-nx1-thermal",
358                 .data       = &uniphier_ld20_tm_data,
359         },
360         { /* sentinel */ }
361 };
362 MODULE_DEVICE_TABLE(of, uniphier_tm_dt_ids);
363
364 static struct platform_driver uniphier_tm_driver = {
365         .probe = uniphier_tm_probe,
366         .remove = uniphier_tm_remove,
367         .driver = {
368                 .name = "uniphier-thermal",
369                 .of_match_table = uniphier_tm_dt_ids,
370         },
371 };
372 module_platform_driver(uniphier_tm_driver);
373
374 MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
375 MODULE_DESCRIPTION("UniPhier thermal driver");
376 MODULE_LICENSE("GPL v2");