1 // SPDX-License-Identifier: GPL-2.0+
3 * Raspberry Pi voltage sensor driver
5 * Based on firmware/raspberrypi.c by Noralf Trønnes
7 * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
9 #include <linux/device.h>
10 #include <linux/devm-helpers.h>
11 #include <linux/err.h>
12 #include <linux/hwmon.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/workqueue.h>
17 #include <soc/bcm2835/raspberrypi-firmware.h>
19 #define UNDERVOLTAGE_STICKY_BIT BIT(16)
21 struct rpi_hwmon_data
{
22 struct device
*hwmon_dev
;
23 struct rpi_firmware
*fw
;
25 struct delayed_work get_values_poll_work
;
28 static void rpi_firmware_get_throttled(struct rpi_hwmon_data
*data
)
30 u32 new_uv
, old_uv
, value
;
33 /* Request firmware to clear sticky bits */
36 ret
= rpi_firmware_property(data
->fw
, RPI_FIRMWARE_GET_THROTTLED
,
37 &value
, sizeof(value
));
39 dev_err_once(data
->hwmon_dev
, "Failed to get throttled (%d)\n",
44 new_uv
= value
& UNDERVOLTAGE_STICKY_BIT
;
45 old_uv
= data
->last_throttled
& UNDERVOLTAGE_STICKY_BIT
;
46 data
->last_throttled
= value
;
52 dev_crit(data
->hwmon_dev
, "Undervoltage detected!\n");
54 dev_info(data
->hwmon_dev
, "Voltage normalised\n");
56 hwmon_notify_event(data
->hwmon_dev
, hwmon_in
, hwmon_in_lcrit_alarm
, 0);
59 static void get_values_poll(struct work_struct
*work
)
61 struct rpi_hwmon_data
*data
;
63 data
= container_of(work
, struct rpi_hwmon_data
,
64 get_values_poll_work
.work
);
66 rpi_firmware_get_throttled(data
);
69 * We can't run faster than the sticky shift (100ms) since we get
70 * flipping in the sticky bits that are cleared.
72 schedule_delayed_work(&data
->get_values_poll_work
, 2 * HZ
);
75 static int rpi_read(struct device
*dev
, enum hwmon_sensor_types type
,
76 u32 attr
, int channel
, long *val
)
78 struct rpi_hwmon_data
*data
= dev_get_drvdata(dev
);
80 *val
= !!(data
->last_throttled
& UNDERVOLTAGE_STICKY_BIT
);
84 static const struct hwmon_channel_info
* const rpi_info
[] = {
85 HWMON_CHANNEL_INFO(in
,
90 static const struct hwmon_ops rpi_hwmon_ops
= {
95 static const struct hwmon_chip_info rpi_chip_info
= {
96 .ops
= &rpi_hwmon_ops
,
100 static int rpi_hwmon_probe(struct platform_device
*pdev
)
102 struct device
*dev
= &pdev
->dev
;
103 struct rpi_hwmon_data
*data
;
106 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
110 /* Parent driver assure that firmware is correct */
111 data
->fw
= dev_get_drvdata(dev
->parent
);
113 data
->hwmon_dev
= devm_hwmon_device_register_with_info(dev
, "rpi_volt",
117 if (IS_ERR(data
->hwmon_dev
))
118 return PTR_ERR(data
->hwmon_dev
);
120 ret
= devm_delayed_work_autocancel(dev
, &data
->get_values_poll_work
,
124 platform_set_drvdata(pdev
, data
);
126 schedule_delayed_work(&data
->get_values_poll_work
, 2 * HZ
);
131 static struct platform_driver rpi_hwmon_driver
= {
132 .probe
= rpi_hwmon_probe
,
134 .name
= "raspberrypi-hwmon",
137 module_platform_driver(rpi_hwmon_driver
);
139 MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
140 MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
141 MODULE_LICENSE("GPL v2");
142 MODULE_ALIAS("platform:raspberrypi-hwmon");