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