blob: f496ce03b6579f7af80e614e400dba7ea9478d78 [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
23
24#define TMR_DISABLE 0x0
25#define TMR_ME 0x80000000
26#define TMR_ALPF 0x0c000000
27#define TMTMIR_DEFAULT 0x00000002
28#define TIER_DISABLE 0x0
29
30/*
31 * i.MX TMU Registers
32 */
33struct imx_tmu_site_regs {
34 u32 tritsr; /* Immediate Temperature Site Register */
35 u32 tratsr; /* Average Temperature Site Register */
36 u8 res0[0x8];
37};
38
39struct imx_tmu_regs {
40 u32 tmr; /* Mode Register */
41 u32 tsr; /* Status Register */
42 u32 tmtmir; /* Temperature measurement interval Register */
43 u8 res0[0x14];
44 u32 tier; /* Interrupt Enable Register */
45 u32 tidr; /* Interrupt Detect Register */
46 u32 tiscr; /* Interrupt Site Capture Register */
47 u32 ticscr; /* Interrupt Critical Site Capture Register */
48 u8 res1[0x10];
49 u32 tmhtcrh; /* High Temperature Capture Register */
50 u32 tmhtcrl; /* Low Temperature Capture Register */
51 u8 res2[0x8];
52 u32 tmhtitr; /* High Temperature Immediate Threshold */
53 u32 tmhtatr; /* High Temperature Average Threshold */
54 u32 tmhtactr; /* High Temperature Average Crit Threshold */
55 u8 res3[0x24];
56 u32 ttcfgr; /* Temperature Configuration Register */
57 u32 tscfgr; /* Sensor Configuration Register */
58 u8 res4[0x78];
59 struct imx_tmu_site_regs site[SITES_MAX];
60 u8 res5[0x9f8];
61 u32 ipbrr0; /* IP Block Revision Register 0 */
62 u32 ipbrr1; /* IP Block Revision Register 1 */
63 u8 res6[0x310];
64 u32 ttr0cr; /* Temperature Range 0 Control Register */
65 u32 ttr1cr; /* Temperature Range 1 Control Register */
66 u32 ttr2cr; /* Temperature Range 2 Control Register */
67 u32 ttr3cr; /* Temperature Range 3 Control Register */
68};
69
70struct imx_tmu_plat {
71 int critical;
72 int alert;
73 int polling_delay;
74 int id;
75 bool zone_node;
76 struct imx_tmu_regs *regs;
77};
78
79static int read_temperature(struct udevice *dev, int *temp)
80{
81 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
82 u32 val;
83
84 do {
85 val = readl(&pdata->regs->site[pdata->id].tritsr);
86 } while (!(val & 0x80000000));
87
88 *temp = (val & 0xff) * 1000;
89
90 return 0;
91}
92
93int imx_tmu_get_temp(struct udevice *dev, int *temp)
94{
95 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
96 int cpu_tmp = 0;
97 int ret;
98
99 ret = read_temperature(dev, &cpu_tmp);
100 if (ret)
101 return ret;
102
103 while (cpu_tmp >= pdata->alert) {
104 printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
105 puts(" waiting...\n");
106 mdelay(pdata->polling_delay);
107 ret = read_temperature(dev, &cpu_tmp);
108 if (ret)
109 return ret;
110 }
111
112 *temp = cpu_tmp / 1000;
113
114 return 0;
115}
116
117static const struct dm_thermal_ops imx_tmu_ops = {
118 .get_temp = imx_tmu_get_temp,
119};
120
121static int imx_tmu_calibration(struct udevice *dev)
122{
123 int i, val, len, ret;
124 u32 range[4];
125 const fdt32_t *calibration;
126 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
127
128 debug("%s\n", __func__);
129
130 ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
131 if (ret) {
132 printf("TMU: missing calibration range, ret = %d.\n", ret);
133 return ret;
134 }
135
136 /* Init temperature range registers */
137 writel(range[0], &pdata->regs->ttr0cr);
138 writel(range[1], &pdata->regs->ttr1cr);
139 writel(range[2], &pdata->regs->ttr2cr);
140 writel(range[3], &pdata->regs->ttr3cr);
141
142 calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
143 if (!calibration || len % 8) {
144 printf("TMU: invalid calibration data.\n");
145 return -ENODEV;
146 }
147
148 for (i = 0; i < len; i += 8, calibration += 2) {
149 val = fdt32_to_cpu(*calibration);
150 writel(val, &pdata->regs->ttcfgr);
151 val = fdt32_to_cpu(*(calibration + 1));
152 writel(val, &pdata->regs->tscfgr);
153 }
154
155 return 0;
156}
157
158static void imx_tmu_init(struct imx_tmu_plat *pdata)
159{
160 debug("%s\n", __func__);
161
162 /* Disable monitoring */
163 writel(TMR_DISABLE, &pdata->regs->tmr);
164
165 /* Disable interrupt, using polling instead */
166 writel(TIER_DISABLE, &pdata->regs->tier);
167
168 /* Set update_interval */
169 writel(TMTMIR_DEFAULT, &pdata->regs->tmtmir);
170}
171
172static int imx_tmu_enable_msite(struct udevice *dev)
173{
174 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
175 u32 reg;
176
177 debug("%s\n", __func__);
178
179 if (!pdata->regs)
180 return -EIO;
181
182 /* Clear the ME before setting MSITE and ALPF*/
183 reg = readl(&pdata->regs->tmr);
184 reg &= ~TMR_ME;
185 writel(reg, &pdata->regs->tmr);
186
187 reg |= 1 << (15 - pdata->id);
188 reg |= TMR_ALPF;
189 writel(reg, &pdata->regs->tmr);
190
191 /* Enable ME */
192 reg |= TMR_ME;
193 writel(reg, &pdata->regs->tmr);
194
195 return 0;
196}
197
198static int imx_tmu_bind(struct udevice *dev)
199{
200 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
201 int ret;
202 ofnode node, offset;
203 const char *name;
204 const void *prop;
205
206 debug("%s dev name %s\n", __func__, dev->name);
207
208 prop = dev_read_prop(dev, "compatible", NULL);
209 if (!prop)
210 return 0;
211
212 pdata->zone_node = 1;
213
214 node = ofnode_path("/thermal-zones");
215 ofnode_for_each_subnode(offset, node) {
216 /* Bind the subnode to this driver */
217 name = ofnode_get_name(offset);
218
219 ret = device_bind_with_driver_data(dev, dev->driver, name,
220 dev->driver_data, offset,
221 NULL);
222 if (ret)
223 printf("Error binding driver '%s': %d\n",
224 dev->driver->name, ret);
225 }
226
227 return 0;
228}
229
230static int imx_tmu_parse_fdt(struct udevice *dev)
231{
232 struct imx_tmu_plat *pdata = dev_get_platdata(dev), *p_parent_data;
233 struct ofnode_phandle_args args;
234 ofnode trips_np;
235 int ret;
236
237 debug("%s dev name %s\n", __func__, dev->name);
238
239 if (pdata->zone_node) {
240 pdata->regs = (struct imx_tmu_regs *)dev_read_addr_ptr(dev);
241
242 if (!pdata->regs)
243 return -EINVAL;
244 return 0;
245 }
246
247 p_parent_data = dev_get_platdata(dev->parent);
248 if (p_parent_data->zone_node)
249 pdata->regs = p_parent_data->regs;
250
251 ret = dev_read_phandle_with_args(dev, "thermal-sensors",
252 "#thermal-sensor-cells",
253 0, 0, &args);
254 if (ret)
255 return ret;
256
257 if (!ofnode_equal(args.node, dev_ofnode(dev->parent)))
258 return -EFAULT;
259
260 if (args.args_count >= 1)
261 pdata->id = args.args[0];
262 else
263 pdata->id = 0;
264
265 debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
266
267 pdata->polling_delay = dev_read_u32_default(dev, "polling-delay", 1000);
268
269 trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
270 ofnode_for_each_subnode(trips_np, trips_np) {
271 const char *type;
272
273 type = ofnode_get_property(trips_np, "type", NULL);
274 if (!type)
275 continue;
276 if (!strcmp(type, "critical"))
277 pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
278 else if (strcmp(type, "passive") == 0)
279 pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
280 else
281 continue;
282 }
283
284 debug("id %d polling_delay %d, critical %d, alert %d\n",
285 pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
286
287 return 0;
288}
289
290static int imx_tmu_probe(struct udevice *dev)
291{
292 struct imx_tmu_plat *pdata = dev_get_platdata(dev);
293 int ret;
294
295 ret = imx_tmu_parse_fdt(dev);
296 if (ret) {
297 printf("Error in parsing TMU FDT %d\n", ret);
298 return ret;
299 }
300
301 if (pdata->zone_node) {
302 imx_tmu_init(pdata);
303 imx_tmu_calibration(dev);
304 } else {
305 imx_tmu_enable_msite(dev);
306 }
307
308 return 0;
309}
310
311static const struct udevice_id imx_tmu_ids[] = {
312 { .compatible = "fsl,imx8mq-tmu", },
313 { }
314};
315
316U_BOOT_DRIVER(imx_tmu) = {
317 .name = "imx_tmu",
318 .id = UCLASS_THERMAL,
319 .ops = &imx_tmu_ops,
320 .of_match = imx_tmu_ids,
321 .bind = imx_tmu_bind,
322 .probe = imx_tmu_probe,
323 .platdata_auto_alloc_size = sizeof(struct imx_tmu_plat),
324 .flags = DM_FLAG_PRE_RELOC,
325};