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