thermal: imx_tmu: Check the TEMP range for iMX8MM
[oweals/u-boot.git] / drivers / thermal / imx_tmu.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2017~2020 NXP
4  *
5  */
6
7 #include <config.h>
8 #include <common.h>
9 #include <asm/io.h>
10 #include <asm/arch/clock.h>
11 #include <asm/arch/sys_proto.h>
12 #include <dm.h>
13 #include <dm/device-internal.h>
14 #include <dm/device.h>
15 #include <errno.h>
16 #include <fuse.h>
17 #include <malloc.h>
18 #include <thermal.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 #define SITES_MAX       16
23 #define FLAGS_VER2      0x1
24
25 #define TMR_DISABLE     0x0
26 #define TMR_ME          0x80000000
27 #define TMR_ALPF        0x0c000000
28 #define TMTMIR_DEFAULT  0x00000002
29 #define TIER_DISABLE    0x0
30
31 #define TER_EN                  0x80000000
32 #define TER_ADC_PD              0x40000000
33 /*
34  * i.MX TMU Registers
35  */
36 struct imx_tmu_site_regs {
37         u32 tritsr;             /* Immediate Temperature Site Register */
38         u32 tratsr;             /* Average Temperature Site Register */
39         u8 res0[0x8];
40 };
41
42 struct imx_tmu_regs {
43         u32 tmr;        /* Mode Register */
44         u32 tsr;        /* Status Register */
45         u32 tmtmir;     /* Temperature measurement interval Register */
46         u8 res0[0x14];
47         u32 tier;       /* Interrupt Enable Register */
48         u32 tidr;       /* Interrupt Detect Register */
49         u32 tiscr;      /* Interrupt Site Capture Register */
50         u32 ticscr;     /* Interrupt Critical Site Capture Register */
51         u8 res1[0x10];
52         u32 tmhtcrh;    /* High Temperature Capture Register */
53         u32 tmhtcrl;    /* Low Temperature Capture Register */
54         u8 res2[0x8];
55         u32 tmhtitr;    /* High Temperature Immediate Threshold */
56         u32 tmhtatr;    /* High Temperature Average Threshold */
57         u32 tmhtactr;   /* High Temperature Average Crit Threshold */
58         u8 res3[0x24];
59         u32 ttcfgr;     /* Temperature Configuration Register */
60         u32 tscfgr;     /* Sensor Configuration Register */
61         u8 res4[0x78];
62         struct imx_tmu_site_regs site[SITES_MAX];
63         u8 res5[0x9f8];
64         u32 ipbrr0;     /* IP Block Revision Register 0 */
65         u32 ipbrr1;     /* IP Block Revision Register 1 */
66         u8 res6[0x310];
67         u32 ttr0cr;     /* Temperature Range 0 Control Register */
68         u32 ttr1cr;     /* Temperature Range 1 Control Register */
69         u32 ttr2cr;     /* Temperature Range 2 Control Register */
70         u32 ttr3cr;     /* Temperature Range 3 Control Register */
71 };
72
73 struct imx_tmu_regs_v2 {
74         u32 ter;        /* TMU enable Register */
75         u32 tsr;        /* Status Register */
76         u32 tier;       /* Interrupt enable register */
77         u32 tidr;       /* Interrupt detect  register */
78         u32 tmhtitr;    /* Monitor high temperature immediate threshold register */
79         u32 tmhtatr;    /* Monitor high temperature average threshold register */
80         u32 tmhtactr;   /* TMU monitor high temperature average critical  threshold register */
81         u32 tscr;       /* Sensor value capture register */
82         u32 tritsr;     /* Report immediate temperature site register 0 */
83         u32 tratsr;     /* Report average temperature site register 0 */
84         u32 tasr;       /* Amplifier setting register */
85         u32 ttmc;       /* Test MUX control */
86         u32 tcaliv;
87 };
88
89 union tmu_regs {
90         struct imx_tmu_regs regs_v1;
91         struct imx_tmu_regs_v2 regs_v2;
92 };
93
94 struct imx_tmu_plat {
95         int critical;
96         int alert;
97         int polling_delay;
98         int id;
99         bool zone_node;
100         union tmu_regs *regs;
101 };
102
103 static int read_temperature(struct udevice *dev, int *temp)
104 {
105         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
106         ulong drv_data = dev_get_driver_data(dev);
107         u32 val;
108         u32 retry = 10;
109         u32 valid = 0;
110
111         do {
112                 mdelay(100);
113                 retry--;
114
115                 if (drv_data & FLAGS_VER2) {
116                         val = readl(&pdata->regs->regs_v2.tritsr);
117                         /*
118                          * Check if TEMP is in valid range, the V bit in TRITSR
119                          * only reflects the RAW uncalibrated data
120                          */
121                         valid =  ((val & 0xff) < 10 || (val & 0xff) > 125) ? 0 : 1;
122                 } else {
123                         val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr);
124                         valid = val & 0x80000000;
125                 }
126         } while (!valid && retry > 0);
127
128         if (retry > 0)
129                 *temp = (val & 0xff) * 1000;
130         else
131                 return -EINVAL;
132
133         return 0;
134 }
135
136 int imx_tmu_get_temp(struct udevice *dev, int *temp)
137 {
138         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
139         int cpu_tmp = 0;
140         int ret;
141
142         ret = read_temperature(dev, &cpu_tmp);
143         if (ret)
144                 return ret;
145
146         while (cpu_tmp >= pdata->alert) {
147                 printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
148                 puts(" waiting...\n");
149                 mdelay(pdata->polling_delay);
150                 ret = read_temperature(dev, &cpu_tmp);
151                 if (ret)
152                         return ret;
153         }
154
155         *temp = cpu_tmp / 1000;
156
157         return 0;
158 }
159
160 static const struct dm_thermal_ops imx_tmu_ops = {
161         .get_temp       = imx_tmu_get_temp,
162 };
163
164 static int imx_tmu_calibration(struct udevice *dev)
165 {
166         int i, val, len, ret;
167         u32 range[4];
168         const fdt32_t *calibration;
169         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
170         ulong drv_data = dev_get_driver_data(dev);
171
172         debug("%s\n", __func__);
173
174         if (drv_data & FLAGS_VER2)
175                 return 0;
176
177         ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
178         if (ret) {
179                 printf("TMU: missing calibration range, ret = %d.\n", ret);
180                 return ret;
181         }
182
183         /* Init temperature range registers */
184         writel(range[0], &pdata->regs->regs_v1.ttr0cr);
185         writel(range[1], &pdata->regs->regs_v1.ttr1cr);
186         writel(range[2], &pdata->regs->regs_v1.ttr2cr);
187         writel(range[3], &pdata->regs->regs_v1.ttr3cr);
188
189         calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
190         if (!calibration || len % 8) {
191                 printf("TMU: invalid calibration data.\n");
192                 return -ENODEV;
193         }
194
195         for (i = 0; i < len; i += 8, calibration += 2) {
196                 val = fdt32_to_cpu(*calibration);
197                 writel(val, &pdata->regs->regs_v1.ttcfgr);
198                 val = fdt32_to_cpu(*(calibration + 1));
199                 writel(val, &pdata->regs->regs_v1.tscfgr);
200         }
201
202         return 0;
203 }
204
205 void __weak imx_tmu_arch_init(void *reg_base)
206 {
207 }
208
209 static void imx_tmu_init(struct udevice *dev)
210 {
211         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
212         ulong drv_data = dev_get_driver_data(dev);
213
214         debug("%s\n", __func__);
215
216         if (drv_data & FLAGS_VER2) {
217                 /* Disable monitoring */
218                 writel(0x0, &pdata->regs->regs_v2.ter);
219
220                 /* Disable interrupt, using polling instead */
221                 writel(0x0, &pdata->regs->regs_v2.tier);
222         } else {
223                 /* Disable monitoring */
224                 writel(TMR_DISABLE, &pdata->regs->regs_v1.tmr);
225
226                 /* Disable interrupt, using polling instead */
227                 writel(TIER_DISABLE, &pdata->regs->regs_v1.tier);
228
229                 /* Set update_interval */
230                 writel(TMTMIR_DEFAULT, &pdata->regs->regs_v1.tmtmir);
231         }
232
233         imx_tmu_arch_init((void *)pdata->regs);
234 }
235
236 static int imx_tmu_enable_msite(struct udevice *dev)
237 {
238         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
239         ulong drv_data = dev_get_driver_data(dev);
240         u32 reg;
241
242         debug("%s\n", __func__);
243
244         if (!pdata->regs)
245                 return -EIO;
246
247         if (drv_data & FLAGS_VER2) {
248                 reg = readl(&pdata->regs->regs_v2.ter);
249                 reg &= ~TER_EN;
250                 writel(reg, &pdata->regs->regs_v2.ter);
251
252                 reg &= ~TER_ALPF;
253                 reg |= 0x1;
254                 writel(reg, &pdata->regs->regs_v2.ter);
255
256                 /* Enable monitor */
257                 reg |= TER_EN;
258                 writel(reg, &pdata->regs->regs_v2.ter);
259         } else {
260                 /* Clear the ME before setting MSITE and ALPF*/
261                 reg = readl(&pdata->regs->regs_v1.tmr);
262                 reg &= ~TMR_ME;
263                 writel(reg, &pdata->regs->regs_v1.tmr);
264
265                 reg |= 1 << (15 - pdata->id);
266                 reg |= TMR_ALPF;
267                 writel(reg, &pdata->regs->regs_v1.tmr);
268
269                 /* Enable ME */
270                 reg |= TMR_ME;
271                 writel(reg, &pdata->regs->regs_v1.tmr);
272         }
273
274         return 0;
275 }
276
277 static int imx_tmu_bind(struct udevice *dev)
278 {
279         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
280         int ret;
281         ofnode node, offset;
282         const char *name;
283         const void *prop;
284
285         debug("%s dev name %s\n", __func__, dev->name);
286
287         prop = dev_read_prop(dev, "compatible", NULL);
288         if (!prop)
289                 return 0;
290
291         pdata->zone_node = 1;
292
293         node = ofnode_path("/thermal-zones");
294         ofnode_for_each_subnode(offset, node) {
295                 /* Bind the subnode to this driver */
296                 name = ofnode_get_name(offset);
297
298                 ret = device_bind_with_driver_data(dev, dev->driver, name,
299                                                    dev->driver_data, offset,
300                                                    NULL);
301                 if (ret)
302                         printf("Error binding driver '%s': %d\n",
303                                dev->driver->name, ret);
304         }
305
306         return 0;
307 }
308
309 static int imx_tmu_parse_fdt(struct udevice *dev)
310 {
311         struct imx_tmu_plat *pdata = dev_get_platdata(dev), *p_parent_data;
312         struct ofnode_phandle_args args;
313         ofnode trips_np;
314         int ret;
315
316         debug("%s dev name %s\n", __func__, dev->name);
317
318         if (pdata->zone_node) {
319                 pdata->regs = (union tmu_regs *)dev_read_addr_ptr(dev);
320
321                 if (!pdata->regs)
322                         return -EINVAL;
323                 return 0;
324         }
325
326         p_parent_data = dev_get_platdata(dev->parent);
327         if (p_parent_data->zone_node)
328                 pdata->regs = p_parent_data->regs;
329
330         ret = dev_read_phandle_with_args(dev, "thermal-sensors",
331                                          "#thermal-sensor-cells",
332                                          0, 0, &args);
333         if (ret)
334                 return ret;
335
336         if (!ofnode_equal(args.node, dev_ofnode(dev->parent)))
337                 return -EFAULT;
338
339         if (args.args_count >= 1)
340                 pdata->id = args.args[0];
341         else
342                 pdata->id = 0;
343
344         debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
345
346         pdata->polling_delay = dev_read_u32_default(dev, "polling-delay", 1000);
347
348         trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
349         ofnode_for_each_subnode(trips_np, trips_np) {
350                 const char *type;
351
352                 type = ofnode_get_property(trips_np, "type", NULL);
353                 if (!type)
354                         continue;
355                 if (!strcmp(type, "critical"))
356                         pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
357                 else if (strcmp(type, "passive") == 0)
358                         pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
359                 else
360                         continue;
361         }
362
363         debug("id %d polling_delay %d, critical %d, alert %d\n",
364               pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
365
366         return 0;
367 }
368
369 static int imx_tmu_probe(struct udevice *dev)
370 {
371         struct imx_tmu_plat *pdata = dev_get_platdata(dev);
372         int ret;
373
374         ret = imx_tmu_parse_fdt(dev);
375         if (ret) {
376                 printf("Error in parsing TMU FDT %d\n", ret);
377                 return ret;
378         }
379
380         if (pdata->zone_node) {
381                 imx_tmu_init(dev);
382                 imx_tmu_calibration(dev);
383         } else {
384                 imx_tmu_enable_msite(dev);
385         }
386
387         return 0;
388 }
389
390 static const struct udevice_id imx_tmu_ids[] = {
391         { .compatible = "fsl,imx8mq-tmu", },
392         { .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, },
393         { }
394 };
395
396 U_BOOT_DRIVER(imx_tmu) = {
397         .name   = "imx_tmu",
398         .id     = UCLASS_THERMAL,
399         .ops    = &imx_tmu_ops,
400         .of_match = imx_tmu_ids,
401         .bind = imx_tmu_bind,
402         .probe  = imx_tmu_probe,
403         .platdata_auto_alloc_size = sizeof(struct imx_tmu_plat),
404         .flags  = DM_FLAG_PRE_RELOC,
405 };