blob: 4ca22089b8b2cc2bed8d206290f933198ae79aad [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>
17#include <malloc.h>
18#include <thermal.h>
19
20DECLARE_GLOBAL_DATA_PTR;
21
22#define SITES_MAX 16
Peng Fanfc8657b2020-05-03 22:19:47 +080023#define FLAGS_VER2 0x1
Peng Fan634fe732020-05-03 22:19:51 +080024#define FLAGS_VER3 0x2
Peng Fan2cd7f472020-05-03 22:19:46 +080025
26#define TMR_DISABLE 0x0
27#define TMR_ME 0x80000000
28#define TMR_ALPF 0x0c000000
29#define TMTMIR_DEFAULT 0x00000002
30#define TIER_DISABLE 0x0
31
Peng Fanfc8657b2020-05-03 22:19:47 +080032#define TER_EN 0x80000000
33#define TER_ADC_PD 0x40000000
Peng Fan634fe732020-05-03 22:19:51 +080034#define TER_ALPF 0x3
35
Peng Fan2cd7f472020-05-03 22:19:46 +080036/*
37 * i.MX TMU Registers
38 */
39struct imx_tmu_site_regs {
40 u32 tritsr; /* Immediate Temperature Site Register */
41 u32 tratsr; /* Average Temperature Site Register */
42 u8 res0[0x8];
43};
44
45struct imx_tmu_regs {
46 u32 tmr; /* Mode Register */
47 u32 tsr; /* Status Register */
48 u32 tmtmir; /* Temperature measurement interval Register */
49 u8 res0[0x14];
50 u32 tier; /* Interrupt Enable Register */
51 u32 tidr; /* Interrupt Detect Register */
52 u32 tiscr; /* Interrupt Site Capture Register */
53 u32 ticscr; /* Interrupt Critical Site Capture Register */
54 u8 res1[0x10];
55 u32 tmhtcrh; /* High Temperature Capture Register */
56 u32 tmhtcrl; /* Low Temperature Capture Register */
57 u8 res2[0x8];
58 u32 tmhtitr; /* High Temperature Immediate Threshold */
59 u32 tmhtatr; /* High Temperature Average Threshold */
60 u32 tmhtactr; /* High Temperature Average Crit Threshold */
61 u8 res3[0x24];
62 u32 ttcfgr; /* Temperature Configuration Register */
63 u32 tscfgr; /* Sensor Configuration Register */
64 u8 res4[0x78];
65 struct imx_tmu_site_regs site[SITES_MAX];
66 u8 res5[0x9f8];
67 u32 ipbrr0; /* IP Block Revision Register 0 */
68 u32 ipbrr1; /* IP Block Revision Register 1 */
69 u8 res6[0x310];
70 u32 ttr0cr; /* Temperature Range 0 Control Register */
71 u32 ttr1cr; /* Temperature Range 1 Control Register */
72 u32 ttr2cr; /* Temperature Range 2 Control Register */
73 u32 ttr3cr; /* Temperature Range 3 Control Register */
74};
75
Peng Fanfc8657b2020-05-03 22:19:47 +080076struct imx_tmu_regs_v2 {
77 u32 ter; /* TMU enable Register */
78 u32 tsr; /* Status Register */
79 u32 tier; /* Interrupt enable register */
80 u32 tidr; /* Interrupt detect register */
81 u32 tmhtitr; /* Monitor high temperature immediate threshold register */
82 u32 tmhtatr; /* Monitor high temperature average threshold register */
83 u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
84 u32 tscr; /* Sensor value capture register */
85 u32 tritsr; /* Report immediate temperature site register 0 */
86 u32 tratsr; /* Report average temperature site register 0 */
87 u32 tasr; /* Amplifier setting register */
88 u32 ttmc; /* Test MUX control */
89 u32 tcaliv;
90};
91
Peng Fan634fe732020-05-03 22:19:51 +080092struct imx_tmu_regs_v3 {
93 u32 ter; /* TMU enable Register */
94 u32 tps; /* Status Register */
95 u32 tier; /* Interrupt enable register */
96 u32 tidr; /* Interrupt detect register */
97 u32 tmhtitr; /* Monitor high temperature immediate threshold register */
98 u32 tmhtatr; /* Monitor high temperature average threshold register */
99 u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
100 u32 tscr; /* Sensor value capture register */
101 u32 tritsr; /* Report immediate temperature site register 0 */
102 u32 tratsr; /* Report average temperature site register 0 */
103 u32 tasr; /* Amplifier setting register */
104 u32 ttmc; /* Test MUX control */
105 u32 tcaliv0;
106 u32 tcaliv1;
107 u32 tcaliv_m40;
108 u32 trim;
109};
110
Peng Fanfc8657b2020-05-03 22:19:47 +0800111union tmu_regs {
112 struct imx_tmu_regs regs_v1;
113 struct imx_tmu_regs_v2 regs_v2;
Peng Fan634fe732020-05-03 22:19:51 +0800114 struct imx_tmu_regs_v3 regs_v3;
Peng Fanfc8657b2020-05-03 22:19:47 +0800115};
116
Peng Fan2cd7f472020-05-03 22:19:46 +0800117struct imx_tmu_plat {
118 int critical;
119 int alert;
120 int polling_delay;
121 int id;
122 bool zone_node;
Peng Fanfc8657b2020-05-03 22:19:47 +0800123 union tmu_regs *regs;
Peng Fan2cd7f472020-05-03 22:19:46 +0800124};
125
126static int read_temperature(struct udevice *dev, int *temp)
127{
128 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800129 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800130 u32 val;
Peng Fanb5447b92020-05-03 22:19:49 +0800131 u32 retry = 10;
Peng Fan951bf192020-05-03 22:19:50 +0800132 u32 valid = 0;
Peng Fan2cd7f472020-05-03 22:19:46 +0800133
134 do {
Peng Fanb5447b92020-05-03 22:19:49 +0800135 mdelay(100);
136 retry--;
137
Peng Fan634fe732020-05-03 22:19:51 +0800138 if (drv_data & FLAGS_VER3) {
139 val = readl(&pdata->regs->regs_v3.tritsr);
140 valid = val & (1 << (30 + pdata->id));
141 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800142 val = readl(&pdata->regs->regs_v2.tritsr);
Peng Fan951bf192020-05-03 22:19:50 +0800143 /*
144 * Check if TEMP is in valid range, the V bit in TRITSR
145 * only reflects the RAW uncalibrated data
146 */
147 valid = ((val & 0xff) < 10 || (val & 0xff) > 125) ? 0 : 1;
148 } else {
Peng Fanfc8657b2020-05-03 22:19:47 +0800149 val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr);
Peng Fan951bf192020-05-03 22:19:50 +0800150 valid = val & 0x80000000;
151 }
152 } while (!valid && retry > 0);
Peng Fan2cd7f472020-05-03 22:19:46 +0800153
Peng Fan634fe732020-05-03 22:19:51 +0800154 if (retry > 0) {
155 if (drv_data & FLAGS_VER3) {
156 val = (val >> (pdata->id * 16)) & 0xff;
157 if (val & 0x80) /* Negative */
158 val = (~(val & 0x7f) + 1);
159
160 *temp = val;
161 if (*temp < -40 || *temp > 125) /* Check the range */
162 return -EINVAL;
163
164 *temp *= 1000;
165 } else {
166 *temp = (val & 0xff) * 1000;
167 }
168 } else {
Peng Fanb5447b92020-05-03 22:19:49 +0800169 return -EINVAL;
Peng Fan634fe732020-05-03 22:19:51 +0800170 }
Peng Fan2cd7f472020-05-03 22:19:46 +0800171
172 return 0;
173}
174
175int imx_tmu_get_temp(struct udevice *dev, int *temp)
176{
177 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
178 int cpu_tmp = 0;
179 int ret;
180
181 ret = read_temperature(dev, &cpu_tmp);
182 if (ret)
183 return ret;
184
185 while (cpu_tmp >= pdata->alert) {
186 printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
187 puts(" waiting...\n");
188 mdelay(pdata->polling_delay);
189 ret = read_temperature(dev, &cpu_tmp);
190 if (ret)
191 return ret;
192 }
193
194 *temp = cpu_tmp / 1000;
195
196 return 0;
197}
198
199static const struct dm_thermal_ops imx_tmu_ops = {
200 .get_temp = imx_tmu_get_temp,
201};
202
203static int imx_tmu_calibration(struct udevice *dev)
204{
205 int i, val, len, ret;
206 u32 range[4];
207 const fdt32_t *calibration;
208 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800209 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800210
211 debug("%s\n", __func__);
212
Peng Fan634fe732020-05-03 22:19:51 +0800213 if (drv_data & (FLAGS_VER2 | FLAGS_VER3))
Peng Fanfc8657b2020-05-03 22:19:47 +0800214 return 0;
215
Peng Fan2cd7f472020-05-03 22:19:46 +0800216 ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
217 if (ret) {
218 printf("TMU: missing calibration range, ret = %d.\n", ret);
219 return ret;
220 }
221
222 /* Init temperature range registers */
Peng Fanfc8657b2020-05-03 22:19:47 +0800223 writel(range[0], &pdata->regs->regs_v1.ttr0cr);
224 writel(range[1], &pdata->regs->regs_v1.ttr1cr);
225 writel(range[2], &pdata->regs->regs_v1.ttr2cr);
226 writel(range[3], &pdata->regs->regs_v1.ttr3cr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800227
228 calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
229 if (!calibration || len % 8) {
230 printf("TMU: invalid calibration data.\n");
231 return -ENODEV;
232 }
233
234 for (i = 0; i < len; i += 8, calibration += 2) {
235 val = fdt32_to_cpu(*calibration);
Peng Fanfc8657b2020-05-03 22:19:47 +0800236 writel(val, &pdata->regs->regs_v1.ttcfgr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800237 val = fdt32_to_cpu(*(calibration + 1));
Peng Fanfc8657b2020-05-03 22:19:47 +0800238 writel(val, &pdata->regs->regs_v1.tscfgr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800239 }
240
241 return 0;
242}
243
Peng Fan84897402020-05-03 22:19:48 +0800244void __weak imx_tmu_arch_init(void *reg_base)
245{
246}
247
Peng Fanfc8657b2020-05-03 22:19:47 +0800248static void imx_tmu_init(struct udevice *dev)
Peng Fan2cd7f472020-05-03 22:19:46 +0800249{
Peng Fanfc8657b2020-05-03 22:19:47 +0800250 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
251 ulong drv_data = dev_get_driver_data(dev);
252
Peng Fan2cd7f472020-05-03 22:19:46 +0800253 debug("%s\n", __func__);
254
Peng Fan634fe732020-05-03 22:19:51 +0800255 if (drv_data & FLAGS_VER3) {
256 /* Disable monitoring */
257 writel(0x0, &pdata->regs->regs_v3.ter);
258
259 /* Disable interrupt, using polling instead */
260 writel(0x0, &pdata->regs->regs_v3.tier);
261
262 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800263 /* Disable monitoring */
264 writel(0x0, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800265
Peng Fanfc8657b2020-05-03 22:19:47 +0800266 /* Disable interrupt, using polling instead */
267 writel(0x0, &pdata->regs->regs_v2.tier);
268 } else {
269 /* Disable monitoring */
270 writel(TMR_DISABLE, &pdata->regs->regs_v1.tmr);
Peng Fan2cd7f472020-05-03 22:19:46 +0800271
Peng Fanfc8657b2020-05-03 22:19:47 +0800272 /* Disable interrupt, using polling instead */
273 writel(TIER_DISABLE, &pdata->regs->regs_v1.tier);
274
275 /* Set update_interval */
276 writel(TMTMIR_DEFAULT, &pdata->regs->regs_v1.tmtmir);
277 }
Peng Fan84897402020-05-03 22:19:48 +0800278
279 imx_tmu_arch_init((void *)pdata->regs);
Peng Fan2cd7f472020-05-03 22:19:46 +0800280}
281
282static int imx_tmu_enable_msite(struct udevice *dev)
283{
284 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
Peng Fanfc8657b2020-05-03 22:19:47 +0800285 ulong drv_data = dev_get_driver_data(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800286 u32 reg;
287
288 debug("%s\n", __func__);
289
290 if (!pdata->regs)
291 return -EIO;
292
Peng Fan634fe732020-05-03 22:19:51 +0800293 if (drv_data & FLAGS_VER3) {
294 reg = readl(&pdata->regs->regs_v3.ter);
295 reg &= ~TER_EN;
296 writel(reg, &pdata->regs->regs_v3.ter);
297
298 writel(pdata->id << 30, &pdata->regs->regs_v3.tps);
299
300 reg &= ~TER_ALPF;
301 reg |= 0x1;
302 reg &= ~TER_ADC_PD;
303 writel(reg, &pdata->regs->regs_v3.ter);
304
305 /* Enable monitor */
306 reg |= TER_EN;
307 writel(reg, &pdata->regs->regs_v3.ter);
308 } else if (drv_data & FLAGS_VER2) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800309 reg = readl(&pdata->regs->regs_v2.ter);
310 reg &= ~TER_EN;
311 writel(reg, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800312
Peng Fanfc8657b2020-05-03 22:19:47 +0800313 reg &= ~TER_ALPF;
314 reg |= 0x1;
315 writel(reg, &pdata->regs->regs_v2.ter);
Peng Fan2cd7f472020-05-03 22:19:46 +0800316
Peng Fanfc8657b2020-05-03 22:19:47 +0800317 /* Enable monitor */
318 reg |= TER_EN;
319 writel(reg, &pdata->regs->regs_v2.ter);
320 } else {
321 /* Clear the ME before setting MSITE and ALPF*/
322 reg = readl(&pdata->regs->regs_v1.tmr);
323 reg &= ~TMR_ME;
324 writel(reg, &pdata->regs->regs_v1.tmr);
325
326 reg |= 1 << (15 - pdata->id);
327 reg |= TMR_ALPF;
328 writel(reg, &pdata->regs->regs_v1.tmr);
329
330 /* Enable ME */
331 reg |= TMR_ME;
332 writel(reg, &pdata->regs->regs_v1.tmr);
333 }
Peng Fan2cd7f472020-05-03 22:19:46 +0800334
335 return 0;
336}
337
338static int imx_tmu_bind(struct udevice *dev)
339{
340 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
341 int ret;
342 ofnode node, offset;
343 const char *name;
344 const void *prop;
345
346 debug("%s dev name %s\n", __func__, dev->name);
347
348 prop = dev_read_prop(dev, "compatible", NULL);
349 if (!prop)
350 return 0;
351
352 pdata->zone_node = 1;
353
354 node = ofnode_path("/thermal-zones");
355 ofnode_for_each_subnode(offset, node) {
356 /* Bind the subnode to this driver */
357 name = ofnode_get_name(offset);
358
359 ret = device_bind_with_driver_data(dev, dev->driver, name,
360 dev->driver_data, offset,
361 NULL);
362 if (ret)
363 printf("Error binding driver '%s': %d\n",
364 dev->driver->name, ret);
365 }
366
367 return 0;
368}
369
370static int imx_tmu_parse_fdt(struct udevice *dev)
371{
372 struct imx_tmu_plat *pdata = dev_get_platdata(dev), *p_parent_data;
373 struct ofnode_phandle_args args;
374 ofnode trips_np;
375 int ret;
376
377 debug("%s dev name %s\n", __func__, dev->name);
378
379 if (pdata->zone_node) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800380 pdata->regs = (union tmu_regs *)dev_read_addr_ptr(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800381
382 if (!pdata->regs)
383 return -EINVAL;
384 return 0;
385 }
386
387 p_parent_data = dev_get_platdata(dev->parent);
388 if (p_parent_data->zone_node)
389 pdata->regs = p_parent_data->regs;
390
391 ret = dev_read_phandle_with_args(dev, "thermal-sensors",
392 "#thermal-sensor-cells",
393 0, 0, &args);
394 if (ret)
395 return ret;
396
397 if (!ofnode_equal(args.node, dev_ofnode(dev->parent)))
398 return -EFAULT;
399
400 if (args.args_count >= 1)
401 pdata->id = args.args[0];
402 else
403 pdata->id = 0;
404
405 debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
406
407 pdata->polling_delay = dev_read_u32_default(dev, "polling-delay", 1000);
408
409 trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
410 ofnode_for_each_subnode(trips_np, trips_np) {
411 const char *type;
412
413 type = ofnode_get_property(trips_np, "type", NULL);
414 if (!type)
415 continue;
416 if (!strcmp(type, "critical"))
417 pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
418 else if (strcmp(type, "passive") == 0)
419 pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
420 else
421 continue;
422 }
423
424 debug("id %d polling_delay %d, critical %d, alert %d\n",
425 pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
426
427 return 0;
428}
429
430static int imx_tmu_probe(struct udevice *dev)
431{
432 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
433 int ret;
434
435 ret = imx_tmu_parse_fdt(dev);
436 if (ret) {
437 printf("Error in parsing TMU FDT %d\n", ret);
438 return ret;
439 }
440
441 if (pdata->zone_node) {
Peng Fanfc8657b2020-05-03 22:19:47 +0800442 imx_tmu_init(dev);
Peng Fan2cd7f472020-05-03 22:19:46 +0800443 imx_tmu_calibration(dev);
444 } else {
445 imx_tmu_enable_msite(dev);
446 }
447
448 return 0;
449}
450
451static const struct udevice_id imx_tmu_ids[] = {
452 { .compatible = "fsl,imx8mq-tmu", },
Peng Fanfc8657b2020-05-03 22:19:47 +0800453 { .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, },
Peng Fan634fe732020-05-03 22:19:51 +0800454 { .compatible = "fsl,imx8mp-tmu", .data = FLAGS_VER3, },
Peng Fan2cd7f472020-05-03 22:19:46 +0800455 { }
456};
457
458U_BOOT_DRIVER(imx_tmu) = {
459 .name = "imx_tmu",
460 .id = UCLASS_THERMAL,
461 .ops = &imx_tmu_ops,
462 .of_match = imx_tmu_ids,
463 .bind = imx_tmu_bind,
464 .probe = imx_tmu_probe,
465 .platdata_auto_alloc_size = sizeof(struct imx_tmu_plat),
466 .flags = DM_FLAG_PRE_RELOC,
467};