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 | ||
16 | #define SITES_MAX 16 | |
17 | ||
18 | /* | |
19 | * QorIQ TMU Registers | |
20 | */ | |
21 | struct qoriq_tmu_site_regs { | |
22 | u32 tritsr; /* Immediate Temperature Site Register */ | |
23 | u32 tratsr; /* Average Temperature Site Register */ | |
24 | u8 res0[0x8]; | |
25 | }; | |
26 | ||
27 | struct qoriq_tmu_regs { | |
28 | u32 tmr; /* Mode Register */ | |
29 | #define TMR_DISABLE 0x0 | |
30 | #define TMR_ME 0x80000000 | |
31 | #define TMR_ALPF 0x0c000000 | |
32 | u32 tsr; /* Status Register */ | |
33 | u32 tmtmir; /* Temperature measurement interval Register */ | |
34 | #define TMTMIR_DEFAULT 0x0000000f | |
35 | u8 res0[0x14]; | |
36 | u32 tier; /* Interrupt Enable Register */ | |
37 | #define TIER_DISABLE 0x0 | |
38 | u32 tidr; /* Interrupt Detect Register */ | |
39 | u32 tiscr; /* Interrupt Site Capture Register */ | |
40 | u32 ticscr; /* Interrupt Critical Site Capture Register */ | |
41 | u8 res1[0x10]; | |
42 | u32 tmhtcrh; /* High Temperature Capture Register */ | |
43 | u32 tmhtcrl; /* Low Temperature Capture Register */ | |
44 | u8 res2[0x8]; | |
45 | u32 tmhtitr; /* High Temperature Immediate Threshold */ | |
46 | u32 tmhtatr; /* High Temperature Average Threshold */ | |
47 | u32 tmhtactr; /* High Temperature Average Crit Threshold */ | |
48 | u8 res3[0x24]; | |
49 | u32 ttcfgr; /* Temperature Configuration Register */ | |
50 | u32 tscfgr; /* Sensor Configuration Register */ | |
51 | u8 res4[0x78]; | |
52 | struct qoriq_tmu_site_regs site[SITES_MAX]; | |
53 | u8 res5[0x9f8]; | |
54 | u32 ipbrr0; /* IP Block Revision Register 0 */ | |
55 | u32 ipbrr1; /* IP Block Revision Register 1 */ | |
56 | u8 res6[0x310]; | |
57 | u32 ttr0cr; /* Temperature Range 0 Control Register */ | |
58 | u32 ttr1cr; /* Temperature Range 1 Control Register */ | |
59 | u32 ttr2cr; /* Temperature Range 2 Control Register */ | |
60 | u32 ttr3cr; /* Temperature Range 3 Control Register */ | |
61 | }; | |
62 | ||
7797ff42 YT |
63 | struct qoriq_tmu_data; |
64 | ||
43528445 JH |
65 | /* |
66 | * Thermal zone data | |
67 | */ | |
7797ff42 YT |
68 | struct qoriq_sensor { |
69 | struct thermal_zone_device *tzd; | |
70 | struct qoriq_tmu_data *qdata; | |
71 | int id; | |
72 | }; | |
73 | ||
43528445 | 74 | struct qoriq_tmu_data { |
43528445 | 75 | struct qoriq_tmu_regs __iomem *regs; |
51904045 | 76 | struct clk *clk; |
43528445 | 77 | bool little_endian; |
7797ff42 | 78 | struct qoriq_sensor *sensor[SITES_MAX]; |
43528445 JH |
79 | }; |
80 | ||
81 | static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) | |
82 | { | |
83 | if (p->little_endian) | |
84 | iowrite32(val, addr); | |
85 | else | |
86 | iowrite32be(val, addr); | |
87 | } | |
88 | ||
89 | static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) | |
90 | { | |
91 | if (p->little_endian) | |
92 | return ioread32(addr); | |
93 | else | |
94 | return ioread32be(addr); | |
95 | } | |
96 | ||
97 | static int tmu_get_temp(void *p, int *temp) | |
98 | { | |
7797ff42 YT |
99 | struct qoriq_sensor *qsensor = p; |
100 | struct qoriq_tmu_data *qdata = qsensor->qdata; | |
43528445 | 101 | u32 val; |
43528445 | 102 | |
7797ff42 | 103 | val = tmu_read(qdata, &qdata->regs->site[qsensor->id].tritsr); |
43528445 JH |
104 | *temp = (val & 0xff) * 1000; |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
7797ff42 YT |
109 | static const struct thermal_zone_of_device_ops tmu_tz_ops = { |
110 | .get_temp = tmu_get_temp, | |
111 | }; | |
43528445 | 112 | |
7797ff42 YT |
113 | static int qoriq_tmu_register_tmu_zone(struct platform_device *pdev) |
114 | { | |
115 | struct qoriq_tmu_data *qdata = platform_get_drvdata(pdev); | |
116 | int id, sites = 0; | |
117 | ||
118 | for (id = 0; id < SITES_MAX; id++) { | |
119 | qdata->sensor[id] = devm_kzalloc(&pdev->dev, | |
120 | sizeof(struct qoriq_sensor), GFP_KERNEL); | |
121 | if (!qdata->sensor[id]) | |
122 | return -ENOMEM; | |
123 | ||
124 | qdata->sensor[id]->id = id; | |
125 | qdata->sensor[id]->qdata = qdata; | |
126 | qdata->sensor[id]->tzd = devm_thermal_zone_of_sensor_register( | |
127 | &pdev->dev, id, qdata->sensor[id], &tmu_tz_ops); | |
128 | if (IS_ERR(qdata->sensor[id]->tzd)) { | |
129 | if (PTR_ERR(qdata->sensor[id]->tzd) == -ENODEV) | |
130 | continue; | |
131 | else | |
132 | return PTR_ERR(qdata->sensor[id]->tzd); | |
133 | } | |
134 | ||
135 | sites |= 0x1 << (15 - id); | |
43528445 JH |
136 | } |
137 | ||
7797ff42 YT |
138 | /* Enable monitoring */ |
139 | if (sites != 0) | |
140 | tmu_write(qdata, sites | TMR_ME | TMR_ALPF, &qdata->regs->tmr); | |
43528445 | 141 | |
7797ff42 | 142 | return 0; |
43528445 JH |
143 | } |
144 | ||
145 | static int qoriq_tmu_calibration(struct platform_device *pdev) | |
146 | { | |
147 | int i, val, len; | |
148 | u32 range[4]; | |
149 | const u32 *calibration; | |
150 | struct device_node *np = pdev->dev.of_node; | |
151 | struct qoriq_tmu_data *data = platform_get_drvdata(pdev); | |
152 | ||
153 | if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) { | |
154 | dev_err(&pdev->dev, "missing calibration range.\n"); | |
155 | return -ENODEV; | |
156 | } | |
157 | ||
158 | /* Init temperature range registers */ | |
159 | tmu_write(data, range[0], &data->regs->ttr0cr); | |
160 | tmu_write(data, range[1], &data->regs->ttr1cr); | |
161 | tmu_write(data, range[2], &data->regs->ttr2cr); | |
162 | tmu_write(data, range[3], &data->regs->ttr3cr); | |
163 | ||
164 | calibration = of_get_property(np, "fsl,tmu-calibration", &len); | |
165 | if (calibration == NULL || len % 8) { | |
166 | dev_err(&pdev->dev, "invalid calibration data.\n"); | |
167 | return -ENODEV; | |
168 | } | |
169 | ||
170 | for (i = 0; i < len; i += 8, calibration += 2) { | |
171 | val = of_read_number(calibration, 1); | |
172 | tmu_write(data, val, &data->regs->ttcfgr); | |
173 | val = of_read_number(calibration + 1, 1); | |
174 | tmu_write(data, val, &data->regs->tscfgr); | |
175 | } | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) | |
181 | { | |
182 | /* Disable interrupt, using polling instead */ | |
183 | tmu_write(data, TIER_DISABLE, &data->regs->tier); | |
184 | ||
185 | /* Set update_interval */ | |
186 | tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); | |
187 | ||
188 | /* Disable monitoring */ | |
189 | tmu_write(data, TMR_DISABLE, &data->regs->tmr); | |
190 | } | |
191 | ||
43528445 JH |
192 | static int qoriq_tmu_probe(struct platform_device *pdev) |
193 | { | |
194 | int ret; | |
43528445 JH |
195 | struct qoriq_tmu_data *data; |
196 | struct device_node *np = pdev->dev.of_node; | |
43528445 | 197 | |
43528445 JH |
198 | data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), |
199 | GFP_KERNEL); | |
200 | if (!data) | |
201 | return -ENOMEM; | |
202 | ||
203 | platform_set_drvdata(pdev, data); | |
204 | ||
205 | data->little_endian = of_property_read_bool(np, "little-endian"); | |
206 | ||
4d82000a AH |
207 | data->regs = devm_platform_ioremap_resource(pdev, 0); |
208 | if (IS_ERR(data->regs)) { | |
43528445 | 209 | dev_err(&pdev->dev, "Failed to get memory region\n"); |
4d82000a | 210 | return PTR_ERR(data->regs); |
43528445 JH |
211 | } |
212 | ||
51904045 AH |
213 | data->clk = devm_clk_get_optional(&pdev->dev, NULL); |
214 | if (IS_ERR(data->clk)) | |
215 | return PTR_ERR(data->clk); | |
216 | ||
217 | ret = clk_prepare_enable(data->clk); | |
218 | if (ret) { | |
219 | dev_err(&pdev->dev, "Failed to enable clock\n"); | |
220 | return ret; | |
221 | } | |
222 | ||
43528445 JH |
223 | qoriq_tmu_init_device(data); /* TMU initialization */ |
224 | ||
225 | ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ | |
226 | if (ret < 0) | |
4d82000a | 227 | goto err; |
43528445 | 228 | |
7797ff42 YT |
229 | ret = qoriq_tmu_register_tmu_zone(pdev); |
230 | if (ret < 0) { | |
231 | dev_err(&pdev->dev, "Failed to register sensors\n"); | |
232 | ret = -ENODEV; | |
4d82000a | 233 | goto err; |
43528445 JH |
234 | } |
235 | ||
43528445 JH |
236 | return 0; |
237 | ||
4d82000a | 238 | err: |
51904045 | 239 | clk_disable_unprepare(data->clk); |
43528445 JH |
240 | platform_set_drvdata(pdev, NULL); |
241 | ||
242 | return ret; | |
243 | } | |
244 | ||
245 | static int qoriq_tmu_remove(struct platform_device *pdev) | |
246 | { | |
247 | struct qoriq_tmu_data *data = platform_get_drvdata(pdev); | |
248 | ||
43528445 JH |
249 | /* Disable monitoring */ |
250 | tmu_write(data, TMR_DISABLE, &data->regs->tmr); | |
251 | ||
51904045 AH |
252 | clk_disable_unprepare(data->clk); |
253 | ||
43528445 JH |
254 | platform_set_drvdata(pdev, NULL); |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | #ifdef CONFIG_PM_SLEEP | |
260 | static int qoriq_tmu_suspend(struct device *dev) | |
261 | { | |
262 | u32 tmr; | |
263 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); | |
264 | ||
265 | /* Disable monitoring */ | |
266 | tmr = tmu_read(data, &data->regs->tmr); | |
267 | tmr &= ~TMR_ME; | |
268 | tmu_write(data, tmr, &data->regs->tmr); | |
269 | ||
51904045 AH |
270 | clk_disable_unprepare(data->clk); |
271 | ||
43528445 JH |
272 | return 0; |
273 | } | |
274 | ||
275 | static int qoriq_tmu_resume(struct device *dev) | |
276 | { | |
277 | u32 tmr; | |
51904045 | 278 | int ret; |
43528445 JH |
279 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
280 | ||
51904045 AH |
281 | ret = clk_prepare_enable(data->clk); |
282 | if (ret) | |
283 | return ret; | |
284 | ||
43528445 JH |
285 | /* Enable monitoring */ |
286 | tmr = tmu_read(data, &data->regs->tmr); | |
287 | tmr |= TMR_ME; | |
288 | tmu_write(data, tmr, &data->regs->tmr); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | #endif | |
293 | ||
294 | static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, | |
295 | qoriq_tmu_suspend, qoriq_tmu_resume); | |
296 | ||
297 | static const struct of_device_id qoriq_tmu_match[] = { | |
298 | { .compatible = "fsl,qoriq-tmu", }, | |
6017e2a9 | 299 | { .compatible = "fsl,imx8mq-tmu", }, |
43528445 JH |
300 | {}, |
301 | }; | |
302 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); | |
303 | ||
304 | static struct platform_driver qoriq_tmu = { | |
305 | .driver = { | |
306 | .name = "qoriq_thermal", | |
307 | .pm = &qoriq_tmu_pm_ops, | |
308 | .of_match_table = qoriq_tmu_match, | |
309 | }, | |
310 | .probe = qoriq_tmu_probe, | |
311 | .remove = qoriq_tmu_remove, | |
312 | }; | |
313 | module_platform_driver(qoriq_tmu); | |
314 | ||
315 | MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>"); | |
316 | MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver"); | |
317 | MODULE_LICENSE("GPL v2"); |