OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From bcb7dd9ef206f7d646ed8dac6fe7772083714253 Mon Sep 17 00:00:00 2001 |
2 | From: Stefan Wahren <stefan.wahren@i2se.com> |
||
3 | Date: Fri, 31 Mar 2017 20:03:06 +0000 |
||
4 | Subject: [PATCH] thermal: bcm2835: add thermal driver for bcm2835 SoC |
||
5 | MIME-Version: 1.0 |
||
6 | Content-Type: text/plain; charset=UTF-8 |
||
7 | Content-Transfer-Encoding: 8bit |
||
8 | |||
9 | Add basic thermal driver for bcm2835 SoC. |
||
10 | |||
11 | This driver currently make sure that tsense HW block is set up |
||
12 | correctly. |
||
13 | |||
14 | Tested-by: Rafał Miłecki <rafal@milecki.pl> |
||
15 | Signed-off-by: Martin Sperl <kernel@martin.sperl.org> |
||
16 | Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com> |
||
17 | Acked-by: Eric Anholt <eric@anholt.net> |
||
18 | Acked-by: Eduardo Valentin <edubezval@gmail.com> |
||
19 | Signed-off-by: Eduardo Valentin <edubezval@gmail.com> |
||
20 | --- |
||
21 | drivers/thermal/Kconfig | 8 + |
||
22 | drivers/thermal/Makefile | 1 + |
||
23 | drivers/thermal/bcm2835_thermal.c | 314 ++++++++++++++++++++++++++++++++++++++ |
||
24 | 3 files changed, 323 insertions(+) |
||
25 | create mode 100644 drivers/thermal/bcm2835_thermal.c |
||
26 | |||
27 | --- a/drivers/thermal/Kconfig |
||
28 | +++ b/drivers/thermal/Kconfig |
||
29 | @@ -434,4 +434,12 @@ depends on (ARCH_QCOM && OF) || COMPILE_ |
||
30 | source "drivers/thermal/qcom/Kconfig" |
||
31 | endmenu |
||
32 | |||
33 | +config BCM2835_THERMAL |
||
34 | + tristate "Thermal sensors on bcm2835 SoC" |
||
35 | + depends on ARCH_BCM2835 || COMPILE_TEST |
||
36 | + depends on HAS_IOMEM |
||
37 | + depends on THERMAL_OF |
||
38 | + help |
||
39 | + Support for thermal sensors on Broadcom bcm2835 SoCs. |
||
40 | + |
||
41 | endif |
||
42 | --- a/drivers/thermal/Makefile |
||
43 | +++ b/drivers/thermal/Makefile |
||
44 | @@ -55,3 +55,4 @@ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ |
||
45 | obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o |
||
46 | obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o |
||
47 | obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o |
||
48 | +obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o |
||
49 | --- /dev/null |
||
50 | +++ b/drivers/thermal/bcm2835_thermal.c |
||
51 | @@ -0,0 +1,314 @@ |
||
52 | +/* |
||
53 | + * Driver for Broadcom BCM2835 SoC temperature sensor |
||
54 | + * |
||
55 | + * Copyright (C) 2016 Martin Sperl |
||
56 | + * |
||
57 | + * This program is free software; you can redistribute it and/or modify |
||
58 | + * it under the terms of the GNU General Public License as published by |
||
59 | + * the Free Software Foundation; either version 2 of the License, or |
||
60 | + * (at your option) any later version. |
||
61 | + * |
||
62 | + * This program is distributed in the hope that it will be useful, |
||
63 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
64 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
65 | + * GNU General Public License for more details. |
||
66 | + */ |
||
67 | + |
||
68 | +#include <linux/clk.h> |
||
69 | +#include <linux/debugfs.h> |
||
70 | +#include <linux/device.h> |
||
71 | +#include <linux/err.h> |
||
72 | +#include <linux/io.h> |
||
73 | +#include <linux/kernel.h> |
||
74 | +#include <linux/module.h> |
||
75 | +#include <linux/of.h> |
||
76 | +#include <linux/of_address.h> |
||
77 | +#include <linux/of_device.h> |
||
78 | +#include <linux/platform_device.h> |
||
79 | +#include <linux/thermal.h> |
||
80 | + |
||
81 | +#define BCM2835_TS_TSENSCTL 0x00 |
||
82 | +#define BCM2835_TS_TSENSSTAT 0x04 |
||
83 | + |
||
84 | +#define BCM2835_TS_TSENSCTL_PRWDW BIT(0) |
||
85 | +#define BCM2835_TS_TSENSCTL_RSTB BIT(1) |
||
86 | + |
||
87 | +/* |
||
88 | + * bandgap reference voltage in 6 mV increments |
||
89 | + * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV |
||
90 | + */ |
||
91 | +#define BCM2835_TS_TSENSCTL_CTRL_BITS 3 |
||
92 | +#define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2 |
||
93 | +#define BCM2835_TS_TSENSCTL_CTRL_MASK \ |
||
94 | + GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \ |
||
95 | + BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \ |
||
96 | + BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
||
97 | +#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1 |
||
98 | +#define BCM2835_TS_TSENSCTL_EN_INT BIT(5) |
||
99 | +#define BCM2835_TS_TSENSCTL_DIRECT BIT(6) |
||
100 | +#define BCM2835_TS_TSENSCTL_CLR_INT BIT(7) |
||
101 | +#define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8 |
||
102 | +#define BCM2835_TS_TSENSCTL_THOLD_BITS 10 |
||
103 | +#define BCM2835_TS_TSENSCTL_THOLD_MASK \ |
||
104 | + GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \ |
||
105 | + BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \ |
||
106 | + BCM2835_TS_TSENSCTL_THOLD_SHIFT) |
||
107 | +/* |
||
108 | + * time how long the block to be asserted in reset |
||
109 | + * which based on a clock counter (TSENS clock assumed) |
||
110 | + */ |
||
111 | +#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18 |
||
112 | +#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8 |
||
113 | +#define BCM2835_TS_TSENSCTL_REGULEN BIT(26) |
||
114 | + |
||
115 | +#define BCM2835_TS_TSENSSTAT_DATA_BITS 10 |
||
116 | +#define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0 |
||
117 | +#define BCM2835_TS_TSENSSTAT_DATA_MASK \ |
||
118 | + GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \ |
||
119 | + BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \ |
||
120 | + BCM2835_TS_TSENSSTAT_DATA_SHIFT) |
||
121 | +#define BCM2835_TS_TSENSSTAT_VALID BIT(10) |
||
122 | +#define BCM2835_TS_TSENSSTAT_INTERRUPT BIT(11) |
||
123 | + |
||
124 | +struct bcm2835_thermal_data { |
||
125 | + struct thermal_zone_device *tz; |
||
126 | + void __iomem *regs; |
||
127 | + struct clk *clk; |
||
128 | + struct dentry *debugfsdir; |
||
129 | +}; |
||
130 | + |
||
131 | +static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope) |
||
132 | +{ |
||
133 | + return offset + slope * adc; |
||
134 | +} |
||
135 | + |
||
136 | +static int bcm2835_thermal_temp2adc(int temp, int offset, int slope) |
||
137 | +{ |
||
138 | + temp -= offset; |
||
139 | + temp /= slope; |
||
140 | + |
||
141 | + if (temp < 0) |
||
142 | + temp = 0; |
||
143 | + if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS)) |
||
144 | + temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1; |
||
145 | + |
||
146 | + return temp; |
||
147 | +} |
||
148 | + |
||
149 | +static int bcm2835_thermal_get_temp(void *d, int *temp) |
||
150 | +{ |
||
151 | + struct bcm2835_thermal_data *data = d; |
||
152 | + u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT); |
||
153 | + |
||
154 | + if (!(val & BCM2835_TS_TSENSSTAT_VALID)) |
||
155 | + return -EIO; |
||
156 | + |
||
157 | + val &= BCM2835_TS_TSENSSTAT_DATA_MASK; |
||
158 | + |
||
159 | + *temp = bcm2835_thermal_adc2temp( |
||
160 | + val, |
||
161 | + thermal_zone_get_offset(data->tz), |
||
162 | + thermal_zone_get_slope(data->tz)); |
||
163 | + |
||
164 | + return 0; |
||
165 | +} |
||
166 | + |
||
167 | +static const struct debugfs_reg32 bcm2835_thermal_regs[] = { |
||
168 | + { |
||
169 | + .name = "ctl", |
||
170 | + .offset = 0 |
||
171 | + }, |
||
172 | + { |
||
173 | + .name = "stat", |
||
174 | + .offset = 4 |
||
175 | + } |
||
176 | +}; |
||
177 | + |
||
178 | +static void bcm2835_thermal_debugfs(struct platform_device *pdev) |
||
179 | +{ |
||
180 | + struct thermal_zone_device *tz = platform_get_drvdata(pdev); |
||
181 | + struct bcm2835_thermal_data *data = tz->devdata; |
||
182 | + struct debugfs_regset32 *regset; |
||
183 | + |
||
184 | + data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL); |
||
185 | + if (!data->debugfsdir) |
||
186 | + return; |
||
187 | + |
||
188 | + regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL); |
||
189 | + if (!regset) |
||
190 | + return; |
||
191 | + |
||
192 | + regset->regs = bcm2835_thermal_regs; |
||
193 | + regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs); |
||
194 | + regset->base = data->regs; |
||
195 | + |
||
196 | + debugfs_create_regset32("regset", 0444, data->debugfsdir, regset); |
||
197 | +} |
||
198 | + |
||
199 | +static struct thermal_zone_of_device_ops bcm2835_thermal_ops = { |
||
200 | + .get_temp = bcm2835_thermal_get_temp, |
||
201 | +}; |
||
202 | + |
||
203 | +/* |
||
204 | + * Note: as per Raspberry Foundation FAQ |
||
205 | + * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature) |
||
206 | + * the recommended temperature range for the SoC -40C to +85C |
||
207 | + * so the trip limit is set to 80C. |
||
208 | + * this applies to all the BCM283X SoC |
||
209 | + */ |
||
210 | + |
||
211 | +static const struct of_device_id bcm2835_thermal_of_match_table[] = { |
||
212 | + { |
||
213 | + .compatible = "brcm,bcm2835-thermal", |
||
214 | + }, |
||
215 | + { |
||
216 | + .compatible = "brcm,bcm2836-thermal", |
||
217 | + }, |
||
218 | + { |
||
219 | + .compatible = "brcm,bcm2837-thermal", |
||
220 | + }, |
||
221 | + {}, |
||
222 | +}; |
||
223 | +MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table); |
||
224 | + |
||
225 | +static int bcm2835_thermal_probe(struct platform_device *pdev) |
||
226 | +{ |
||
227 | + const struct of_device_id *match; |
||
228 | + struct thermal_zone_device *tz; |
||
229 | + struct bcm2835_thermal_data *data; |
||
230 | + struct resource *res; |
||
231 | + int err = 0; |
||
232 | + u32 val; |
||
233 | + unsigned long rate; |
||
234 | + |
||
235 | + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); |
||
236 | + if (!data) |
||
237 | + return -ENOMEM; |
||
238 | + |
||
239 | + match = of_match_device(bcm2835_thermal_of_match_table, |
||
240 | + &pdev->dev); |
||
241 | + if (!match) |
||
242 | + return -EINVAL; |
||
243 | + |
||
244 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||
245 | + data->regs = devm_ioremap_resource(&pdev->dev, res); |
||
246 | + if (IS_ERR(data->regs)) { |
||
247 | + err = PTR_ERR(data->regs); |
||
248 | + dev_err(&pdev->dev, "Could not get registers: %d\n", err); |
||
249 | + return err; |
||
250 | + } |
||
251 | + |
||
252 | + data->clk = devm_clk_get(&pdev->dev, NULL); |
||
253 | + if (IS_ERR(data->clk)) { |
||
254 | + err = PTR_ERR(data->clk); |
||
255 | + if (err != -EPROBE_DEFER) |
||
256 | + dev_err(&pdev->dev, "Could not get clk: %d\n", err); |
||
257 | + return err; |
||
258 | + } |
||
259 | + |
||
260 | + err = clk_prepare_enable(data->clk); |
||
261 | + if (err) |
||
262 | + return err; |
||
263 | + |
||
264 | + rate = clk_get_rate(data->clk); |
||
265 | + if ((rate < 1920000) || (rate > 5000000)) |
||
266 | + dev_warn(&pdev->dev, |
||
267 | + "Clock %pCn running at %pCr Hz is outside of the recommended range: 1.92 to 5MHz\n", |
||
268 | + data->clk, data->clk); |
||
269 | + |
||
270 | + /* register of thermal sensor and get info from DT */ |
||
271 | + tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data, |
||
272 | + &bcm2835_thermal_ops); |
||
273 | + if (IS_ERR(tz)) { |
||
274 | + err = PTR_ERR(tz); |
||
275 | + dev_err(&pdev->dev, |
||
276 | + "Failed to register the thermal device: %d\n", |
||
277 | + err); |
||
278 | + goto err_clk; |
||
279 | + } |
||
280 | + |
||
281 | + /* |
||
282 | + * right now the FW does set up the HW-block, so we are not |
||
283 | + * touching the configuration registers. |
||
284 | + * But if the HW is not enabled, then set it up |
||
285 | + * using "sane" values used by the firmware right now. |
||
286 | + */ |
||
287 | + val = readl(data->regs + BCM2835_TS_TSENSCTL); |
||
288 | + if (!(val & BCM2835_TS_TSENSCTL_RSTB)) { |
||
289 | + int trip_temp, offset, slope; |
||
290 | + |
||
291 | + slope = thermal_zone_get_slope(tz); |
||
292 | + offset = thermal_zone_get_offset(tz); |
||
293 | + /* |
||
294 | + * For now we deal only with critical, otherwise |
||
295 | + * would need to iterate |
||
296 | + */ |
||
297 | + err = tz->ops->get_trip_temp(tz, 0, &trip_temp); |
||
298 | + if (err < 0) { |
||
299 | + err = PTR_ERR(tz); |
||
300 | + dev_err(&pdev->dev, |
||
301 | + "Not able to read trip_temp: %d\n", |
||
302 | + err); |
||
303 | + goto err_tz; |
||
304 | + } |
||
305 | + |
||
306 | + /* set bandgap reference voltage and enable voltage regulator */ |
||
307 | + val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT << |
||
308 | + BCM2835_TS_TSENSCTL_CTRL_SHIFT) | |
||
309 | + BCM2835_TS_TSENSCTL_REGULEN; |
||
310 | + |
||
311 | + /* use the recommended reset duration */ |
||
312 | + val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT); |
||
313 | + |
||
314 | + /* trip_adc value from info */ |
||
315 | + val |= bcm2835_thermal_temp2adc(trip_temp, |
||
316 | + offset, |
||
317 | + slope) |
||
318 | + << BCM2835_TS_TSENSCTL_THOLD_SHIFT; |
||
319 | + |
||
320 | + /* write the value back to the register as 2 steps */ |
||
321 | + writel(val, data->regs + BCM2835_TS_TSENSCTL); |
||
322 | + val |= BCM2835_TS_TSENSCTL_RSTB; |
||
323 | + writel(val, data->regs + BCM2835_TS_TSENSCTL); |
||
324 | + } |
||
325 | + |
||
326 | + data->tz = tz; |
||
327 | + |
||
328 | + platform_set_drvdata(pdev, tz); |
||
329 | + |
||
330 | + bcm2835_thermal_debugfs(pdev); |
||
331 | + |
||
332 | + return 0; |
||
333 | +err_tz: |
||
334 | + thermal_zone_of_sensor_unregister(&pdev->dev, tz); |
||
335 | +err_clk: |
||
336 | + clk_disable_unprepare(data->clk); |
||
337 | + |
||
338 | + return err; |
||
339 | +} |
||
340 | + |
||
341 | +static int bcm2835_thermal_remove(struct platform_device *pdev) |
||
342 | +{ |
||
343 | + struct thermal_zone_device *tz = platform_get_drvdata(pdev); |
||
344 | + struct bcm2835_thermal_data *data = tz->devdata; |
||
345 | + |
||
346 | + debugfs_remove_recursive(data->debugfsdir); |
||
347 | + thermal_zone_of_sensor_unregister(&pdev->dev, tz); |
||
348 | + clk_disable_unprepare(data->clk); |
||
349 | + |
||
350 | + return 0; |
||
351 | +} |
||
352 | + |
||
353 | +static struct platform_driver bcm2835_thermal_driver = { |
||
354 | + .probe = bcm2835_thermal_probe, |
||
355 | + .remove = bcm2835_thermal_remove, |
||
356 | + .driver = { |
||
357 | + .name = "bcm2835_thermal", |
||
358 | + .of_match_table = bcm2835_thermal_of_match_table, |
||
359 | + }, |
||
360 | +}; |
||
361 | +module_platform_driver(bcm2835_thermal_driver); |
||
362 | + |
||
363 | +MODULE_AUTHOR("Martin Sperl"); |
||
364 | +MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); |
||
365 | +MODULE_LICENSE("GPL"); |