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/err.h>
11 #include <linux/hwmon.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/workqueue.h>
16 #include <soc/bcm2835/raspberrypi-firmware.h>
18 #define UNDERVOLTAGE_STICKY_BIT BIT(16)
20 struct rpi_hwmon_data
{
21 struct device
*hwmon_dev
;
22 struct rpi_firmware
*fw
;
24 struct delayed_work get_values_poll_work
;
27 static void rpi_firmware_get_throttled(struct rpi_hwmon_data
*data
)
29 u32 new_uv
, old_uv
, value
;
32 /* Request firmware to clear sticky bits */
35 ret
= rpi_firmware_property(data
->fw
, RPI_FIRMWARE_GET_THROTTLED
,
36 &value
, sizeof(value
));
38 dev_err_once(data
->hwmon_dev
, "Failed to get throttled (%d)\n",
43 new_uv
= value
& UNDERVOLTAGE_STICKY_BIT
;
44 old_uv
= data
->last_throttled
& UNDERVOLTAGE_STICKY_BIT
;
45 data
->last_throttled
= value
;
51 dev_crit(data
->hwmon_dev
, "Undervoltage detected!\n");
53 dev_info(data
->hwmon_dev
, "Voltage normalised\n");
55 sysfs_notify(&data
->hwmon_dev
->kobj
, NULL
, "in0_lcrit_alarm");
58 static void get_values_poll(struct work_struct
*work
)
60 struct rpi_hwmon_data
*data
;
62 data
= container_of(work
, struct rpi_hwmon_data
,
63 get_values_poll_work
.work
);
65 rpi_firmware_get_throttled(data
);
68 * We can't run faster than the sticky shift (100ms) since we get
69 * flipping in the sticky bits that are cleared.
71 schedule_delayed_work(&data
->get_values_poll_work
, 2 * HZ
);
74 static int rpi_read(struct device
*dev
, enum hwmon_sensor_types type
,
75 u32 attr
, int channel
, long *val
)
77 struct rpi_hwmon_data
*data
= dev_get_drvdata(dev
);
79 *val
= !!(data
->last_throttled
& UNDERVOLTAGE_STICKY_BIT
);
83 static umode_t
rpi_is_visible(const void *_data
, enum hwmon_sensor_types type
,
84 u32 attr
, int channel
)
89 static const u32 rpi_in_config
[] = {
94 static const struct hwmon_channel_info rpi_in
= {
96 .config
= rpi_in_config
,
99 static const struct hwmon_channel_info
*rpi_info
[] = {
104 static const struct hwmon_ops rpi_hwmon_ops
= {
105 .is_visible
= rpi_is_visible
,
109 static const struct hwmon_chip_info rpi_chip_info
= {
110 .ops
= &rpi_hwmon_ops
,
114 static int rpi_hwmon_probe(struct platform_device
*pdev
)
116 struct device
*dev
= &pdev
->dev
;
117 struct rpi_hwmon_data
*data
;
119 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
123 /* Parent driver assure that firmware is correct */
124 data
->fw
= dev_get_drvdata(dev
->parent
);
126 data
->hwmon_dev
= devm_hwmon_device_register_with_info(dev
, "rpi_volt",
131 INIT_DELAYED_WORK(&data
->get_values_poll_work
, get_values_poll
);
132 platform_set_drvdata(pdev
, data
);
134 if (!PTR_ERR_OR_ZERO(data
->hwmon_dev
))
135 schedule_delayed_work(&data
->get_values_poll_work
, 2 * HZ
);
137 return PTR_ERR_OR_ZERO(data
->hwmon_dev
);
140 static int rpi_hwmon_remove(struct platform_device
*pdev
)
142 struct rpi_hwmon_data
*data
= platform_get_drvdata(pdev
);
144 cancel_delayed_work_sync(&data
->get_values_poll_work
);
149 static struct platform_driver rpi_hwmon_driver
= {
150 .probe
= rpi_hwmon_probe
,
151 .remove
= rpi_hwmon_remove
,
153 .name
= "raspberrypi-hwmon",
156 module_platform_driver(rpi_hwmon_driver
);
158 MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>");
159 MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
160 MODULE_LICENSE("GPL v2");
161 MODULE_ALIAS("platform:raspberrypi-hwmon");