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