1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Raspberry Pi voltage sensor driver 4 * 5 * Based on firmware/raspberrypi.c by Noralf Trønnes 6 * 7 * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com> 8 * Copyright (C) 2026 Shubham Chakraborty <chakrabortyshubham66@gmail.com> 9 */ 10 #include <linux/device.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> 18 19 #define UNDERVOLTAGE_STICKY_BIT BIT(16) 20 21 struct rpi_hwmon_data { 22 struct device *hwmon_dev; 23 struct rpi_firmware *fw; 24 u32 valid_inputs; 25 u32 last_throttled; 26 struct delayed_work get_values_poll_work; 27 }; 28 29 static const char * const rpi_hwmon_labels[] = { 30 "core", 31 "sdram_c", 32 "sdram_i", 33 "sdram_p", 34 }; 35 36 static void rpi_firmware_get_throttled(struct rpi_hwmon_data *data) 37 { 38 u32 new_uv, old_uv, value; 39 int ret; 40 41 /* Request firmware to clear sticky bits */ 42 value = 0xffff; 43 44 ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_THROTTLED, 45 &value, sizeof(value)); 46 if (ret) { 47 dev_err_once(data->hwmon_dev, "Failed to get throttled (%d)\n", 48 ret); 49 return; 50 } 51 52 new_uv = value & UNDERVOLTAGE_STICKY_BIT; 53 old_uv = data->last_throttled & UNDERVOLTAGE_STICKY_BIT; 54 data->last_throttled = value; 55 56 if (new_uv == old_uv) 57 return; 58 59 if (new_uv) 60 dev_crit(data->hwmon_dev, "Undervoltage detected!\n"); 61 else 62 dev_info(data->hwmon_dev, "Voltage normalised\n"); 63 64 hwmon_notify_event(data->hwmon_dev, hwmon_in, hwmon_in_lcrit_alarm, 0); 65 } 66 67 static int rpi_firmware_get_voltage(struct rpi_hwmon_data *data, u32 id, 68 long *val) 69 { 70 struct rpi_firmware_get_voltage_request packet = 71 RPI_FIRMWARE_GET_VOLTAGE_REQUEST(id); 72 int ret; 73 74 ret = rpi_firmware_property(data->fw, RPI_FIRMWARE_GET_VOLTAGE, 75 &packet, sizeof(packet)); 76 if (ret) 77 return ret; 78 79 *val = le32_to_cpu(packet.value) / 1000; 80 return 0; 81 } 82 83 static void get_values_poll(struct work_struct *work) 84 { 85 struct rpi_hwmon_data *data; 86 87 data = container_of(work, struct rpi_hwmon_data, 88 get_values_poll_work.work); 89 90 rpi_firmware_get_throttled(data); 91 92 /* 93 * We can't run faster than the sticky shift (100ms) since we get 94 * flipping in the sticky bits that are cleared. 95 */ 96 schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); 97 } 98 99 static void rpi_hwmon_cancel_poll_work(void *res) 100 { 101 struct rpi_hwmon_data *data = res; 102 103 disable_delayed_work_sync(&data->get_values_poll_work); 104 } 105 106 static int rpi_read(struct device *dev, enum hwmon_sensor_types type, 107 u32 attr, int channel, long *val) 108 { 109 struct rpi_hwmon_data *data = dev_get_drvdata(dev); 110 111 if (type == hwmon_in) { 112 switch (attr) { 113 case hwmon_in_input: 114 switch (channel) { 115 case 0: 116 return rpi_firmware_get_voltage(data, 117 RPI_FIRMWARE_VOLT_ID_CORE, 118 val); 119 case 1: 120 return rpi_firmware_get_voltage(data, 121 RPI_FIRMWARE_VOLT_ID_SDRAM_C, 122 val); 123 case 2: 124 return rpi_firmware_get_voltage(data, 125 RPI_FIRMWARE_VOLT_ID_SDRAM_I, 126 val); 127 case 3: 128 return rpi_firmware_get_voltage(data, 129 RPI_FIRMWARE_VOLT_ID_SDRAM_P, 130 val); 131 default: 132 return -EOPNOTSUPP; 133 } 134 case hwmon_in_lcrit_alarm: 135 if (channel == 0) { 136 *val = !!(data->last_throttled & UNDERVOLTAGE_STICKY_BIT); 137 return 0; 138 } 139 return -EOPNOTSUPP; 140 default: 141 return -EOPNOTSUPP; 142 } 143 } 144 145 return -EOPNOTSUPP; 146 } 147 148 static int rpi_read_string(struct device *dev, enum hwmon_sensor_types type, 149 u32 attr, int channel, const char **str) 150 { 151 if (type == hwmon_in && attr == hwmon_in_label) { 152 if (channel >= ARRAY_SIZE(rpi_hwmon_labels)) 153 return -EOPNOTSUPP; 154 155 *str = rpi_hwmon_labels[channel]; 156 return 0; 157 } 158 159 return -EOPNOTSUPP; 160 } 161 162 static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type, 163 u32 attr, int channel) 164 { 165 const struct rpi_hwmon_data *data = _data; 166 167 if (type == hwmon_in) { 168 switch (attr) { 169 case hwmon_in_input: 170 case hwmon_in_label: 171 if (!(data->valid_inputs & BIT(channel))) 172 return 0; 173 return 0444; 174 case hwmon_in_lcrit_alarm: 175 if (channel == 0) 176 return 0444; 177 return 0; 178 default: 179 return 0; 180 } 181 } 182 183 return 0; 184 } 185 186 static const struct hwmon_channel_info * const rpi_info[] = { 187 HWMON_CHANNEL_INFO(in, 188 HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT_ALARM, 189 HWMON_I_INPUT | HWMON_I_LABEL, 190 HWMON_I_INPUT | HWMON_I_LABEL, 191 HWMON_I_INPUT | HWMON_I_LABEL), 192 NULL 193 }; 194 195 static const struct hwmon_ops rpi_hwmon_ops = { 196 .is_visible = rpi_is_visible, 197 .read = rpi_read, 198 .read_string = rpi_read_string, 199 }; 200 201 static const struct hwmon_chip_info rpi_chip_info = { 202 .ops = &rpi_hwmon_ops, 203 .info = rpi_info, 204 }; 205 206 static int rpi_hwmon_probe(struct platform_device *pdev) 207 { 208 struct device *dev = &pdev->dev; 209 struct rpi_hwmon_data *data; 210 long voltage; 211 int ret; 212 213 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 214 if (!data) 215 return -ENOMEM; 216 217 /* Parent driver assure that firmware is correct */ 218 data->fw = dev_get_drvdata(dev->parent); 219 220 ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_CORE, 221 &voltage); 222 if (!ret) 223 data->valid_inputs |= BIT(0); 224 225 ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_C, 226 &voltage); 227 if (!ret) 228 data->valid_inputs |= BIT(1); 229 230 ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_I, 231 &voltage); 232 if (!ret) 233 data->valid_inputs |= BIT(2); 234 235 ret = rpi_firmware_get_voltage(data, RPI_FIRMWARE_VOLT_ID_SDRAM_P, 236 &voltage); 237 if (!ret) 238 data->valid_inputs |= BIT(3); 239 240 data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "rpi_volt", 241 data, 242 &rpi_chip_info, 243 NULL); 244 if (IS_ERR(data->hwmon_dev)) 245 return PTR_ERR(data->hwmon_dev); 246 247 INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); 248 ret = devm_add_action_or_reset(dev, rpi_hwmon_cancel_poll_work, data); 249 if (ret) 250 return ret; 251 platform_set_drvdata(pdev, data); 252 253 schedule_delayed_work(&data->get_values_poll_work, 2 * HZ); 254 255 return 0; 256 } 257 258 static int rpi_hwmon_suspend(struct device *dev) 259 { 260 struct rpi_hwmon_data *data = dev_get_drvdata(dev); 261 262 cancel_delayed_work_sync(&data->get_values_poll_work); 263 264 return 0; 265 } 266 267 static int rpi_hwmon_resume(struct device *dev) 268 { 269 struct rpi_hwmon_data *data = dev_get_drvdata(dev); 270 271 get_values_poll(&data->get_values_poll_work.work); 272 273 return 0; 274 } 275 276 static DEFINE_SIMPLE_DEV_PM_OPS(rpi_hwmon_pm_ops, rpi_hwmon_suspend, 277 rpi_hwmon_resume); 278 279 static struct platform_driver rpi_hwmon_driver = { 280 .probe = rpi_hwmon_probe, 281 .driver = { 282 .name = "raspberrypi-hwmon", 283 .pm = pm_ptr(&rpi_hwmon_pm_ops), 284 }, 285 }; 286 module_platform_driver(rpi_hwmon_driver); 287 288 MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); 289 MODULE_AUTHOR("Shubham Chakraborty <chakrabortyshubham66@gmail.com>"); 290 MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); 291 MODULE_LICENSE("GPL v2"); 292 MODULE_ALIAS("platform:raspberrypi-hwmon"); 293