1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright 2016 Freescale Semiconductor, Inc. |
4 | |
5 | #include <linux/clk.h> |
6 | #include <linux/err.h> |
7 | #include <linux/io.h> |
8 | #include <linux/module.h> |
9 | #include <linux/of.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/regmap.h> |
12 | #include <linux/sizes.h> |
13 | #include <linux/thermal.h> |
14 | #include <linux/units.h> |
15 | |
16 | #include "thermal_hwmon.h" |
17 | |
18 | #define SITES_MAX 16 |
19 | #define TMR_DISABLE 0x0 |
20 | #define TMR_ME 0x80000000 |
21 | #define TMR_ALPF 0x0c000000 |
22 | #define TMR_ALPF_V2 0x03000000 |
23 | #define TMTMIR_DEFAULT 0x0000000f |
24 | #define TIER_DISABLE 0x0 |
25 | #define TEUMR0_V2 0x51009c00 |
26 | #define TMSARA_V2 0xe |
27 | #define TMU_VER1 0x1 |
28 | #define TMU_VER2 0x2 |
29 | |
30 | #define REGS_TMR 0x000 /* Mode Register */ |
31 | #define TMR_DISABLE 0x0 |
32 | #define TMR_ME 0x80000000 |
33 | #define TMR_ALPF 0x0c000000 |
34 | |
35 | #define REGS_TMTMIR 0x008 /* Temperature measurement interval Register */ |
36 | #define TMTMIR_DEFAULT 0x0000000f |
37 | |
38 | #define REGS_V2_TMSR 0x008 /* monitor site register */ |
39 | |
40 | #define REGS_V2_TMTMIR 0x00c /* Temperature measurement interval Register */ |
41 | |
42 | #define REGS_TIER 0x020 /* Interrupt Enable Register */ |
43 | #define TIER_DISABLE 0x0 |
44 | |
45 | |
46 | #define REGS_TTCFGR 0x080 /* Temperature Configuration Register */ |
47 | #define REGS_TSCFGR 0x084 /* Sensor Configuration Register */ |
48 | |
49 | #define REGS_TRITSR(n) (0x100 + 16 * (n)) /* Immediate Temperature |
50 | * Site Register |
51 | */ |
52 | #define TRITSR_V BIT(31) |
53 | #define TRITSR_TP5 BIT(9) |
54 | #define REGS_V2_TMSAR(n) (0x304 + 16 * (n)) /* TMU monitoring |
55 | * site adjustment register |
56 | */ |
57 | #define REGS_TTRnCR(n) (0xf10 + 4 * (n)) /* Temperature Range n |
58 | * Control Register |
59 | */ |
60 | #define REGS_IPBRR(n) (0xbf8 + 4 * (n)) /* IP Block Revision |
61 | * Register n |
62 | */ |
63 | #define REGS_V2_TEUMR(n) (0xf00 + 4 * (n)) |
64 | |
65 | /* |
66 | * Thermal zone data |
67 | */ |
68 | struct qoriq_sensor { |
69 | int id; |
70 | }; |
71 | |
72 | struct qoriq_tmu_data { |
73 | int ver; |
74 | struct regmap *regmap; |
75 | struct clk *clk; |
76 | struct qoriq_sensor sensor[SITES_MAX]; |
77 | }; |
78 | |
79 | static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s) |
80 | { |
81 | return container_of(s, struct qoriq_tmu_data, sensor[s->id]); |
82 | } |
83 | |
84 | static int tmu_get_temp(struct thermal_zone_device *tz, int *temp) |
85 | { |
86 | struct qoriq_sensor *qsensor = thermal_zone_device_priv(tzd: tz); |
87 | struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(s: qsensor); |
88 | u32 val; |
89 | /* |
90 | * REGS_TRITSR(id) has the following layout: |
91 | * |
92 | * For TMU Rev1: |
93 | * 31 ... 7 6 5 4 3 2 1 0 |
94 | * V TEMP |
95 | * |
96 | * Where V bit signifies if the measurement is ready and is |
97 | * within sensor range. TEMP is an 8 bit value representing |
98 | * temperature in Celsius. |
99 | |
100 | * For TMU Rev2: |
101 | * 31 ... 8 7 6 5 4 3 2 1 0 |
102 | * V TEMP |
103 | * |
104 | * Where V bit signifies if the measurement is ready and is |
105 | * within sensor range. TEMP is an 9 bit value representing |
106 | * temperature in KelVin. |
107 | */ |
108 | |
109 | regmap_read(map: qdata->regmap, REGS_TMR, val: &val); |
110 | if (!(val & TMR_ME)) |
111 | return -EAGAIN; |
112 | |
113 | if (regmap_read_poll_timeout(qdata->regmap, |
114 | REGS_TRITSR(qsensor->id), |
115 | val, |
116 | val & TRITSR_V, |
117 | USEC_PER_MSEC, |
118 | 10 * USEC_PER_MSEC)) |
119 | return -ENODATA; |
120 | |
121 | if (qdata->ver == TMU_VER1) { |
122 | *temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE; |
123 | } else { |
124 | if (val & TRITSR_TP5) |
125 | *temp = milli_kelvin_to_millicelsius(t: (val & GENMASK(8, 0)) * |
126 | MILLIDEGREE_PER_DEGREE + 500); |
127 | else |
128 | *temp = kelvin_to_millicelsius(t: val & GENMASK(8, 0)); |
129 | } |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static const struct thermal_zone_device_ops tmu_tz_ops = { |
135 | .get_temp = tmu_get_temp, |
136 | }; |
137 | |
138 | static int qoriq_tmu_register_tmu_zone(struct device *dev, |
139 | struct qoriq_tmu_data *qdata) |
140 | { |
141 | int id, sites = 0; |
142 | |
143 | for (id = 0; id < SITES_MAX; id++) { |
144 | struct thermal_zone_device *tzd; |
145 | struct qoriq_sensor *sensor = &qdata->sensor[id]; |
146 | int ret; |
147 | |
148 | sensor->id = id; |
149 | |
150 | tzd = devm_thermal_of_zone_register(dev, id, |
151 | data: sensor, |
152 | ops: &tmu_tz_ops); |
153 | ret = PTR_ERR_OR_ZERO(ptr: tzd); |
154 | if (ret) { |
155 | if (ret == -ENODEV) |
156 | continue; |
157 | |
158 | return ret; |
159 | } |
160 | |
161 | if (qdata->ver == TMU_VER1) |
162 | sites |= 0x1 << (15 - id); |
163 | else |
164 | sites |= 0x1 << id; |
165 | |
166 | devm_thermal_add_hwmon_sysfs(dev, tz: tzd); |
167 | } |
168 | |
169 | if (sites) { |
170 | if (qdata->ver == TMU_VER1) { |
171 | regmap_write(map: qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF | sites); |
172 | } else { |
173 | regmap_write(map: qdata->regmap, REGS_V2_TMSR, val: sites); |
174 | regmap_write(map: qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2); |
175 | } |
176 | } |
177 | |
178 | return 0; |
179 | } |
180 | |
181 | static int qoriq_tmu_calibration(struct device *dev, |
182 | struct qoriq_tmu_data *data) |
183 | { |
184 | int i, val, len; |
185 | u32 range[4]; |
186 | const u32 *calibration; |
187 | struct device_node *np = dev->of_node; |
188 | |
189 | len = of_property_count_u32_elems(np, propname: "fsl,tmu-range" ); |
190 | if (len < 0 || len > 4) { |
191 | dev_err(dev, "invalid range data.\n" ); |
192 | return len; |
193 | } |
194 | |
195 | val = of_property_read_u32_array(np, propname: "fsl,tmu-range" , out_values: range, sz: len); |
196 | if (val != 0) { |
197 | dev_err(dev, "failed to read range data.\n" ); |
198 | return val; |
199 | } |
200 | |
201 | /* Init temperature range registers */ |
202 | for (i = 0; i < len; i++) |
203 | regmap_write(map: data->regmap, REGS_TTRnCR(i), val: range[i]); |
204 | |
205 | calibration = of_get_property(node: np, name: "fsl,tmu-calibration" , lenp: &len); |
206 | if (calibration == NULL || len % 8) { |
207 | dev_err(dev, "invalid calibration data.\n" ); |
208 | return -ENODEV; |
209 | } |
210 | |
211 | for (i = 0; i < len; i += 8, calibration += 2) { |
212 | val = of_read_number(cell: calibration, size: 1); |
213 | regmap_write(map: data->regmap, REGS_TTCFGR, val); |
214 | val = of_read_number(cell: calibration + 1, size: 1); |
215 | regmap_write(map: data->regmap, REGS_TSCFGR, val); |
216 | } |
217 | |
218 | return 0; |
219 | } |
220 | |
221 | static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) |
222 | { |
223 | /* Disable interrupt, using polling instead */ |
224 | regmap_write(map: data->regmap, REGS_TIER, TIER_DISABLE); |
225 | |
226 | /* Set update_interval */ |
227 | |
228 | if (data->ver == TMU_VER1) { |
229 | regmap_write(map: data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT); |
230 | } else { |
231 | regmap_write(map: data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT); |
232 | regmap_write(map: data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2); |
233 | } |
234 | |
235 | /* Disable monitoring */ |
236 | regmap_write(map: data->regmap, REGS_TMR, TMR_DISABLE); |
237 | } |
238 | |
239 | static const struct regmap_range qoriq_yes_ranges[] = { |
240 | regmap_reg_range(REGS_TMR, REGS_TSCFGR), |
241 | regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(15)), |
242 | regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)), |
243 | regmap_reg_range(REGS_V2_TMSAR(0), REGS_V2_TMSAR(15)), |
244 | regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)), |
245 | /* Read only registers below */ |
246 | regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)), |
247 | }; |
248 | |
249 | static const struct regmap_access_table qoriq_wr_table = { |
250 | .yes_ranges = qoriq_yes_ranges, |
251 | .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges) - 1, |
252 | }; |
253 | |
254 | static const struct regmap_access_table qoriq_rd_table = { |
255 | .yes_ranges = qoriq_yes_ranges, |
256 | .n_yes_ranges = ARRAY_SIZE(qoriq_yes_ranges), |
257 | }; |
258 | |
259 | static void qoriq_tmu_action(void *p) |
260 | { |
261 | struct qoriq_tmu_data *data = p; |
262 | |
263 | regmap_write(map: data->regmap, REGS_TMR, TMR_DISABLE); |
264 | clk_disable_unprepare(clk: data->clk); |
265 | } |
266 | |
267 | static int qoriq_tmu_probe(struct platform_device *pdev) |
268 | { |
269 | int ret; |
270 | u32 ver; |
271 | struct qoriq_tmu_data *data; |
272 | struct device_node *np = pdev->dev.of_node; |
273 | struct device *dev = &pdev->dev; |
274 | const bool little_endian = of_property_read_bool(np, propname: "little-endian" ); |
275 | const enum regmap_endian format_endian = |
276 | little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG; |
277 | const struct regmap_config regmap_config = { |
278 | .reg_bits = 32, |
279 | .val_bits = 32, |
280 | .reg_stride = 4, |
281 | .rd_table = &qoriq_rd_table, |
282 | .wr_table = &qoriq_wr_table, |
283 | .val_format_endian = format_endian, |
284 | .max_register = SZ_4K, |
285 | }; |
286 | void __iomem *base; |
287 | |
288 | data = devm_kzalloc(dev, size: sizeof(struct qoriq_tmu_data), |
289 | GFP_KERNEL); |
290 | if (!data) |
291 | return -ENOMEM; |
292 | |
293 | base = devm_platform_ioremap_resource(pdev, index: 0); |
294 | ret = PTR_ERR_OR_ZERO(ptr: base); |
295 | if (ret) { |
296 | dev_err(dev, "Failed to get memory region\n" ); |
297 | return ret; |
298 | } |
299 | |
300 | data->regmap = devm_regmap_init_mmio(dev, base, ®map_config); |
301 | ret = PTR_ERR_OR_ZERO(ptr: data->regmap); |
302 | if (ret) { |
303 | dev_err(dev, "Failed to init regmap (%d)\n" , ret); |
304 | return ret; |
305 | } |
306 | |
307 | data->clk = devm_clk_get_optional(dev, NULL); |
308 | if (IS_ERR(ptr: data->clk)) |
309 | return PTR_ERR(ptr: data->clk); |
310 | |
311 | ret = clk_prepare_enable(clk: data->clk); |
312 | if (ret) { |
313 | dev_err(dev, "Failed to enable clock\n" ); |
314 | return ret; |
315 | } |
316 | |
317 | ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data); |
318 | if (ret) |
319 | return ret; |
320 | |
321 | /* version register offset at: 0xbf8 on both v1 and v2 */ |
322 | ret = regmap_read(map: data->regmap, REGS_IPBRR(0), val: &ver); |
323 | if (ret) { |
324 | dev_err(&pdev->dev, "Failed to read IP block version\n" ); |
325 | return ret; |
326 | } |
327 | data->ver = (ver >> 8) & 0xff; |
328 | |
329 | qoriq_tmu_init_device(data); /* TMU initialization */ |
330 | |
331 | ret = qoriq_tmu_calibration(dev, data); /* TMU calibration */ |
332 | if (ret < 0) |
333 | return ret; |
334 | |
335 | ret = qoriq_tmu_register_tmu_zone(dev, qdata: data); |
336 | if (ret < 0) { |
337 | dev_err(dev, "Failed to register sensors\n" ); |
338 | return ret; |
339 | } |
340 | |
341 | platform_set_drvdata(pdev, data); |
342 | |
343 | return 0; |
344 | } |
345 | |
346 | static int __maybe_unused qoriq_tmu_suspend(struct device *dev) |
347 | { |
348 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
349 | int ret; |
350 | |
351 | ret = regmap_update_bits(map: data->regmap, REGS_TMR, TMR_ME, val: 0); |
352 | if (ret) |
353 | return ret; |
354 | |
355 | clk_disable_unprepare(clk: data->clk); |
356 | |
357 | return 0; |
358 | } |
359 | |
360 | static int __maybe_unused qoriq_tmu_resume(struct device *dev) |
361 | { |
362 | int ret; |
363 | struct qoriq_tmu_data *data = dev_get_drvdata(dev); |
364 | |
365 | ret = clk_prepare_enable(clk: data->clk); |
366 | if (ret) |
367 | return ret; |
368 | |
369 | /* Enable monitoring */ |
370 | return regmap_update_bits(map: data->regmap, REGS_TMR, TMR_ME, TMR_ME); |
371 | } |
372 | |
373 | static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, |
374 | qoriq_tmu_suspend, qoriq_tmu_resume); |
375 | |
376 | static const struct of_device_id qoriq_tmu_match[] = { |
377 | { .compatible = "fsl,qoriq-tmu" , }, |
378 | { .compatible = "fsl,imx8mq-tmu" , }, |
379 | {}, |
380 | }; |
381 | MODULE_DEVICE_TABLE(of, qoriq_tmu_match); |
382 | |
383 | static struct platform_driver qoriq_tmu = { |
384 | .driver = { |
385 | .name = "qoriq_thermal" , |
386 | .pm = &qoriq_tmu_pm_ops, |
387 | .of_match_table = qoriq_tmu_match, |
388 | }, |
389 | .probe = qoriq_tmu_probe, |
390 | }; |
391 | module_platform_driver(qoriq_tmu); |
392 | |
393 | MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>" ); |
394 | MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver" ); |
395 | MODULE_LICENSE("GPL v2" ); |
396 | |