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