Skip to content

Commit 4dbb408

Browse files
codingWiz-rickopsiff
authored andcommitted
hwmon: raspberrypi: Add voltage input support
Extend the raspberrypi-hwmon driver to expose firmware-provided voltage measurements through the hwmon subsystem. The driver now exports the following voltage inputs: - in0_input (core) - in1_input (sdram_c) - in2_input (sdram_i) - in3_input (sdram_p) Voltage values returned by firmware are converted from microvolts to millivolts as expected by the hwmon subsystem. Update the documentation related to it. The existing undervoltage sticky alarm handling is preserved and associated with the first voltage channel. Tested in - - Raspberry Pi 3b+ (Linux raspberrypi 6.12.75+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.12.75-1+rpt1 (2026-03-11) aarch64 GNU/Linux) Signed-off-by: Shubham Chakraborty <chakrabortyshubham66@gmail.com> Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com> Link: https://lore.kernel.org/r/20260517080445.103962-3-chakrabortyshubham66@gmail.com [groeck: Added missing empty line after declaration] Signed-off-by: Guenter Roeck <linux@roeck-us.net> Link: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git/commit/?h=hwmon-next&id=9656ebcce062f845e5f5586129227e3c6fba4b6b Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
1 parent 597c5d4 commit 4dbb408

2 files changed

Lines changed: 138 additions & 5 deletions

File tree

Documentation/hwmon/raspberrypi-hwmon.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ undervoltage conditions.
2020
Sysfs entries
2121
-------------
2222

23-
======================= ==================
23+
======================= ======================================================
24+
in0_input Core voltage in millivolts
25+
in1_input SDRAM controller voltage in millivolts
26+
in2_input SDRAM I/O voltage in millivolts
27+
in3_input SDRAM PHY voltage in millivolts
28+
in0_label "core"
29+
in1_label "sdram_c"
30+
in2_label "sdram_i"
31+
in3_label "sdram_p"
2432
in0_lcrit_alarm Undervoltage alarm
25-
======================= ==================
33+
======================= ======================================================
34+
35+
The voltage inputs and labels are only exposed if the firmware reports support
36+
for the corresponding voltage ID.

drivers/hwmon/raspberrypi-hwmon.c

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Based on firmware/raspberrypi.c by Noralf Trønnes
66
*
77
* Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
8+
* Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com>
89
*/
910
#include <linux/device.h>
1011
#include <linux/devm-helpers.h>
@@ -21,10 +22,18 @@
2122
struct rpi_hwmon_data {
2223
struct device *hwmon_dev;
2324
struct rpi_firmware *fw;
25+
u32 valid_inputs;
2426
u32 last_throttled;
2527
struct delayed_work get_values_poll_work;
2628
};
2729

30+
static const char * const rpi_hwmon_labels[] = {
31+
"core",
32+
"sdram_c",
33+
"sdram_i",
34+
"sdram_p",
35+
};
36+
2837
static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
2938
{
3039
u32 new_uv, old_uv, value;
@@ -56,6 +65,22 @@ static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data)
5665
hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0);
5766
}
5867

68+
static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id,
69+
long *val)
70+
{
71+
struct rpi_firmware_get_voltage_request packet =
72+
RPI_FIRMWARE_GET_VOLTAGE_REQUEST(id);
73+
int ret;
74+
75+
ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE,
76+
&packet, sizeof(packet));
77+
if (ret)
78+
return ret;
79+
80+
*val = le32_to_cpu(packet.value) / 1000;
81+
return 0;
82+
}
83+
5984
static void get_values_poll(struct work_struct *work)
6085
{
6186
struct rpi_hwmon_data *data;
@@ -77,19 +102,94 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
77102
{
78103
struct rpi_hwmon_data *data = dev_get_drvdata(dev);
79104

80-
*val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
105+
if (type == hwmon_in) {
106+
switch (attr) {
107+
case hwmon_in_input:
108+
switch (channel) {
109+
case 0:
110+
return rpi_firmware_get_voltage(data,
111+
RPI_FIRMWARE_VOLT_ID_CORE,
112+
val);
113+
case 1:
114+
return rpi_firmware_get_voltage(data,
115+
RPI_FIRMWARE_VOLT_ID_SDRAM_C,
116+
val);
117+
case 2:
118+
return rpi_firmware_get_voltage(data,
119+
RPI_FIRMWARE_VOLT_ID_SDRAM_I,
120+
val);
121+
case 3:
122+
return rpi_firmware_get_voltage(data,
123+
RPI_FIRMWARE_VOLT_ID_SDRAM_P,
124+
val);
125+
default:
126+
return -EOPNOTSUPP;
127+
}
128+
case hwmon_in_lcrit_alarm:
129+
if (channel == 0) {
130+
*val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT);
131+
return 0;
132+
}
133+
return -EOPNOTSUPP;
134+
default:
135+
return -EOPNOTSUPP;
136+
}
137+
}
138+
139+
return -EOPNOTSUPP;
140+
}
141+
142+
static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type,
143+
u32 attr, int channel, const char **str)
144+
{
145+
if (type == hwmon_in && attr == hwmon_in_label) {
146+
if (channel >= ARRAY_SIZE(rpi_hwmon_labels))
147+
return -EOPNOTSUPP;
148+
149+
*str = rpi_hwmon_labels[channel];
150+
return 0;
151+
}
152+
153+
return -EOPNOTSUPP;
154+
}
155+
156+
static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
157+
u32 attr, int channel)
158+
{
159+
const struct rpi_hwmon_data *data = _data;
160+
161+
if (type == hwmon_in) {
162+
switch (attr) {
163+
case hwmon_in_input:
164+
case hwmon_in_label:
165+
if (!(data->valid_inputs & BIT(channel)))
166+
return 0;
167+
return 0444;
168+
case hwmon_in_lcrit_alarm:
169+
if (channel == 0)
170+
return 0444;
171+
return 0;
172+
default:
173+
return 0;
174+
}
175+
}
176+
81177
return 0;
82178
}
83179

84180
static const struct hwmon_channel_info * const rpi_info[] = {
85181
HWMON_CHANNEL_INFO(in,
86-
HWMON_I_LCRIT_ALARM),
182+
HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM,
183+
HWMON_I_INPUT | HWMON_I_LABEL,
184+
HWMON_I_INPUT | HWMON_I_LABEL,
185+
HWMON_I_INPUT | HWMON_I_LABEL),
87186
NULL
88187
};
89188

90189
static const struct hwmon_ops rpi_hwmon_ops = {
91-
.visible = 0444,
190+
.is_visible = rpi_is_visible,
92191
.read = rpi_read,
192+
.read_string = rpi_read_string,
93193
};
94194

95195
static const struct hwmon_chip_info rpi_chip_info = {
@@ -101,6 +201,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
101201
{
102202
struct device *dev = &pdev->dev;
103203
struct rpi_hwmon_data *data;
204+
long voltage;
104205
int ret;
105206

106207
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
@@ -110,6 +211,26 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
110211
/* Parent driver assure that firmware is correct */
111212
data->fw = dev_get_drvdata(dev->parent);
112213

214+
ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_CORE,
215+
&voltage);
216+
if (!ret)
217+
data->valid_inputs |= BIT(0);
218+
219+
ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_C,
220+
&voltage);
221+
if (!ret)
222+
data->valid_inputs |= BIT(1);
223+
224+
ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_I,
225+
&voltage);
226+
if (!ret)
227+
data->valid_inputs |= BIT(2);
228+
229+
ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_P,
230+
&voltage);
231+
if (!ret)
232+
data->valid_inputs |= BIT(3);
233+
113234
data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt",
114235
data,
115236
&rpi_chip_info,
@@ -159,6 +280,7 @@ static struct platform_driver rpi_hwmon_driver = {
159280
module_platform_driver(rpi_hwmon_driver);
160281

161282
MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>");
283+
MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>");
162284
MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver");
163285
MODULE_LICENSE("GPL v2");
164286
MODULE_ALIAS("platform:raspberrypi-hwmon");

0 commit comments

Comments
 (0)