]>
Commit | Line | Data |
---|---|---|
2cd7f479 PF |
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> | |
85abf041 | 17 | #include <linux/delay.h> |
2cd7f479 PF |
18 | #include <malloc.h> |
19 | #include <thermal.h> | |
20 | ||
21 | DECLARE_GLOBAL_DATA_PTR; | |
22 | ||
23 | #define SITES_MAX 16 | |
fc8657b7 | 24 | #define FLAGS_VER2 0x1 |
634fe73e | 25 | #define FLAGS_VER3 0x2 |
2cd7f479 PF |
26 | |
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 | ||
fc8657b7 PF |
33 | #define TER_EN 0x80000000 |
34 | #define TER_ADC_PD 0x40000000 | |
634fe73e PF |
35 | #define TER_ALPF 0x3 |
36 | ||
2cd7f479 PF |
37 | /* |
38 | * i.MX TMU Registers | |
39 | */ | |
40 | struct imx_tmu_site_regs { | |
41 | u32 tritsr; /* Immediate Temperature Site Register */ | |
42 | u32 tratsr; /* Average Temperature Site Register */ | |
43 | u8 res0[0x8]; | |
44 | }; | |
45 | ||
46 | struct 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 | ||
fc8657b7 PF |
77 | struct 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 | ||
634fe73e PF |
93 | struct 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 | ||
fc8657b7 PF |
112 | union tmu_regs { |
113 | struct imx_tmu_regs regs_v1; | |
114 | struct imx_tmu_regs_v2 regs_v2; | |
634fe73e | 115 | struct imx_tmu_regs_v3 regs_v3; |
fc8657b7 PF |
116 | }; |
117 | ||
2cd7f479 PF |
118 | struct imx_tmu_plat { |
119 | int critical; | |
120 | int alert; | |
121 | int polling_delay; | |
122 | int id; | |
123 | bool zone_node; | |
fc8657b7 | 124 | union tmu_regs *regs; |
2cd7f479 PF |
125 | }; |
126 | ||
127 | static int read_temperature(struct udevice *dev, int *temp) | |
128 | { | |
c69cda25 | 129 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
fc8657b7 | 130 | ulong drv_data = dev_get_driver_data(dev); |
2cd7f479 | 131 | u32 val; |
b5447b98 | 132 | u32 retry = 10; |
951bf19d | 133 | u32 valid = 0; |
2cd7f479 PF |
134 | |
135 | do { | |
b5447b98 PF |
136 | mdelay(100); |
137 | retry--; | |
138 | ||
634fe73e PF |
139 | 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) { | |
fc8657b7 | 143 | val = readl(&pdata->regs->regs_v2.tritsr); |
951bf19d PF |
144 | /* |
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 { | |
fc8657b7 | 150 | val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr); |
951bf19d PF |
151 | valid = val & 0x80000000; |
152 | } | |
153 | } while (!valid && retry > 0); | |
2cd7f479 | 154 | |
634fe73e PF |
155 | 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 { | |
b5447b98 | 170 | return -EINVAL; |
634fe73e | 171 | } |
2cd7f479 PF |
172 | |
173 | return 0; | |
174 | } | |
175 | ||
176 | int imx_tmu_get_temp(struct udevice *dev, int *temp) | |
177 | { | |
c69cda25 | 178 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
2cd7f479 PF |
179 | 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 | ||
200 | static const struct dm_thermal_ops imx_tmu_ops = { | |
201 | .get_temp = imx_tmu_get_temp, | |
202 | }; | |
203 | ||
204 | static int imx_tmu_calibration(struct udevice *dev) | |
205 | { | |
206 | int i, val, len, ret; | |
207 | u32 range[4]; | |
208 | const fdt32_t *calibration; | |
c69cda25 | 209 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
fc8657b7 | 210 | ulong drv_data = dev_get_driver_data(dev); |
2cd7f479 PF |
211 | |
212 | debug("%s\n", __func__); | |
213 | ||
634fe73e | 214 | if (drv_data & (FLAGS_VER2 | FLAGS_VER3)) |
fc8657b7 PF |
215 | return 0; |
216 | ||
2cd7f479 PF |
217 | 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 */ | |
fc8657b7 PF |
224 | 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); | |
2cd7f479 PF |
228 | |
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); | |
fc8657b7 | 237 | writel(val, &pdata->regs->regs_v1.ttcfgr); |
2cd7f479 | 238 | val = fdt32_to_cpu(*(calibration + 1)); |
fc8657b7 | 239 | writel(val, &pdata->regs->regs_v1.tscfgr); |
2cd7f479 PF |
240 | } |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
84897408 PF |
245 | void __weak imx_tmu_arch_init(void *reg_base) |
246 | { | |
247 | } | |
248 | ||
fc8657b7 | 249 | static void imx_tmu_init(struct udevice *dev) |
2cd7f479 | 250 | { |
c69cda25 | 251 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
fc8657b7 PF |
252 | ulong drv_data = dev_get_driver_data(dev); |
253 | ||
2cd7f479 PF |
254 | debug("%s\n", __func__); |
255 | ||
634fe73e PF |
256 | 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) { | |
fc8657b7 PF |
264 | /* Disable monitoring */ |
265 | writel(0x0, &pdata->regs->regs_v2.ter); | |
266 | ||
267 | /* 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); | |
2cd7f479 | 272 | |
fc8657b7 PF |
273 | /* Disable interrupt, using polling instead */ |
274 | writel(TIER_DISABLE, &pdata->regs->regs_v1.tier); | |
2cd7f479 | 275 | |
fc8657b7 PF |
276 | /* Set update_interval */ |
277 | writel(TMTMIR_DEFAULT, &pdata->regs->regs_v1.tmtmir); | |
278 | } | |
84897408 PF |
279 | |
280 | imx_tmu_arch_init((void *)pdata->regs); | |
2cd7f479 PF |
281 | } |
282 | ||
283 | static int imx_tmu_enable_msite(struct udevice *dev) | |
284 | { | |
c69cda25 | 285 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
fc8657b7 | 286 | ulong drv_data = dev_get_driver_data(dev); |
2cd7f479 PF |
287 | u32 reg; |
288 | ||
289 | debug("%s\n", __func__); | |
290 | ||
291 | if (!pdata->regs) | |
292 | return -EIO; | |
293 | ||
634fe73e PF |
294 | 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) { | |
fc8657b7 PF |
310 | reg = readl(&pdata->regs->regs_v2.ter); |
311 | reg &= ~TER_EN; | |
312 | writel(reg, &pdata->regs->regs_v2.ter); | |
2cd7f479 | 313 | |
fc8657b7 PF |
314 | reg &= ~TER_ALPF; |
315 | reg |= 0x1; | |
316 | writel(reg, &pdata->regs->regs_v2.ter); | |
2cd7f479 | 317 | |
fc8657b7 PF |
318 | /* 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 | } | |
2cd7f479 PF |
335 | |
336 | return 0; | |
337 | } | |
338 | ||
339 | static int imx_tmu_bind(struct udevice *dev) | |
340 | { | |
c69cda25 | 341 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
2cd7f479 PF |
342 | 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 | ||
371 | static int imx_tmu_parse_fdt(struct udevice *dev) | |
372 | { | |
c69cda25 | 373 | struct imx_tmu_plat *pdata = dev_get_plat(dev), *p_parent_data; |
2cd7f479 PF |
374 | 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) { | |
fc8657b7 | 381 | pdata->regs = (union tmu_regs *)dev_read_addr_ptr(dev); |
2cd7f479 PF |
382 | |
383 | if (!pdata->regs) | |
384 | return -EINVAL; | |
385 | return 0; | |
386 | } | |
387 | ||
c69cda25 | 388 | p_parent_data = dev_get_plat(dev->parent); |
2cd7f479 PF |
389 | 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 | ||
431 | static int imx_tmu_probe(struct udevice *dev) | |
432 | { | |
c69cda25 | 433 | struct imx_tmu_plat *pdata = dev_get_plat(dev); |
2cd7f479 PF |
434 | 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) { | |
fc8657b7 | 443 | imx_tmu_init(dev); |
2cd7f479 PF |
444 | imx_tmu_calibration(dev); |
445 | } else { | |
446 | imx_tmu_enable_msite(dev); | |
447 | } | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | static const struct udevice_id imx_tmu_ids[] = { | |
453 | { .compatible = "fsl,imx8mq-tmu", }, | |
fc8657b7 | 454 | { .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, }, |
634fe73e | 455 | { .compatible = "fsl,imx8mp-tmu", .data = FLAGS_VER3, }, |
2cd7f479 PF |
456 | { } |
457 | }; | |
458 | ||
459 | U_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, | |
caa4daa2 | 466 | .plat_auto = sizeof(struct imx_tmu_plat), |
2cd7f479 PF |
467 | .flags = DM_FLAG_PRE_RELOC, |
468 | }; |