blob: 1bd0340410ac166b616939d428a9b51ad38dd35f [file] [log] [blame]
Peng Fan2cd7f472020-05-03 22:19:46 +08001// 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>
Tim Harvey85abf042020-10-12 12:21:54 -070017#include <linux/delay.h>
Peng Fan2cd7f472020-05-03 22:19:46 +080018#include <malloc.h>
19#include <thermal.h>
20
21DECLARE_GLOBAL_DATA_PTR;
22
23#define SITES_MAX 16
Peng Fanfc8657b2020-05-03 22:19:47 +080024#define FLAGS_VER2 0x1
Peng Fan634fe732020-05-03 22:19:51 +080025#define FLAGS_VER3 0x2
Peng Fan2cd7f472020-05-03 22:19:46 +080026
27#define TMR_DISABLE 0x0
28#define TMR_ME 0x80000000
29#define TMR_ALPF 0x0c000000
30#define TMTMIR_DEFAULT 0x00000002
31#define TIER_DISABLE 0x0
32
Peng Fanfc8657b2020-05-03 22:19:47 +080033#define TER_EN 0x80000000
34#define TER_ADC_PD 0x40000000
Peng Fan634fe732020-05-03 22:19:51 +080035#define TER_ALPF 0x3
36
Peng Fan2cd7f472020-05-03 22:19:46 +080037/*
38 * i.MX TMU Registers
39 */
40struct imx_tmu_site_regs {
41 u32 tritsr; /* Immediate Temperature Site Register */
42 u32 tratsr; /* Average Temperature Site Register */
43 u8 res0[0x8];
44};
45
46struct imx_tmu_regs {
47 u32 tmr; /* Mode Register */
48 u32 tsr; /* Status Register */
49 u32 tmtmir; /* Temperature measurement interval Register */
50 u8 res0[0x14];
51 u32 tier; /* Interrupt Enable Register */
52 u32 tidr; /* Interrupt Detect Register */
53 u32 tiscr; /* Interrupt Site Capture Register */
54 u32 ticscr; /* Interrupt Critical Site Capture Register */
55 u8 res1[0x10];
56 u32 tmhtcrh; /* High Temperature Capture Register */
57 u32 tmhtcrl; /* Low Temperature Capture Register */
58 u8 res2[0x8];
59 u32 tmhtitr; /* High Temperature Immediate Threshold */
60 u32 tmhtatr; /* High Temperature Average Threshold */
61 u32 tmhtactr; /* High Temperature Average Crit Threshold */
62 u8 res3[0x24];
63 u32 ttcfgr; /* Temperature Configuration Register */
64 u32 tscfgr; /* Sensor Configuration Register */
65 u8 res4[0x78];
66 struct imx_tmu_site_regs site[SITES_MAX];
67 u8 res5[0x9f8];
68 u32 ipbrr0; /* IP Block Revision Register 0 */
69 u32 ipbrr1; /* IP Block Revision Register 1 */
70 u8 res6[0x310];
71 u32 ttr0cr; /* Temperature Range 0 Control Register */
72 u32 ttr1cr; /* Temperature Range 1 Control Register */
73 u32 ttr2cr; /* Temperature Range 2 Control Register */
74 u32 ttr3cr; /* Temperature Range 3 Control Register */
75};
76
Peng Fanfc8657b2020-05-03 22:19:47 +080077struct imx_tmu_regs_v2 {
78 u32 ter; /* TMU enable Register */
79 u32 tsr; /* Status Register */
80 u32 tier; /* Interrupt enable register */
81 u32 tidr; /* Interrupt detect register */
82 u32 tmhtitr; /* Monitor high temperature immediate threshold register */
83 u32 tmhtatr; /* Monitor high temperature average threshold register */
84 u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
85 u32 tscr; /* Sensor value capture register */
86 u32 tritsr; /* Report immediate temperature site register 0 */
87 u32 tratsr; /* Report average temperature site register 0 */
88 u32 tasr; /* Amplifier setting register */
89 u32 ttmc; /* Test MUX control */
90 u32 tcaliv;
91};
92
Peng Fan634fe732020-05-03 22:19:51 +080093struct imx_tmu_regs_v3 {
94 u32 ter; /* TMU enable Register */
95 u32 tps; /* Status Register */
96 u32 tier; /* Interrupt enable register */
97 u32 tidr; /* Interrupt detect register */
98 u32 tmhtitr; /* Monitor high temperature immediate threshold register */
99 u32 tmhtatr; /* Monitor high temperature average threshold register */
100 u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
101 u32 tscr; /* Sensor value capture register */
102 u32 tritsr; /* Report immediate temperature site register 0 */
103 u32 tratsr; /* Report average temperature site register 0 */
104 u32 tasr; /* Amplifier setting register */
105 u32 ttmc; /* Test MUX control */
106 u32 tcaliv0;
107 u32 tcaliv1;
108 u32 tcaliv_m40;
109 u32 trim;
110};
111
Peng Fanfc8657b2020-05-03 22:19:47 +0800112union tmu_regs {
113 struct imx_tmu_regs regs_v1;
114 struct imx_tmu_regs_v2 regs_v2;
Peng Fan634fe732020-05-03 22:19:51 +0800115 struct imx_tmu_regs_v3 regs_v3;
Peng Fanfc8657b2020-05-03 22:19:47 +0800116};
117
Peng Fan2cd7f472020-05-03 22:19:46 +0800118struct imx_tmu_plat {
119 int critical;
120 int alert;
121 int polling_delay;
122 int id;
123 bool zone_node;
Peng Fanfc8657b2020-05-03 22:19:47 +0800124 union tmu_regs *regs;
Peng Fan2cd7f472020-05-03 22:19:46 +0800125};
126
127static int read_temperature(struct udevice *dev, int *temp)
128{
Simon Glassc69cda22020-12-03 16:55:20 -0700129 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800130 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800131 u32 val;
Peng Fanb5447b92020-05-03 22:19:49 +0800132 u32 retry = 10;
Peng Fan951bf192020-05-03 22:19:50 +0800133 u32 valid = 0;
Peng Fan2cd7f472020-05-03 22:19:46 +0800134
135 do {
Peng Fanb5447b92020-05-03 22:19:49 +0800136 mdelay(100);
137 retry--;
138
Peng Fan634fe732020-05-03 22:19:51 +0800139 if (drv_data & FLAGS_VER3) {
140 val = readl(&pdata->regs->regs_v3.tritsr);
141 valid = val & (1 << (30 + pdata->id));
142 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800143 val = readl(&pdata->regs->regs_v2.tritsr);
Peng Fan951bf192020-05-03 22:19:50 +0800144 /*
145 * Check if TEMP is in valid range, the V bit in TRITSR
146 * only reflects the RAW uncalibrated data
147 */
148 valid = ((val & 0xff) < 10 || (val & 0xff) > 125) ? 0 : 1;
149 } else {
Peng Fanfc8657b2020-05-03 22:19:47 +0800150 val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr);
Peng Fan951bf192020-05-03 22:19:50 +0800151 valid = val & 0x80000000;
152 }
153 } while (!valid && retry > 0);
Peng Fan2cd7f472020-05-03 22:19:46 +0800154
Peng Fan634fe732020-05-03 22:19:51 +0800155 if (retry > 0) {
156 if (drv_data & FLAGS_VER3) {
157 val = (val >> (pdata->id * 16)) & 0xff;
158 if (val & 0x80) /* Negative */
159 val = (~(val & 0x7f) + 1);
160
161 *temp = val;
162 if (*temp < -40 || *temp > 125) /* Check the range */
163 return -EINVAL;
164
165 *temp *= 1000;
166 } else {
167 *temp = (val & 0xff) * 1000;
168 }
169 } else {
Peng Fanb5447b92020-05-03 22:19:49 +0800170 return -EINVAL;
Peng Fan634fe732020-05-03 22:19:51 +0800171 }
Peng Fan2cd7f472020-05-03 22:19:46 +0800172
173 return 0;
174}
175
176int imx_tmu_get_temp(struct udevice *dev, int *temp)
177{
Simon Glassc69cda22020-12-03 16:55:20 -0700178 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800179 int cpu_tmp = 0;
180 int ret;
181
182 ret = read_temperature(dev, &cpu_tmp);
183 if (ret)
184 return ret;
185
186 while (cpu_tmp >= pdata->alert) {
187 printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
188 puts(" waiting...\n");
189 mdelay(pdata->polling_delay);
190 ret = read_temperature(dev, &cpu_tmp);
191 if (ret)
192 return ret;
193 }
194
195 *temp = cpu_tmp / 1000;
196
197 return 0;
198}
199
200static const struct dm_thermal_ops imx_tmu_ops = {
201 .get_temp = imx_tmu_get_temp,
202};
203
204static int imx_tmu_calibration(struct udevice *dev)
205{
206 int i, val, len, ret;
207 u32 range[4];
208 const fdt32_t *calibration;
Simon Glassc69cda22020-12-03 16:55:20 -0700209 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800210 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800211
212 debug("%s\n", __func__);
213
Peng Fan634fe732020-05-03 22:19:51 +0800214 if (drv_data & (FLAGS_VER2 | FLAGS_VER3))
Peng Fanfc8657b2020-05-03 22:19:47 +0800215 return 0;
216
Peng Fan2cd7f472020-05-03 22:19:46 +0800217 ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
218 if (ret) {
219 printf("TMU: missing calibration range, ret = %d.\n", ret);
220 return ret;
221 }
222
223 /* Init temperature range registers */
Peng Fanfc8657b2020-05-03 22:19:47 +0800224 writel(range[0], &pdata->regs->regs_v1.ttr0cr);
225 writel(range[1], &pdata->regs->regs_v1.ttr1cr);
226 writel(range[2], &pdata->regs->regs_v1.ttr2cr);
227 writel(range[3], &pdata->regs->regs_v1.ttr3cr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800228
229 calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
230 if (!calibration || len % 8) {
231 printf("TMU: invalid calibration data.\n");
232 return -ENODEV;
233 }
234
235 for (i = 0; i < len; i += 8, calibration += 2) {
236 val = fdt32_to_cpu(*calibration);
Peng Fanfc8657b2020-05-03 22:19:47 +0800237 writel(val, &pdata->regs->regs_v1.ttcfgr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800238 val = fdt32_to_cpu(*(calibration + 1));
Peng Fanfc8657b2020-05-03 22:19:47 +0800239 writel(val, &pdata->regs->regs_v1.tscfgr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800240 }
241
242 return 0;
243}
244
Peng Fan84897402020-05-03 22:19:48 +0800245void __weak imx_tmu_arch_init(void *reg_base)
246{
247}
248
Peng Fanfc8657b2020-05-03 22:19:47 +0800249static void imx_tmu_init(struct udevice *dev)
Peng Fan2cd7f472020-05-03 22:19:46 +0800250{
Simon Glassc69cda22020-12-03 16:55:20 -0700251 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800252 ulong drv_data = dev_get_driver_data(dev);
253
Peng Fan2cd7f472020-05-03 22:19:46 +0800254 debug("%s\n", __func__);
255
Peng Fan634fe732020-05-03 22:19:51 +0800256 if (drv_data & FLAGS_VER3) {
257 /* Disable monitoring */
258 writel(0x0, &pdata->regs->regs_v3.ter);
259
260 /* Disable interrupt, using polling instead */
261 writel(0x0, &pdata->regs->regs_v3.tier);
262
263 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800264 /* Disable monitoring */
265 writel(0x0, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800266
Peng Fanfc8657b2020-05-03 22:19:47 +0800267 /* Disable interrupt, using polling instead */
268 writel(0x0, &pdata->regs->regs_v2.tier);
269 } else {
270 /* Disable monitoring */
271 writel(TMR_DISABLE, &pdata->regs->regs_v1.tmr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800272
Peng Fanfc8657b2020-05-03 22:19:47 +0800273 /* Disable interrupt, using polling instead */
274 writel(TIER_DISABLE, &pdata->regs->regs_v1.tier);
275
276 /* Set update_interval */
277 writel(TMTMIR_DEFAULT, &pdata->regs->regs_v1.tmtmir);
278 }
Peng Fan84897402020-05-03 22:19:48 +0800279
280 imx_tmu_arch_init((void *)pdata->regs);
Peng Fan2cd7f472020-05-03 22:19:46 +0800281}
282
283static int imx_tmu_enable_msite(struct udevice *dev)
284{
Simon Glassc69cda22020-12-03 16:55:20 -0700285 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800286 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800287 u32 reg;
288
289 debug("%s\n", __func__);
290
291 if (!pdata->regs)
292 return -EIO;
293
Peng Fan634fe732020-05-03 22:19:51 +0800294 if (drv_data & FLAGS_VER3) {
295 reg = readl(&pdata->regs->regs_v3.ter);
296 reg &= ~TER_EN;
297 writel(reg, &pdata->regs->regs_v3.ter);
298
299 writel(pdata->id << 30, &pdata->regs->regs_v3.tps);
300
301 reg &= ~TER_ALPF;
302 reg |= 0x1;
303 reg &= ~TER_ADC_PD;
304 writel(reg, &pdata->regs->regs_v3.ter);
305
306 /* Enable monitor */
307 reg |= TER_EN;
308 writel(reg, &pdata->regs->regs_v3.ter);
309 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800310 reg = readl(&pdata->regs->regs_v2.ter);
311 reg &= ~TER_EN;
312 writel(reg, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800313
Peng Fanfc8657b2020-05-03 22:19:47 +0800314 reg &= ~TER_ALPF;
315 reg |= 0x1;
316 writel(reg, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800317
Peng Fanfc8657b2020-05-03 22:19:47 +0800318 /* Enable monitor */
319 reg |= TER_EN;
320 writel(reg, &pdata->regs->regs_v2.ter);
321 } else {
322 /* Clear the ME before setting MSITE and ALPF*/
323 reg = readl(&pdata->regs->regs_v1.tmr);
324 reg &= ~TMR_ME;
325 writel(reg, &pdata->regs->regs_v1.tmr);
326
327 reg |= 1 << (15 - pdata->id);
328 reg |= TMR_ALPF;
329 writel(reg, &pdata->regs->regs_v1.tmr);
330
331 /* Enable ME */
332 reg |= TMR_ME;
333 writel(reg, &pdata->regs->regs_v1.tmr);
334 }
Peng Fan2cd7f472020-05-03 22:19:46 +0800335
336 return 0;
337}
338
339static int imx_tmu_bind(struct udevice *dev)
340{
Simon Glassc69cda22020-12-03 16:55:20 -0700341 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800342 int ret;
343 ofnode node, offset;
344 const char *name;
345 const void *prop;
346
347 debug("%s dev name %s\n", __func__, dev->name);
348
349 prop = dev_read_prop(dev, "compatible", NULL);
350 if (!prop)
351 return 0;
352
353 pdata->zone_node = 1;
354
355 node = ofnode_path("/thermal-zones");
356 ofnode_for_each_subnode(offset, node) {
357 /* Bind the subnode to this driver */
358 name = ofnode_get_name(offset);
359
360 ret = device_bind_with_driver_data(dev, dev->driver, name,
361 dev->driver_data, offset,
362 NULL);
363 if (ret)
364 printf("Error binding driver '%s': %d\n",
365 dev->driver->name, ret);
366 }
367
368 return 0;
369}
370
371static int imx_tmu_parse_fdt(struct udevice *dev)
372{
Simon Glassc69cda22020-12-03 16:55:20 -0700373 struct imx_tmu_plat *pdata = dev_get_plat(dev), *p_parent_data;
Peng Fan2cd7f472020-05-03 22:19:46 +0800374 struct ofnode_phandle_args args;
375 ofnode trips_np;
376 int ret;
377
378 debug("%s dev name %s\n", __func__, dev->name);
379
380 if (pdata->zone_node) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800381 pdata->regs = (union tmu_regs *)dev_read_addr_ptr(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800382
383 if (!pdata->regs)
384 return -EINVAL;
385 return 0;
386 }
387
Simon Glassc69cda22020-12-03 16:55:20 -0700388 p_parent_data = dev_get_plat(dev->parent);
Peng Fan2cd7f472020-05-03 22:19:46 +0800389 if (p_parent_data->zone_node)
390 pdata->regs = p_parent_data->regs;
391
392 ret = dev_read_phandle_with_args(dev, "thermal-sensors",
393 "#thermal-sensor-cells",
394 0, 0, &args);
395 if (ret)
396 return ret;
397
398 if (!ofnode_equal(args.node, dev_ofnode(dev->parent)))
399 return -EFAULT;
400
401 if (args.args_count >= 1)
402 pdata->id = args.args[0];
403 else
404 pdata->id = 0;
405
406 debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
407
408 pdata->polling_delay = dev_read_u32_default(dev, "polling-delay", 1000);
409
410 trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
411 ofnode_for_each_subnode(trips_np, trips_np) {
412 const char *type;
413
414 type = ofnode_get_property(trips_np, "type", NULL);
415 if (!type)
416 continue;
417 if (!strcmp(type, "critical"))
418 pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
419 else if (strcmp(type, "passive") == 0)
420 pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
421 else
422 continue;
423 }
424
425 debug("id %d polling_delay %d, critical %d, alert %d\n",
426 pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
427
428 return 0;
429}
430
431static int imx_tmu_probe(struct udevice *dev)
432{
Simon Glassc69cda22020-12-03 16:55:20 -0700433 struct imx_tmu_plat *pdata = dev_get_plat(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800434 int ret;
435
436 ret = imx_tmu_parse_fdt(dev);
437 if (ret) {
438 printf("Error in parsing TMU FDT %d\n", ret);
439 return ret;
440 }
441
442 if (pdata->zone_node) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800443 imx_tmu_init(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800444 imx_tmu_calibration(dev);
445 } else {
446 imx_tmu_enable_msite(dev);
447 }
448
449 return 0;
450}
451
452static const struct udevice_id imx_tmu_ids[] = {
453 { .compatible = "fsl,imx8mq-tmu", },
Peng Fanfc8657b2020-05-03 22:19:47 +0800454 { .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, },
Peng Fan634fe732020-05-03 22:19:51 +0800455 { .compatible = "fsl,imx8mp-tmu", .data = FLAGS_VER3, },
Peng Fan2cd7f472020-05-03 22:19:46 +0800456 { }
457};
458
459U_BOOT_DRIVER(imx_tmu) = {
460 .name = "imx_tmu",
461 .id = UCLASS_THERMAL,
462 .ops = &imx_tmu_ops,
463 .of_match = imx_tmu_ids,
464 .bind = imx_tmu_bind,
465 .probe = imx_tmu_probe,
Simon Glasscaa4daa2020-12-03 16:55:18 -0700466 .plat_auto = sizeof(struct imx_tmu_plat),
Peng Fan2cd7f472020-05-03 22:19:46 +0800467 .flags = DM_FLAG_PRE_RELOC,
468};