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 struct hwmon_channel_info
*rpi_info
[] = {
90 HWMON_CHANNEL_INFO(in
,
95 static const struct hwmon_ops rpi_hwmon_ops
= {
96 .is_visible
= rpi_is_visible
,
100 static const struct hwmon_chip_info rpi_chip_info
= {
101 .ops
= &rpi_hwmon_ops
,
105 static int rpi_hwmon_probe(struct platform_device
*pdev
)
107 struct device
*dev
= &pdev
->dev
;
108 struct rpi_hwmon_data
*data
;
110 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
114 /* Parent driver assure that firmware is correct */
115 data
->fw
= dev_get_drvdata(dev
->parent
);
117 data
->hwmon_dev
= devm_hwmon_device_register_with_info(dev
, "rpi_volt",
122 INIT_DELAYED_WORK(&data
->get_values_poll_work
, get_values_poll
);
123 platform_set_drvdata(pdev
, data
);
125 if (!PTR_ERR_OR_ZERO(data
->hwmon_dev
))
126 schedule_delayed_work(&data
->get_values_poll_work
, 2 * HZ
);
128 return PTR_ERR_OR_ZERO(data
->hwmon_dev
);
131 static int rpi_hwmon_remove(struct platform_device
*pdev
)
133 struct rpi_hwmon_data
*data
= platform_get_drvdata(pdev
);
135 cancel_delayed_work_sync(&data
->get_values_poll_work
);
140 static struct platform_driver rpi_hwmon_driver
= {
141 .probe
= rpi_hwmon_probe
,
142 .remove
= rpi_hwmon_remove
,
144 .name
= "raspberrypi-hwmon",
147 module_platform_driver(rpi_hwmon_driver
);
149 MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
150 MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
151 MODULE_LICENSE("GPL v2");
152 MODULE_ALIAS("platform:raspberrypi-hwmon");