]>
Commit | Line | Data |
---|---|---|
2dfef650 FE |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright 2016 Freescale Semiconductor, Inc. | |
43528445 | 4 | |
51904045 | 5 | #include <linux/clk.h> |
43528445 JH |
6 | #include <linux/module.h> |
7 | #include <linux/platform_device.h> | |
8 | #include <linux/err.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/of_address.h> | |
12 | #include <linux/thermal.h> | |
13 | ||
14 | #include "thermal_core.h" | |
15 | ||
9809797b YT |
16 | #define SITES_MAX 16 |
17 | #define TMR_DISABLE 0x0 | |
18 | #define TMR_ME 0x80000000 | |
19 | #define TMR_ALPF 0x0c000000 | |
20 | #define TMR_ALPF_V2 0x03000000 | |
21 | #define TMTMIR_DEFAULT 0x0000000f | |
22 | #define TIER_DISABLE 0x0 | |
23 | #define TEUMR0_V2 0x51009c00 | |
24 | #define TMU_VER1 0x1 | |
25 | #define TMU_VER2 0x2 | |
43528445 JH |
26 | |
27 | /* | |
28 | * QorIQ TMU Registers | |
29 | */ | |
30 | struct qoriq_tmu_site_regs { | |
31 | u32 tritsr; /* Immediate Temperature Site Register */ | |
32 | u32 tratsr; /* Average Temperature Site Register */ | |
33 | u8 res0[0x8]; | |
34 | }; | |
35 | ||
9809797b | 36 | struct qoriq_tmu_regs_v1 { |
43528445 | 37 | u32 tmr; /* Mode Register */ |
43528445 JH |
38 | u32 tsr; /* Status Register */ |
39 | u32 tmtmir; /* Temperature measurement interval Register */ | |
43528445 JH |
40 | u8 res0[0x14]; |
41 | u32 tier; /* Interrupt Enable Register */ | |
43528445 JH |
42 | u32 tidr; /* Interrupt Detect Register */ |
43 | u32 tiscr; /* Interrupt Site Capture Register */ | |
44 | u32 ticscr; /* Interrupt Critical Site Capture Register */ | |
45 | u8 res1[0x10]; | |
46 | u32 tmhtcrh; /* High Temperature Capture Register */ | |
47 | u32 tmhtcrl; /* Low Temperature Capture Register */ | |
48 | u8 res2[0x8]; | |
49 | u32 tmhtitr; /* High Temperature Immediate Threshold */ | |
50 | u32 tmhtatr; /* High Temperature Average Threshold */ | |
51 | u32 tmhtactr; /* High Temperature Average Crit Threshold */ | |
52 | u8 res3[0x24]; | |
53 | u32 ttcfgr; /* Temperature Configuration Register */ | |
54 | u32 tscfgr; /* Sensor Configuration Register */ | |
55 | u8 res4[0x78]; | |
56 | struct qoriq_tmu_site_regs site[SITES_MAX]; | |
57 | u8 res5[0x9f8]; | |
58 | u32 ipbrr0; /* IP Block Revision Register 0 */ | |
59 | u32 ipbrr1; /* IP Block Revision Register 1 */ | |
60 | u8 res6[0x310]; | |
9809797b YT |
61 | u32 ttrcr[4]; /* Temperature Range Control Register */ |
62 | }; | |
63 | ||
64 | struct qoriq_tmu_regs_v2 { | |
65 | u32 tmr; /* Mode Register */ | |
66 | u32 tsr; /* Status Register */ | |
67 | u32 tmsr; /* monitor site register */ | |
68 | u32 tmtmir; /* Temperature measurement interval Register */ | |
69 | u8 res0[0x10]; | |
70 | u32 tier; /* Interrupt Enable Register */ | |
71 | u32 tidr; /* Interrupt Detect Register */ | |
72 | u8 res1[0x8]; | |
73 | u32 tiiscr; /* interrupt immediate site capture register */ | |
74 | u32 tiascr; /* interrupt average site capture register */ | |
75 | u32 ticscr; /* Interrupt Critical Site Capture Register */ | |
76 | u32 res2; | |
77 | u32 tmhtcr; /* monitor high temperature capture register */ | |
78 | u32 tmltcr; /* monitor low temperature capture register */ | |
79 | u32 tmrtrcr; /* monitor rising temperature rate capture register */ | |
80 | u32 tmftrcr; /* monitor falling temperature rate capture register */ | |
81 | u32 tmhtitr; /* High Temperature Immediate Threshold */ | |
82 | u32 tmhtatr; /* High Temperature Average Threshold */ | |
83 | u32 tmhtactr; /* High Temperature Average Crit Threshold */ | |
84 | u32 res3; | |
85 | u32 tmltitr; /* monitor low temperature immediate threshold */ | |
86 | u32 tmltatr; /* monitor low temperature average threshold register */ | |
87 | u32 tmltactr; /* monitor low temperature average critical threshold */ | |
88 | u32 res4; | |
89 | u32 tmrtrctr; /* monitor rising temperature rate critical threshold */ | |
90 | u32 tmftrctr; /* monitor falling temperature rate critical threshold*/ | |
91 | u8 res5[0x8]; | |
92 | u32 ttcfgr; /* Temperature Configuration Register */ | |
93 | u32 tscfgr; /* Sensor Configuration Register */ | |
94 | u8 res6[0x78]; | |
95 | struct qoriq_tmu_site_regs site[SITES_MAX]; | |
96 | u8 res7[0x9f8]; | |
97 | u32 ipbrr0; /* IP Block Revision Register 0 */ | |
98 | u32 ipbrr1; /* IP Block Revision Register 1 */ | |
99 | u8 res8[0x300]; | |
100 | u32 teumr0; | |
101 | u32 teumr1; | |
102 | u32 teumr2; | |
103 | u32 res9; | |
104 | u32 ttrcr[4]; /* Temperature Range Control Register */ | |
43528445 JH |
105 | }; |
106 | ||
7797ff42 YT |
107 | struct qoriq_tmu_data; |
108 | ||
43528445 JH |
109 | /* |
110 | * Thermal zone data | |
111 | */ | |
7797ff42 | 112 | struct qoriq_sensor { |
7797ff42 YT |
113 | struct qoriq_tmu_data *qdata; |
114 | int id; | |
115 | }; | |
116 | ||
43528445 | 117 | struct qoriq_tmu_data { |
9809797b YT |
118 | int ver; |
119 | struct qoriq_tmu_regs_v1 __iomem *regs; | |
120 | struct qoriq_tmu_regs_v2 __iomem *regs_v2; | |
51904045 | 121 | struct clk *clk; |
43528445 | 122 | bool little_endian; |
7797ff42 | 123 | struct qoriq_sensor *sensor[SITES_MAX]; |
43528445 JH |
124 | }; |
125 | ||
126 | static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) | |
127 | { | |
128 | if (p->little_endian) | |
129 | iowrite32(val, addr); | |
130 | else | |
131 | iowrite32be(val, addr); | |
132 | } | |
133 | ||
134 | static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) | |
135 | { | |
136 | if (p->little_endian) | |
137 | return ioread32(addr); | |
138 | else | |
139 | return ioread32be(addr); | |
140 | } | |
141 | ||
142 | static int tmu_get_temp(void *p, int *temp) | |
143 | { | |
7797ff42 YT |
144 | struct qoriq_sensor *qsensor = p; |
145 | struct qoriq_tmu_data *qdata = qsensor->qdata; | |
43528445 | 146 | u32 val; |
43528445 | 147 | |
7797ff42 | 148 | val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); |
43528445 JH |
149 | *temp = (val & 0xff) * 1000; |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
7797ff42 YT |
154 | static const struct thermal_zone_of_device_ops tmu_tz_ops = { |
155 | .get_temp = tmu_get_temp, | |
156 | }; | |
43528445 | 157 | |
7797ff42 YT |
158 | static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) |
159 | { | |
160 | struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); | |
161 | int id, sites = 0; | |
162 | ||
163 | for (id = 0; id < SITES_MAX; id++) { | |
11ef00f7 | 164 | struct thermal_zone_device *tzd; |
d6fb0564 | 165 | struct qoriq_sensor *sensor; |
11ef00f7 AS |
166 | int ret; |
167 | ||
d6fb0564 AS |
168 | sensor = devm_kzalloc(&pdev->dev, |
169 | sizeof(struct qoriq_sensor), | |
170 | GFP_KERNEL); | |
7797ff42 YT |
171 | if (!qdata->sensor[id]) |
172 | return -ENOMEM; | |
173 | ||
d6fb0564 AS |
174 | qdata->sensor[id] = sensor; |
175 | ||
176 | sensor->id = id; | |
177 | sensor->qdata = qdata; | |
11ef00f7 AS |
178 | |
179 | tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, id, | |
d6fb0564 | 180 | sensor, |
11ef00f7 AS |
181 | &tmu_tz_ops); |
182 | ret = PTR_ERR_OR_ZERO(tzd); | |
183 | if (ret) { | |
184 | if (ret == -ENODEV) | |
7797ff42 YT |
185 | continue; |
186 | else | |
11ef00f7 | 187 | return ret; |
7797ff42 YT |
188 | } |
189 | ||
9809797b YT |
190 | if (qdata->ver == TMU_VER1) |
191 | sites |= 0x1 << (15 - id); | |
192 | else | |
193 | sites |= 0x1 << id; | |
43528445 JH |
194 | } |
195 | ||
7797ff42 | 196 | /* Enable monitoring */ |
9809797b YT |
197 | if (sites != 0) { |
198 | if (qdata->ver == TMU_VER1) { | |
199 | tmu_write(qdata, sites | TMR_ME | TMR_ALPF, | |
200 | &qdata->regs->tmr); | |
201 | } else { | |
202 | tmu_write(qdata, sites, &qdata->regs_v2->tmsr); | |
203 | tmu_write(qdata, TMR_ME | TMR_ALPF_V2, | |
204 | &qdata->regs_v2->tmr); | |
205 | } | |
206 | } | |
43528445 | 207 | |
7797ff42 | 208 | return 0; |
43528445 JH |
209 | } |
210 | ||
211 | static int qoriq_tmu_calibration(struct platform_device *pdev) | |
212 | { | |
213 | int i, val, len; | |
214 | u32 range[4]; | |
215 | const u32 *calibration; | |
216 | struct device_node *np = pdev->dev.of_node; | |
217 | struct qoriq_tmu_data *data = platform_get_drvdata(pdev); | |
218 | ||
9809797b YT |
219 | len = of_property_count_u32_elems(np, "fsl,tmu-range"); |
220 | if (len < 0 || len > 4) { | |
221 | dev_err(&pdev->dev, "invalid range data.\n"); | |
222 | return len; | |
223 | } | |
224 | ||
225 | val = of_property_read_u32_array(np, "fsl,tmu-range", range, len); | |
226 | if (val != 0) { | |
227 | dev_err(&pdev->dev, "failed to read range data.\n"); | |
228 | return val; | |
43528445 JH |
229 | } |
230 | ||
231 | /* Init temperature range registers */ | |
9809797b YT |
232 | for (i = 0; i < len; i++) |
233 | tmu_write(data, range[i], &data->regs->ttrcr[i]); | |
43528445 JH |
234 | |
235 | calibration = of_get_property(np, "fsl,tmu-calibration", &len); | |
236 | if (calibration == NULL || len % 8) { | |
237 | dev_err(&pdev->dev, "invalid calibration data.\n"); | |
238 | return -ENODEV; | |
239 | } | |
240 | ||
241 | for (i = 0; i < len; i += 8, calibration += 2) { | |
242 | val = of_read_number(calibration, 1); | |
243 | tmu_write(data, val, &data->regs->ttcfgr); | |
244 | val = of_read_number(calibration + 1, 1); | |
245 | tmu_write(data, val, &data->regs->tscfgr); | |
246 | } | |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
251 | static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) | |
252 | { | |
253 | /* Disable interrupt, using polling instead */ | |
254 | tmu_write(data, TIER_DISABLE, &data->regs->tier); | |
255 | ||
256 | /* Set update_interval */ | |
9809797b YT |
257 | if (data->ver == TMU_VER1) { |
258 | tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); | |
259 | } else { | |
260 | tmu_write(data, TMTMIR_DEFAULT, &data->regs_v2->tmtmir); | |
261 | tmu_write(data, TEUMR0_V2, &data->regs_v2->teumr0); | |
262 | } | |
43528445 JH |
263 | |
264 | /* Disable monitoring */ | |
265 | tmu_write(data, TMR_DISABLE, &data->regs->tmr); | |
266 | } | |
267 | ||
43528445 JH |
268 | static int qoriq_tmu_probe(struct platform_device *pdev) |
269 | { | |
270 | int ret; | |
9809797b | 271 | u32 ver; |
43528445 JH |
272 | struct qoriq_tmu_data *data; |
273 | struct device_node *np = pdev->dev.of_node; | |
e167dc43 | 274 | struct device *dev = &pdev->dev; |
43528445 | 275 | |
e167dc43 | 276 | data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data), |
43528445 JH |
277 | GFP_KERNEL); |
278 | if (!data) | |
279 | return -ENOMEM; | |
280 | ||
281 | platform_set_drvdata(pdev, data); | |
282 | ||
283 | data->little_endian = of_property_read_bool(np, "little-endian"); | |
284 | ||
4d82000a AH |
285 | data->regs = devm_platform_ioremap_resource(pdev, 0); |
286 | if (IS_ERR(data->regs)) { | |
e167dc43 | 287 | dev_err(dev, "Failed to get memory region\n"); |
4d82000a | 288 | return PTR_ERR(data->regs); |
43528445 JH |
289 | } |
290 | ||
e167dc43 | 291 | data->clk = devm_clk_get_optional(dev, NULL); |
51904045 AH |
292 | if (IS_ERR(data->clk)) |
293 | return PTR_ERR(data->clk); | |
294 | ||
295 | ret = clk_prepare_enable(data->clk); | |
296 | if (ret) { | |
e167dc43 | 297 | dev_err(dev, "Failed to enable clock\n"); |
51904045 AH |
298 | return ret; |
299 | } | |
300 | ||
9809797b YT |
301 | /* version register offset at: 0xbf8 on both v1 and v2 */ |
302 | ver = tmu_read(data, &data->regs->ipbrr0); | |
303 | data->ver = (ver >> 8) & 0xff; | |
304 | if (data->ver == TMU_VER2) | |
305 | data->regs_v2 = (void __iomem *)data->regs; | |
306 | ||
43528445 JH |
307 | qoriq_tmu_init_device(data); /* TMU initialization */ |
308 | ||
309 | ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ | |
310 | if (ret < 0) | |
4d82000a | 311 | goto err; |
43528445 | 312 | |
7797ff42 YT |
313 | ret = qoriq_tmu_register_tmu_zone(pdev); |
314 | if (ret < 0) { | |
e167dc43 | 315 | dev_err(dev, "Failed to register sensors\n"); |
7797ff42 | 316 | ret = -ENODEV; |
4d82000a | 317 | goto err; |
43528445 JH |
318 | } |
319 | ||
43528445 JH |
320 | return 0; |
321 | ||
4d82000a | 322 | err: |
51904045 | 323 | clk_disable_unprepare(data->clk); |
43528445 JH |
324 | platform_set_drvdata(pdev, NULL); |
325 | ||
326 | return ret; | |
327 | } | |
328 | ||
329 | static int qoriq_tmu_remove(struct platform_device *pdev) | |
330 | { | |
331 | struct qoriq_tmu_data *data = platform_get_drvdata(pdev); | |
332 | ||
43528445 JH |
333 | /* Disable monitoring */ |
334 | tmu_write(data, TMR_DISABLE, &data->regs->tmr); | |
335 | ||
51904045 AH |
336 | clk_disable_unprepare(data->clk); |
337 | ||
43528445 JH |
338 | platform_set_drvdata(pdev, NULL); |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
aea59197 | 343 | static int __maybe_unused qoriq_tmu_suspend(struct device *dev) |
43528445 JH |
344 | { |
345 | u32 tmr; | |
346 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); | |
347 | ||
348 | /* Disable monitoring */ | |
349 | tmr = tmu_read(data, &data->regs->tmr); | |
350 | tmr &= ~TMR_ME; | |
351 | tmu_write(data, tmr, &data->regs->tmr); | |
352 | ||
51904045 AH |
353 | clk_disable_unprepare(data->clk); |
354 | ||
43528445 JH |
355 | return 0; |
356 | } | |
357 | ||
aea59197 | 358 | static int __maybe_unused qoriq_tmu_resume(struct device *dev) |
43528445 JH |
359 | { |
360 | u32 tmr; | |
51904045 | 361 | int ret; |
43528445 JH |
362 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
363 | ||
51904045 AH |
364 | ret = clk_prepare_enable(data->clk); |
365 | if (ret) | |
366 | return ret; | |
367 | ||
43528445 JH |
368 | /* Enable monitoring */ |
369 | tmr = tmu_read(data, &data->regs->tmr); | |
370 | tmr |= TMR_ME; | |
371 | tmu_write(data, tmr, &data->regs->tmr); | |
372 | ||
373 | return 0; | |
374 | } | |
43528445 JH |
375 | |
376 | static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, | |
377 | qoriq_tmu_suspend, qoriq_tmu_resume); | |
378 | ||
379 | static const struct of_device_id qoriq_tmu_match[] = { | |
380 | { .compatible = "fsl,qoriq-tmu", }, | |
6017e2a9 | 381 | { .compatible = "fsl,imx8mq-tmu", }, |
43528445 JH |
382 | {}, |
383 | }; | |
384 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); | |
385 | ||
386 | static struct platform_driver qoriq_tmu = { | |
387 | .driver = { | |
388 | .name = "qoriq_thermal", | |
389 | .pm = &qoriq_tmu_pm_ops, | |
390 | .of_match_table = qoriq_tmu_match, | |
391 | }, | |
392 | .probe = qoriq_tmu_probe, | |
393 | .remove = qoriq_tmu_remove, | |
394 | }; | |
395 | module_platform_driver(qoriq_tmu); | |
396 | ||
397 | MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>"); | |
398 | MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver"); | |
399 | MODULE_LICENSE("GPL v2"); |