Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * Driver for Broadcom BCM2835 SoC temperature sensor
4 : : *
5 : : * Copyright (C) 2016 Martin Sperl
6 : : */
7 : :
8 : : #include <linux/clk.h>
9 : : #include <linux/debugfs.h>
10 : : #include <linux/device.h>
11 : : #include <linux/err.h>
12 : : #include <linux/io.h>
13 : : #include <linux/kernel.h>
14 : : #include <linux/module.h>
15 : : #include <linux/of.h>
16 : : #include <linux/of_address.h>
17 : : #include <linux/of_device.h>
18 : : #include <linux/platform_device.h>
19 : : #include <linux/thermal.h>
20 : :
21 : : #include "../thermal_hwmon.h"
22 : :
23 : : #define BCM2835_TS_TSENSCTL 0x00
24 : : #define BCM2835_TS_TSENSSTAT 0x04
25 : :
26 : : #define BCM2835_TS_TSENSCTL_PRWDW BIT(0)
27 : : #define BCM2835_TS_TSENSCTL_RSTB BIT(1)
28 : :
29 : : /*
30 : : * bandgap reference voltage in 6 mV increments
31 : : * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV
32 : : */
33 : : #define BCM2835_TS_TSENSCTL_CTRL_BITS 3
34 : : #define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2
35 : : #define BCM2835_TS_TSENSCTL_CTRL_MASK \
36 : : GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \
37 : : BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
38 : : BCM2835_TS_TSENSCTL_CTRL_SHIFT)
39 : : #define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1
40 : : #define BCM2835_TS_TSENSCTL_EN_INT BIT(5)
41 : : #define BCM2835_TS_TSENSCTL_DIRECT BIT(6)
42 : : #define BCM2835_TS_TSENSCTL_CLR_INT BIT(7)
43 : : #define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8
44 : : #define BCM2835_TS_TSENSCTL_THOLD_BITS 10
45 : : #define BCM2835_TS_TSENSCTL_THOLD_MASK \
46 : : GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \
47 : : BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
48 : : BCM2835_TS_TSENSCTL_THOLD_SHIFT)
49 : : /*
50 : : * time how long the block to be asserted in reset
51 : : * which based on a clock counter (TSENS clock assumed)
52 : : */
53 : : #define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18
54 : : #define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8
55 : : #define BCM2835_TS_TSENSCTL_REGULEN BIT(26)
56 : :
57 : : #define BCM2835_TS_TSENSSTAT_DATA_BITS 10
58 : : #define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0
59 : : #define BCM2835_TS_TSENSSTAT_DATA_MASK \
60 : : GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \
61 : : BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
62 : : BCM2835_TS_TSENSSTAT_DATA_SHIFT)
63 : : #define BCM2835_TS_TSENSSTAT_VALID BIT(10)
64 : : #define BCM2835_TS_TSENSSTAT_INTERRUPT BIT(11)
65 : :
66 : : struct bcm2835_thermal_data {
67 : : struct thermal_zone_device *tz;
68 : : void __iomem *regs;
69 : : struct clk *clk;
70 : : struct dentry *debugfsdir;
71 : : };
72 : :
73 : : static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)
74 : : {
75 : 207 : return offset + slope * adc;
76 : : }
77 : :
78 : : static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
79 : : {
80 : 0 : temp -= offset;
81 : 0 : temp /= slope;
82 : :
83 [ # # ]: 0 : if (temp < 0)
84 : : temp = 0;
85 [ # # ]: 0 : if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
86 : : temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
87 : :
88 : : return temp;
89 : : }
90 : :
91 : 207 : static int bcm2835_thermal_get_temp(void *d, int *temp)
92 : : {
93 : : struct bcm2835_thermal_data *data = d;
94 : 414 : u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
95 : :
96 [ + - ]: 207 : if (!(val & BCM2835_TS_TSENSSTAT_VALID))
97 : : return -EIO;
98 : :
99 : 207 : val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
100 : :
101 : 414 : *temp = bcm2835_thermal_adc2temp(
102 : : val,
103 : : thermal_zone_get_offset(data->tz),
104 : : thermal_zone_get_slope(data->tz));
105 : :
106 : 207 : return 0;
107 : : }
108 : :
109 : : static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
110 : : {
111 : : .name = "ctl",
112 : : .offset = 0
113 : : },
114 : : {
115 : : .name = "stat",
116 : : .offset = 4
117 : : }
118 : : };
119 : :
120 : 207 : static void bcm2835_thermal_debugfs(struct platform_device *pdev)
121 : : {
122 : : struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
123 : : struct debugfs_regset32 *regset;
124 : :
125 : 207 : data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
126 : :
127 : 207 : regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);
128 [ + - ]: 207 : if (!regset)
129 : 207 : return;
130 : :
131 : 207 : regset->regs = bcm2835_thermal_regs;
132 : 207 : regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
133 : 207 : regset->base = data->regs;
134 : :
135 : 207 : debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);
136 : : }
137 : :
138 : : static const struct thermal_zone_of_device_ops bcm2835_thermal_ops = {
139 : : .get_temp = bcm2835_thermal_get_temp,
140 : : };
141 : :
142 : : /*
143 : : * Note: as per Raspberry Foundation FAQ
144 : : * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)
145 : : * the recommended temperature range for the SoC -40C to +85C
146 : : * so the trip limit is set to 80C.
147 : : * this applies to all the BCM283X SoC
148 : : */
149 : :
150 : : static const struct of_device_id bcm2835_thermal_of_match_table[] = {
151 : : {
152 : : .compatible = "brcm,bcm2835-thermal",
153 : : },
154 : : {
155 : : .compatible = "brcm,bcm2836-thermal",
156 : : },
157 : : {
158 : : .compatible = "brcm,bcm2837-thermal",
159 : : },
160 : : {},
161 : : };
162 : : MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
163 : :
164 : 414 : static int bcm2835_thermal_probe(struct platform_device *pdev)
165 : : {
166 : : const struct of_device_id *match;
167 : : struct thermal_zone_device *tz;
168 : : struct bcm2835_thermal_data *data;
169 : : struct resource *res;
170 : : int err = 0;
171 : : u32 val;
172 : : unsigned long rate;
173 : :
174 : 414 : data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
175 [ + - ]: 414 : if (!data)
176 : : return -ENOMEM;
177 : :
178 : 414 : match = of_match_device(bcm2835_thermal_of_match_table,
179 : : &pdev->dev);
180 [ + - ]: 414 : if (!match)
181 : : return -EINVAL;
182 : :
183 : 414 : res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184 : 414 : data->regs = devm_ioremap_resource(&pdev->dev, res);
185 [ - + ]: 414 : if (IS_ERR(data->regs)) {
186 : : err = PTR_ERR(data->regs);
187 : 0 : dev_err(&pdev->dev, "Could not get registers: %d\n", err);
188 : 0 : return err;
189 : : }
190 : :
191 : 414 : data->clk = devm_clk_get(&pdev->dev, NULL);
192 [ + + ]: 414 : if (IS_ERR(data->clk)) {
193 : : err = PTR_ERR(data->clk);
194 [ - + ]: 207 : if (err != -EPROBE_DEFER)
195 : 0 : dev_err(&pdev->dev, "Could not get clk: %d\n", err);
196 : 207 : return err;
197 : : }
198 : :
199 : 207 : err = clk_prepare_enable(data->clk);
200 [ + - ]: 207 : if (err)
201 : : return err;
202 : :
203 : 207 : rate = clk_get_rate(data->clk);
204 [ - + ]: 207 : if ((rate < 1920000) || (rate > 5000000))
205 : 0 : dev_warn(&pdev->dev,
206 : : "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",
207 : : data->clk, rate);
208 : :
209 : : /* register of thermal sensor and get info from DT */
210 : 207 : tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
211 : : &bcm2835_thermal_ops);
212 [ - + ]: 207 : if (IS_ERR(tz)) {
213 : : err = PTR_ERR(tz);
214 : 0 : dev_err(&pdev->dev,
215 : : "Failed to register the thermal device: %d\n",
216 : : err);
217 : 0 : goto err_clk;
218 : : }
219 : :
220 : : /*
221 : : * right now the FW does set up the HW-block, so we are not
222 : : * touching the configuration registers.
223 : : * But if the HW is not enabled, then set it up
224 : : * using "sane" values used by the firmware right now.
225 : : */
226 : 414 : val = readl(data->regs + BCM2835_TS_TSENSCTL);
227 [ - + ]: 207 : if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
228 : : int trip_temp, offset, slope;
229 : :
230 : 0 : slope = thermal_zone_get_slope(tz);
231 : 0 : offset = thermal_zone_get_offset(tz);
232 : : /*
233 : : * For now we deal only with critical, otherwise
234 : : * would need to iterate
235 : : */
236 : 0 : err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
237 [ # # ]: 0 : if (err < 0) {
238 : 0 : dev_err(&pdev->dev,
239 : : "Not able to read trip_temp: %d\n",
240 : : err);
241 : 0 : goto err_tz;
242 : : }
243 : :
244 : : /* set bandgap reference voltage and enable voltage regulator */
245 : : val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
246 : : BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
247 : : BCM2835_TS_TSENSCTL_REGULEN;
248 : :
249 : : /* use the recommended reset duration */
250 : : val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
251 : :
252 : : /* trip_adc value from info */
253 : 0 : val |= bcm2835_thermal_temp2adc(trip_temp,
254 : : offset,
255 : : slope)
256 : 0 : << BCM2835_TS_TSENSCTL_THOLD_SHIFT;
257 : :
258 : : /* write the value back to the register as 2 steps */
259 : 0 : writel(val, data->regs + BCM2835_TS_TSENSCTL);
260 : 0 : val |= BCM2835_TS_TSENSCTL_RSTB;
261 : 0 : writel(val, data->regs + BCM2835_TS_TSENSCTL);
262 : : }
263 : :
264 : 207 : data->tz = tz;
265 : :
266 : : platform_set_drvdata(pdev, data);
267 : :
268 : : /*
269 : : * Thermal_zone doesn't enable hwmon as default,
270 : : * enable it here
271 : : */
272 : 207 : tz->tzp->no_hwmon = false;
273 : 207 : err = thermal_add_hwmon_sysfs(tz);
274 [ + - ]: 207 : if (err)
275 : : goto err_tz;
276 : :
277 : 207 : bcm2835_thermal_debugfs(pdev);
278 : :
279 : 207 : return 0;
280 : : err_tz:
281 : 0 : thermal_zone_of_sensor_unregister(&pdev->dev, tz);
282 : : err_clk:
283 : 0 : clk_disable_unprepare(data->clk);
284 : :
285 : 0 : return err;
286 : : }
287 : :
288 : 0 : static int bcm2835_thermal_remove(struct platform_device *pdev)
289 : : {
290 : : struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);
291 : 0 : struct thermal_zone_device *tz = data->tz;
292 : :
293 : 0 : debugfs_remove_recursive(data->debugfsdir);
294 : 0 : thermal_zone_of_sensor_unregister(&pdev->dev, tz);
295 : 0 : clk_disable_unprepare(data->clk);
296 : :
297 : 0 : return 0;
298 : : }
299 : :
300 : : static struct platform_driver bcm2835_thermal_driver = {
301 : : .probe = bcm2835_thermal_probe,
302 : : .remove = bcm2835_thermal_remove,
303 : : .driver = {
304 : : .name = "bcm2835_thermal",
305 : : .of_match_table = bcm2835_thermal_of_match_table,
306 : : },
307 : : };
308 : 207 : module_platform_driver(bcm2835_thermal_driver);
309 : :
310 : : MODULE_AUTHOR("Martin Sperl");
311 : : MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
312 : : MODULE_LICENSE("GPL");
|