OpenWrt – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From 1f8ba449fabb0eda1d835138d2324f8bc251e8c5 Mon Sep 17 00:00:00 2001 |
2 | From: Serge Schneider <serge@raspberrypi.org> |
||
3 | Date: Mon, 9 Jul 2018 12:54:25 +0100 |
||
4 | Subject: [PATCH 365/454] Add rpi-poe-fan driver |
||
5 | |||
6 | Signed-off-by: Serge Schneider <serge@raspberrypi.org> |
||
7 | --- |
||
8 | arch/arm/boot/dts/overlays/Makefile | 1 + |
||
9 | arch/arm/boot/dts/overlays/README | 6 + |
||
10 | .../arm/boot/dts/overlays/rpi-poe-overlay.dts | 61 +++ |
||
11 | arch/arm/configs/bcm2709_defconfig | 1 + |
||
12 | arch/arm/configs/bcmrpi_defconfig | 1 + |
||
13 | drivers/hwmon/Kconfig | 10 + |
||
14 | drivers/hwmon/Makefile | 1 + |
||
15 | drivers/hwmon/rpi-poe-fan.c | 443 ++++++++++++++++++ |
||
16 | include/soc/bcm2835/raspberrypi-firmware.h | 2 + |
||
17 | 9 files changed, 526 insertions(+) |
||
18 | create mode 100644 arch/arm/boot/dts/overlays/rpi-poe-overlay.dts |
||
19 | create mode 100644 drivers/hwmon/rpi-poe-fan.c |
||
20 | |||
21 | --- a/arch/arm/boot/dts/overlays/Makefile |
||
22 | +++ b/arch/arm/boot/dts/overlays/Makefile |
||
23 | @@ -103,6 +103,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ |
||
24 | rpi-dac.dtbo \ |
||
25 | rpi-display.dtbo \ |
||
26 | rpi-ft5406.dtbo \ |
||
27 | + rpi-poe.dtbo \ |
||
28 | rpi-proto.dtbo \ |
||
29 | rpi-sense.dtbo \ |
||
30 | rpi-tv.dtbo \ |
||
31 | --- a/arch/arm/boot/dts/overlays/README |
||
32 | +++ b/arch/arm/boot/dts/overlays/README |
||
33 | @@ -1576,6 +1576,12 @@ Params: touchscreen-size-x Touchscr |
||
34 | touchscreen-swapped-x-y Swap X and Y cordinates (default 0); |
||
35 | |||
36 | |||
37 | +Name: rpi-poe |
||
38 | +Info: Raspberry Pi POE HAT |
||
39 | +Load: dtoverlay=rpi-poe |
||
40 | +Params: <None> |
||
41 | + |
||
42 | + |
||
43 | Name: rpi-proto |
||
44 | Info: Configures the RPi Proto audio card |
||
45 | Load: dtoverlay=rpi-proto |
||
46 | --- /dev/null |
||
47 | +++ b/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts |
||
48 | @@ -0,0 +1,61 @@ |
||
49 | +/* |
||
50 | + * Overlay for the Raspberry Pi POE HAT. |
||
51 | + */ |
||
52 | +/dts-v1/; |
||
53 | +/plugin/; |
||
54 | + |
||
55 | +/ { |
||
56 | + compatible = "brcm,bcm2708"; |
||
57 | + |
||
58 | + fragment@0 { |
||
59 | + target-path = "/"; |
||
60 | + __overlay__ { |
||
61 | + fan0: rpi-poe-fan@0 { |
||
62 | + compatible = "rpi-poe-fan"; |
||
63 | + firmware = <&firmware>; |
||
64 | + cooling-min-state = <0>; |
||
65 | + cooling-max-state = <3>; |
||
66 | + #cooling-cells = <2>; |
||
67 | + cooling-levels = <0 50 150 255>; |
||
68 | + status = "okay"; |
||
69 | + }; |
||
70 | + }; |
||
71 | + }; |
||
72 | + |
||
73 | + fragment@1 { |
||
74 | + target = <&cpu_thermal>; |
||
75 | + __overlay__ { |
||
76 | + trips { |
||
77 | + threshold: trip-point@0 { |
||
78 | + temperature = <45000>; |
||
79 | + hysteresis = <5000>; |
||
80 | + type = "active"; |
||
81 | + }; |
||
82 | + target: trip-point@1 { |
||
83 | + temperature = <50000>; |
||
84 | + hysteresis = <2000>; |
||
85 | + type = "active"; |
||
86 | + }; |
||
87 | + cpu_hot: cpu_hot@0 { |
||
88 | + temperature = <55000>; |
||
89 | + hysteresis = <2000>; |
||
90 | + type = "active"; |
||
91 | + }; |
||
92 | + }; |
||
93 | + cooling-maps { |
||
94 | + map0 { |
||
95 | + trip = <&threshold>; |
||
96 | + cooling-device = <&fan0 0 1>; |
||
97 | + }; |
||
98 | + map1 { |
||
99 | + trip = <&target>; |
||
100 | + cooling-device = <&fan0 1 2>; |
||
101 | + }; |
||
102 | + map2 { |
||
103 | + trip = <&cpu_hot>; |
||
104 | + cooling-device = <&fan0 2 3>; |
||
105 | + }; |
||
106 | + }; |
||
107 | + }; |
||
108 | + }; |
||
109 | +}; |
||
110 | --- a/arch/arm/configs/bcm2709_defconfig |
||
111 | +++ b/arch/arm/configs/bcm2709_defconfig |
||
112 | @@ -658,6 +658,7 @@ CONFIG_HWMON=m |
||
113 | CONFIG_SENSORS_DS1621=m |
||
114 | CONFIG_SENSORS_JC42=m |
||
115 | CONFIG_SENSORS_LM75=m |
||
116 | +CONFIG_SENSORS_RPI_POE_FAN=m |
||
117 | CONFIG_SENSORS_SHT21=m |
||
118 | CONFIG_SENSORS_SHT3x=m |
||
119 | CONFIG_SENSORS_SHTC1=m |
||
120 | --- a/arch/arm/configs/bcmrpi_defconfig |
||
121 | +++ b/arch/arm/configs/bcmrpi_defconfig |
||
122 | @@ -651,6 +651,7 @@ CONFIG_HWMON=m |
||
123 | CONFIG_SENSORS_DS1621=m |
||
124 | CONFIG_SENSORS_JC42=m |
||
125 | CONFIG_SENSORS_LM75=m |
||
126 | +CONFIG_SENSORS_RPI_POE_FAN=m |
||
127 | CONFIG_SENSORS_SHT21=m |
||
128 | CONFIG_SENSORS_SHT3x=m |
||
129 | CONFIG_SENSORS_SHTC1=m |
||
130 | --- a/drivers/hwmon/Kconfig |
||
131 | +++ b/drivers/hwmon/Kconfig |
||
132 | @@ -1286,6 +1286,16 @@ config SENSORS_PWM_FAN |
||
133 | This driver can also be built as a module. If so, the module |
||
134 | will be called pwm-fan. |
||
135 | |||
136 | +config SENSORS_RPI_POE_FAN |
||
137 | + tristate "Raspberry Pi POE HAT fan" |
||
138 | + depends on RASPBERRYPI_FIRMWARE |
||
139 | + depends on THERMAL || THERMAL=n |
||
140 | + help |
||
141 | + If you say yes here you get support for Raspberry Pi POE HAT fan. |
||
142 | + |
||
143 | + This driver can also be built as a module. If so, the module |
||
144 | + will be called rpi-poe-fan. |
||
145 | + |
||
146 | config SENSORS_SHT15 |
||
147 | tristate "Sensiron humidity and temperature sensors. SHT15 and compat." |
||
148 | depends on GPIOLIB || COMPILE_TEST |
||
149 | --- a/drivers/hwmon/Makefile |
||
150 | +++ b/drivers/hwmon/Makefile |
||
151 | @@ -138,6 +138,7 @@ obj-$(CONFIG_SENSORS_PC87427) += pc87427 |
||
152 | obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o |
||
153 | obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o |
||
154 | obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o |
||
155 | +obj-$(CONFIG_SENSORS_RPI_POE_FAN) += rpi-poe-fan.o |
||
156 | obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o |
||
157 | obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o |
||
158 | obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o |
||
159 | --- /dev/null |
||
160 | +++ b/drivers/hwmon/rpi-poe-fan.c |
||
161 | @@ -0,0 +1,443 @@ |
||
162 | +/* |
||
163 | + * rpi-poe-fan.c - Hwmon driver for Raspberry Pi POE HAT fan. |
||
164 | + * |
||
165 | + * Copyright (C) 2018 Raspberry Pi (Trading) Ltd. |
||
166 | + * Based on pwm-fan.c by Kamil Debski <k.debski@samsung.com> |
||
167 | + * |
||
168 | + * Author: Serge Schneider <serge@raspberrypi.org> |
||
169 | + * |
||
170 | + * This program is free software; you can redistribute it and/or modify |
||
171 | + * it under the terms of the GNU General Public License as published by |
||
172 | + * the Free Software Foundation; either version 2 of the License, or |
||
173 | + * (at your option) any later version. |
||
174 | + * |
||
175 | + * This program is distributed in the hope that it will be useful, |
||
176 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
177 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
178 | + * GNU General Public License for more details. |
||
179 | + */ |
||
180 | + |
||
181 | +#include <linux/hwmon.h> |
||
182 | +#include <linux/hwmon-sysfs.h> |
||
183 | +#include <linux/module.h> |
||
184 | +#include <linux/mutex.h> |
||
185 | +#include <linux/notifier.h> |
||
186 | +#include <linux/of.h> |
||
187 | +#include <linux/platform_device.h> |
||
188 | +#include <linux/reboot.h> |
||
189 | +#include <linux/sysfs.h> |
||
190 | +#include <linux/thermal.h> |
||
191 | +#include <soc/bcm2835/raspberrypi-firmware.h> |
||
192 | + |
||
193 | +#define MAX_PWM 255 |
||
194 | + |
||
195 | +#define POE_CUR_PWM 0x0 |
||
196 | +#define POE_DEF_PWM 0x1 |
||
197 | + |
||
198 | +struct rpi_poe_fan_ctx { |
||
199 | + struct mutex lock; |
||
200 | + struct rpi_firmware *fw; |
||
201 | + unsigned int pwm_value; |
||
202 | + unsigned int def_pwm_value; |
||
203 | + unsigned int rpi_poe_fan_state; |
||
204 | + unsigned int rpi_poe_fan_max_state; |
||
205 | + unsigned int *rpi_poe_fan_cooling_levels; |
||
206 | + struct thermal_cooling_device *cdev; |
||
207 | + struct notifier_block nb; |
||
208 | +}; |
||
209 | + |
||
210 | +struct m_data_s{ |
||
211 | + u32 reg; |
||
212 | + u32 val; |
||
213 | + u32 ret; |
||
214 | +}; |
||
215 | + |
||
216 | +static int write_reg(struct rpi_firmware *fw, u32 reg, u32 *val){ |
||
217 | + struct m_data_s m_data = { |
||
218 | + .reg = reg, |
||
219 | + .val = *val |
||
220 | + }; |
||
221 | + int ret; |
||
222 | + ret = rpi_firmware_property(fw, RPI_FIRMWARE_SET_POE_HAT_VAL, |
||
223 | + &m_data, sizeof(m_data)); |
||
224 | + if (ret) { |
||
225 | + return ret; |
||
226 | + } else if (m_data.ret) { |
||
227 | + return -EIO; |
||
228 | + } |
||
229 | + return 0; |
||
230 | +} |
||
231 | + |
||
232 | +static int read_reg(struct rpi_firmware *fw, u32 reg, u32 *val){ |
||
233 | + struct m_data_s m_data = { |
||
234 | + .reg = reg, |
||
235 | + }; |
||
236 | + int ret; |
||
237 | + ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_POE_HAT_VAL, |
||
238 | + &m_data, sizeof(m_data)); |
||
239 | + if (ret) { |
||
240 | + return ret; |
||
241 | + } else if (m_data.ret) { |
||
242 | + return -EIO; |
||
243 | + } |
||
244 | + *val = m_data.val; |
||
245 | + return 0; |
||
246 | +} |
||
247 | + |
||
248 | +static int rpi_poe_reboot(struct notifier_block *nb, unsigned long code, |
||
249 | + void *unused) |
||
250 | +{ |
||
251 | + struct rpi_poe_fan_ctx *ctx = container_of(nb, struct rpi_poe_fan_ctx, |
||
252 | + nb); |
||
253 | + |
||
254 | + if (ctx->pwm_value != ctx->def_pwm_value) |
||
255 | + write_reg(ctx->fw, POE_CUR_PWM, &ctx->def_pwm_value); |
||
256 | + |
||
257 | + return NOTIFY_DONE; |
||
258 | +} |
||
259 | + |
||
260 | +static int __set_pwm(struct rpi_poe_fan_ctx *ctx, u32 pwm) |
||
261 | +{ |
||
262 | + int ret = 0; |
||
263 | + |
||
264 | + mutex_lock(&ctx->lock); |
||
265 | + if (ctx->pwm_value == pwm) |
||
266 | + goto exit_set_pwm_err; |
||
267 | + |
||
268 | + ret = write_reg(ctx->fw, POE_CUR_PWM, &pwm); |
||
269 | + if (!ret) |
||
270 | + ctx->pwm_value = pwm; |
||
271 | +exit_set_pwm_err: |
||
272 | + mutex_unlock(&ctx->lock); |
||
273 | + return ret; |
||
274 | +} |
||
275 | + |
||
276 | +static int __set_def_pwm(struct rpi_poe_fan_ctx *ctx, u32 def_pwm) |
||
277 | +{ |
||
278 | + int ret = 0; |
||
279 | + mutex_lock(&ctx->lock); |
||
280 | + if (ctx->def_pwm_value == def_pwm) |
||
281 | + goto exit_set_def_pwm_err; |
||
282 | + |
||
283 | + ret = write_reg(ctx->fw, POE_CUR_PWM, &def_pwm); |
||
284 | + if (!ret) |
||
285 | + ctx->def_pwm_value = def_pwm; |
||
286 | +exit_set_def_pwm_err: |
||
287 | + mutex_unlock(&ctx->lock); |
||
288 | + return ret; |
||
289 | +} |
||
290 | + |
||
291 | +static void rpi_poe_fan_update_state(struct rpi_poe_fan_ctx *ctx, |
||
292 | + unsigned long pwm) |
||
293 | +{ |
||
294 | + int i; |
||
295 | + |
||
296 | + for (i = 0; i < ctx->rpi_poe_fan_max_state; ++i) |
||
297 | + if (pwm < ctx->rpi_poe_fan_cooling_levels[i + 1]) |
||
298 | + break; |
||
299 | + |
||
300 | + ctx->rpi_poe_fan_state = i; |
||
301 | +} |
||
302 | + |
||
303 | +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, |
||
304 | + const char *buf, size_t count) |
||
305 | +{ |
||
306 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
307 | + unsigned long pwm; |
||
308 | + int ret; |
||
309 | + |
||
310 | + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) |
||
311 | + return -EINVAL; |
||
312 | + |
||
313 | + ret = __set_pwm(ctx, pwm); |
||
314 | + if (ret) |
||
315 | + return ret; |
||
316 | + |
||
317 | + rpi_poe_fan_update_state(ctx, pwm); |
||
318 | + return count; |
||
319 | +} |
||
320 | + |
||
321 | +static ssize_t set_def_pwm(struct device *dev, struct device_attribute *attr, |
||
322 | + const char *buf, size_t count) |
||
323 | +{ |
||
324 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
325 | + unsigned long def_pwm; |
||
326 | + int ret; |
||
327 | + |
||
328 | + if (kstrtoul(buf, 10, &def_pwm) || def_pwm > MAX_PWM) |
||
329 | + return -EINVAL; |
||
330 | + |
||
331 | + ret = __set_def_pwm(ctx, def_pwm); |
||
332 | + if (ret) |
||
333 | + return ret; |
||
334 | + return count; |
||
335 | +} |
||
336 | + |
||
337 | +static ssize_t show_pwm(struct device *dev, |
||
338 | + struct device_attribute *attr, char *buf) |
||
339 | +{ |
||
340 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
341 | + |
||
342 | + return sprintf(buf, "%u\n", ctx->pwm_value); |
||
343 | +} |
||
344 | + |
||
345 | +static ssize_t show_def_pwm(struct device *dev, |
||
346 | + struct device_attribute *attr, char *buf) |
||
347 | +{ |
||
348 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
349 | + |
||
350 | + return sprintf(buf, "%u\n", ctx->def_pwm_value); |
||
351 | +} |
||
352 | + |
||
353 | + |
||
354 | +static SENSOR_DEVICE_ATTR(pwm1, 0644, show_pwm, set_pwm, 0); |
||
355 | +static SENSOR_DEVICE_ATTR(def_pwm1, 0644, show_def_pwm, set_def_pwm, 1); |
||
356 | + |
||
357 | +static struct attribute *rpi_poe_fan_attrs[] = { |
||
358 | + &sensor_dev_attr_pwm1.dev_attr.attr, |
||
359 | + &sensor_dev_attr_def_pwm1.dev_attr.attr, |
||
360 | + NULL, |
||
361 | +}; |
||
362 | + |
||
363 | +ATTRIBUTE_GROUPS(rpi_poe_fan); |
||
364 | + |
||
365 | +/* thermal cooling device callbacks */ |
||
366 | +static int rpi_poe_fan_get_max_state(struct thermal_cooling_device *cdev, |
||
367 | + unsigned long *state) |
||
368 | +{ |
||
369 | + struct rpi_poe_fan_ctx *ctx = cdev->devdata; |
||
370 | + |
||
371 | + if (!ctx) |
||
372 | + return -EINVAL; |
||
373 | + |
||
374 | + *state = ctx->rpi_poe_fan_max_state; |
||
375 | + |
||
376 | + return 0; |
||
377 | +} |
||
378 | + |
||
379 | +static int rpi_poe_fan_get_cur_state(struct thermal_cooling_device *cdev, |
||
380 | + unsigned long *state) |
||
381 | +{ |
||
382 | + struct rpi_poe_fan_ctx *ctx = cdev->devdata; |
||
383 | + |
||
384 | + if (!ctx) |
||
385 | + return -EINVAL; |
||
386 | + |
||
387 | + *state = ctx->rpi_poe_fan_state; |
||
388 | + |
||
389 | + return 0; |
||
390 | +} |
||
391 | + |
||
392 | +static int rpi_poe_fan_set_cur_state(struct thermal_cooling_device *cdev, |
||
393 | + unsigned long state) |
||
394 | +{ |
||
395 | + struct rpi_poe_fan_ctx *ctx = cdev->devdata; |
||
396 | + int ret; |
||
397 | + |
||
398 | + if (!ctx || (state > ctx->rpi_poe_fan_max_state)) |
||
399 | + return -EINVAL; |
||
400 | + |
||
401 | + if (state == ctx->rpi_poe_fan_state) |
||
402 | + return 0; |
||
403 | + |
||
404 | + ret = __set_pwm(ctx, ctx->rpi_poe_fan_cooling_levels[state]); |
||
405 | + if (ret) { |
||
406 | + dev_err(&cdev->device, "Cannot set pwm!\n"); |
||
407 | + return ret; |
||
408 | + } |
||
409 | + |
||
410 | + ctx->rpi_poe_fan_state = state; |
||
411 | + |
||
412 | + return ret; |
||
413 | +} |
||
414 | + |
||
415 | +static const struct thermal_cooling_device_ops rpi_poe_fan_cooling_ops = { |
||
416 | + .get_max_state = rpi_poe_fan_get_max_state, |
||
417 | + .get_cur_state = rpi_poe_fan_get_cur_state, |
||
418 | + .set_cur_state = rpi_poe_fan_set_cur_state, |
||
419 | +}; |
||
420 | + |
||
421 | +static int rpi_poe_fan_of_get_cooling_data(struct device *dev, |
||
422 | + struct rpi_poe_fan_ctx *ctx) |
||
423 | +{ |
||
424 | + struct device_node *np = dev->of_node; |
||
425 | + int num, i, ret; |
||
426 | + |
||
427 | + if (!of_find_property(np, "cooling-levels", NULL)) |
||
428 | + return 0; |
||
429 | + |
||
430 | + ret = of_property_count_u32_elems(np, "cooling-levels"); |
||
431 | + if (ret <= 0) { |
||
432 | + dev_err(dev, "Wrong data!\n"); |
||
433 | + return ret ? : -EINVAL; |
||
434 | + } |
||
435 | + |
||
436 | + num = ret; |
||
437 | + ctx->rpi_poe_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32), |
||
438 | + GFP_KERNEL); |
||
439 | + if (!ctx->rpi_poe_fan_cooling_levels) |
||
440 | + return -ENOMEM; |
||
441 | + |
||
442 | + ret = of_property_read_u32_array(np, "cooling-levels", |
||
443 | + ctx->rpi_poe_fan_cooling_levels, num); |
||
444 | + if (ret) { |
||
445 | + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); |
||
446 | + return ret; |
||
447 | + } |
||
448 | + |
||
449 | + for (i = 0; i < num; i++) { |
||
450 | + if (ctx->rpi_poe_fan_cooling_levels[i] > MAX_PWM) { |
||
451 | + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i, |
||
452 | + ctx->rpi_poe_fan_cooling_levels[i], MAX_PWM); |
||
453 | + return -EINVAL; |
||
454 | + } |
||
455 | + } |
||
456 | + |
||
457 | + ctx->rpi_poe_fan_max_state = num - 1; |
||
458 | + |
||
459 | + return 0; |
||
460 | +} |
||
461 | + |
||
462 | +static int rpi_poe_fan_probe(struct platform_device *pdev) |
||
463 | +{ |
||
464 | + struct thermal_cooling_device *cdev; |
||
465 | + struct rpi_poe_fan_ctx *ctx; |
||
466 | + struct device *hwmon; |
||
467 | + struct device_node *np = pdev->dev.of_node; |
||
468 | + struct device_node *fw_node; |
||
469 | + int ret; |
||
470 | + |
||
471 | + fw_node = of_parse_phandle(np, "firmware", 0); |
||
472 | + if (!fw_node) { |
||
473 | + dev_err(&pdev->dev, "Missing firmware node\n"); |
||
474 | + return -ENOENT; |
||
475 | + } |
||
476 | + |
||
477 | + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); |
||
478 | + if (!ctx) |
||
479 | + return -ENOMEM; |
||
480 | + |
||
481 | + mutex_init(&ctx->lock); |
||
482 | + |
||
483 | + ctx->fw = rpi_firmware_get(fw_node); |
||
484 | + if (!ctx->fw) |
||
485 | + return -EPROBE_DEFER; |
||
486 | + |
||
487 | + platform_set_drvdata(pdev, ctx); |
||
488 | + |
||
489 | + ctx->nb.notifier_call = rpi_poe_reboot; |
||
490 | + ret = register_reboot_notifier(&ctx->nb); |
||
491 | + if (ret) { |
||
492 | + dev_err(&pdev->dev, "Failed to register reboot notifier: %i\n", |
||
493 | + ret); |
||
494 | + return ret; |
||
495 | + } |
||
496 | + ret = read_reg(ctx->fw, POE_DEF_PWM, &ctx->def_pwm_value); |
||
497 | + if (ret) { |
||
498 | + dev_err(&pdev->dev, "Failed to get default PWM value: %i\n", |
||
499 | + ret); |
||
500 | + goto err; |
||
501 | + } |
||
502 | + ret = read_reg(ctx->fw, POE_CUR_PWM, &ctx->pwm_value); |
||
503 | + if (ret) { |
||
504 | + dev_err(&pdev->dev, "Failed to get current PWM value: %i\n", |
||
505 | + ret); |
||
506 | + goto err; |
||
507 | + } |
||
508 | + |
||
509 | + hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "rpipoefan", |
||
510 | + ctx, rpi_poe_fan_groups); |
||
511 | + if (IS_ERR(hwmon)) { |
||
512 | + dev_err(&pdev->dev, "Failed to register hwmon device\n"); |
||
513 | + ret = PTR_ERR(hwmon); |
||
514 | + goto err; |
||
515 | + } |
||
516 | + |
||
517 | + ret = rpi_poe_fan_of_get_cooling_data(&pdev->dev, ctx); |
||
518 | + if (ret) |
||
519 | + return ret; |
||
520 | + |
||
521 | + rpi_poe_fan_update_state(ctx, ctx->pwm_value); |
||
522 | + if (!IS_ENABLED(CONFIG_THERMAL)) |
||
523 | + return 0; |
||
524 | + |
||
525 | + cdev = thermal_of_cooling_device_register(np, |
||
526 | + "rpi-poe-fan", ctx, |
||
527 | + &rpi_poe_fan_cooling_ops); |
||
528 | + if (IS_ERR(cdev)) { |
||
529 | + dev_err(&pdev->dev, |
||
530 | + "Failed to register rpi-poe-fan as cooling device"); |
||
531 | + ret = PTR_ERR(cdev); |
||
532 | + goto err; |
||
533 | + } |
||
534 | + ctx->cdev = cdev; |
||
535 | + thermal_cdev_update(cdev); |
||
536 | + |
||
537 | + return 0; |
||
538 | +err: |
||
539 | + unregister_reboot_notifier(&ctx->nb); |
||
540 | + return ret; |
||
541 | +} |
||
542 | + |
||
543 | +static int rpi_poe_fan_remove(struct platform_device *pdev) |
||
544 | +{ |
||
545 | + struct rpi_poe_fan_ctx *ctx = platform_get_drvdata(pdev); |
||
546 | + u32 value = ctx->def_pwm_value; |
||
547 | + |
||
548 | + unregister_reboot_notifier(&ctx->nb); |
||
549 | + thermal_cooling_device_unregister(ctx->cdev); |
||
550 | + if (ctx->pwm_value != value) { |
||
551 | + write_reg(ctx->fw, POE_CUR_PWM, &value); |
||
552 | + } |
||
553 | + return 0; |
||
554 | +} |
||
555 | + |
||
556 | +#ifdef CONFIG_PM_SLEEP |
||
557 | +static int rpi_poe_fan_suspend(struct device *dev) |
||
558 | +{ |
||
559 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
560 | + u32 value = 0; |
||
561 | + |
||
562 | + if (ctx->pwm_value != value) |
||
563 | + ret = write_reg(ctx->fw, POE_CUR_PWM, &value); |
||
564 | + return 0; |
||
565 | +} |
||
566 | + |
||
567 | +static int rpi_poe_fan_resume(struct device *dev) |
||
568 | +{ |
||
569 | + struct rpi_poe_fan_ctx *ctx = dev_get_drvdata(dev); |
||
570 | + u32 value = ctx->pwm_value; |
||
571 | + int ret = 0; |
||
572 | + |
||
573 | + if (value != 0) |
||
574 | + ret = write_reg(ctx->fw, POE_CUR_PWM, &value); |
||
575 | + |
||
576 | + return ret; |
||
577 | +} |
||
578 | +#endif |
||
579 | + |
||
580 | +static SIMPLE_DEV_PM_OPS(rpi_poe_fan_pm, rpi_poe_fan_suspend, |
||
581 | + rpi_poe_fan_resume); |
||
582 | + |
||
583 | +static const struct of_device_id of_rpi_poe_fan_match[] = { |
||
584 | + { .compatible = "rpi-poe-fan", }, |
||
585 | + {}, |
||
586 | +}; |
||
587 | +MODULE_DEVICE_TABLE(of, of_rpi_poe_fan_match); |
||
588 | + |
||
589 | +static struct platform_driver rpi_poe_fan_driver = { |
||
590 | + .probe = rpi_poe_fan_probe, |
||
591 | + .remove = rpi_poe_fan_remove, |
||
592 | + .driver = { |
||
593 | + .name = "rpi-poe-fan", |
||
594 | + .pm = &rpi_poe_fan_pm, |
||
595 | + .of_match_table = of_rpi_poe_fan_match, |
||
596 | + }, |
||
597 | +}; |
||
598 | + |
||
599 | +module_platform_driver(rpi_poe_fan_driver); |
||
600 | + |
||
601 | +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>"); |
||
602 | +MODULE_ALIAS("platform:rpi-poe-fan"); |
||
603 | +MODULE_DESCRIPTION("Raspberry Pi POE HAT fan driver"); |
||
604 | +MODULE_LICENSE("GPL"); |
||
605 | --- a/include/soc/bcm2835/raspberrypi-firmware.h |
||
606 | +++ b/include/soc/bcm2835/raspberrypi-firmware.h |
||
607 | @@ -93,6 +93,8 @@ enum rpi_firmware_property_tag { |
||
608 | RPI_FIRMWARE_SET_GPIO_CONFIG = 0x00038043, |
||
609 | RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045, |
||
610 | RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, |
||
611 | + RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049, |
||
612 | + RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050, |
||
613 | |||
614 | |||
615 | /* Dispmanx TAGS */ |