Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (max77705) Add initial support

Maxim MAX77705 is a Companion Power Management and Type-C interface IC.
It includes charger and fuel gauge blocks, and is capable of measuring
charger input current, system bus volatage and current, and bypass
voltage.

Add support for mentioned measurements.

Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
Link: https://lore.kernel.org/r/20250423-initial-support-for-max77705-sensors-v6-1-ff379e1b06c5@gmail.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Dzmitry Sankouski and committed by
Guenter Roeck
8debd851 3e749ce1

+278
+1
Documentation/hwmon/index.rst
··· 163 163 max6639 164 164 max6650 165 165 max6697 166 + max77705 166 167 max8688 167 168 mc13783-adc 168 169 mc34vr500
+39
Documentation/hwmon/max77705.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + Kernel driver max77705 4 + ====================== 5 + 6 + Supported chips: 7 + 8 + * Maxim Integrated MAX77705 9 + 10 + Prefix: 'max77705' 11 + 12 + Addresses scanned: none 13 + 14 + Datasheet: Not available 15 + 16 + Authors: 17 + - Dzmitry Sankouski <dsankouski@gmail.com> 18 + 19 + Description 20 + ----------- 21 + 22 + The MAX77705 PMIC provides current and voltage measurements besides fuelgauge: 23 + - chip input current 24 + - system bus current and voltage 25 + - VBYP voltage 26 + 27 + Sysfs Attributes 28 + ---------------- 29 + 30 + ================= ======================================== 31 + in1_label "vbyp" 32 + in1_input Measured chip vbyp voltage 33 + in2_label "vsys" 34 + in2_input Measured chip system bus voltage 35 + curr1_label "iin" 36 + curr1_input Measured chip input current. 37 + curr2_label "isys" 38 + curr2_input Measured chip system bus current. 39 + ================= ========================================
+7
MAINTAINERS
··· 18433 18433 F: Documentation/hwmon/pc87427.rst 18434 18434 F: drivers/hwmon/pc87427.c 18435 18435 18436 + MAX77705 HARDWARE MONITORING DRIVER 18437 + M: Dzmitry Sankouski <dsankouski@gmail.com> 18438 + L: linux-hwmon@vger.kernel.org 18439 + S: Maintained 18440 + F: Documentation/hwmon/max77705.rst 18441 + F: drivers/hwmon/max77705-hwmon.c 18442 + 18436 18443 PCA9532 LED DRIVER 18437 18444 M: Riku Voipio <riku.voipio@iki.fi> 18438 18445 S: Maintained
+9
drivers/hwmon/Kconfig
··· 1318 1318 This driver can also be built as a module. If so, the module 1319 1319 will be called max31790. 1320 1320 1321 + config SENSORS_MAX77705 1322 + tristate "MAX77705 current and voltage sensor" 1323 + depends on MFD_MAX77705 1324 + help 1325 + If you say yes here you get support for MAX77705 sensors connected with I2C. 1326 + 1327 + This driver can also be built as a module. If so, the module 1328 + will be called max77705-hwmon. 1329 + 1321 1330 config SENSORS_MC34VR500 1322 1331 tristate "NXP MC34VR500 hardware monitoring driver" 1323 1332 depends on I2C
+1
drivers/hwmon/Makefile
··· 162 162 obj-$(CONFIG_SENSORS_MAX6697) += max6697.o 163 163 obj-$(CONFIG_SENSORS_MAX31790) += max31790.o 164 164 obj-$(CONFIG_MAX31827) += max31827.o 165 + obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o 165 166 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o 166 167 obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o 167 168 obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
+221
drivers/hwmon/max77705-hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * MAX77705 voltage and current hwmon driver. 4 + * 5 + * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com> 6 + */ 7 + 8 + #include <linux/err.h> 9 + #include <linux/hwmon-sysfs.h> 10 + #include <linux/hwmon.h> 11 + #include <linux/kernel.h> 12 + #include <linux/mfd/max77705-private.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/regmap.h> 15 + 16 + struct channel_desc { 17 + u8 reg; 18 + u8 avg_reg; 19 + const char *const label; 20 + // register resolution. nano Volts for voltage, nano Amperes for current 21 + u32 resolution; 22 + }; 23 + 24 + static const struct channel_desc current_channel_desc[] = { 25 + { 26 + .reg = IIN_REG, 27 + .label = "IIN_REG", 28 + .resolution = 125000 29 + }, 30 + { 31 + .reg = ISYS_REG, 32 + .avg_reg = AVGISYS_REG, 33 + .label = "ISYS_REG", 34 + .resolution = 312500 35 + } 36 + }; 37 + 38 + static const struct channel_desc voltage_channel_desc[] = { 39 + { 40 + .reg = VBYP_REG, 41 + .label = "VBYP_REG", 42 + .resolution = 427246 43 + }, 44 + { 45 + .reg = VSYS_REG, 46 + .label = "VSYS_REG", 47 + .resolution = 156250 48 + } 49 + }; 50 + 51 + static int max77705_read_and_convert(struct regmap *regmap, u8 reg, u32 res, 52 + bool is_signed, long *val) 53 + { 54 + int ret; 55 + u32 regval; 56 + 57 + ret = regmap_read(regmap, reg, &regval); 58 + if (ret < 0) 59 + return ret; 60 + 61 + if (is_signed) 62 + *val = mult_frac((long)sign_extend32(regval, 15), res, 1000000); 63 + else 64 + *val = mult_frac((long)regval, res, 1000000); 65 + 66 + return 0; 67 + } 68 + 69 + static umode_t max77705_is_visible(const void *data, 70 + enum hwmon_sensor_types type, 71 + u32 attr, int channel) 72 + { 73 + switch (type) { 74 + case hwmon_in: 75 + switch (attr) { 76 + case hwmon_in_input: 77 + case hwmon_in_label: 78 + return 0444; 79 + default: 80 + break; 81 + } 82 + break; 83 + case hwmon_curr: 84 + switch (attr) { 85 + case hwmon_curr_input: 86 + case hwmon_in_label: 87 + return 0444; 88 + case hwmon_curr_average: 89 + if (current_channel_desc[channel].avg_reg) 90 + return 0444; 91 + break; 92 + default: 93 + break; 94 + } 95 + break; 96 + default: 97 + break; 98 + } 99 + return 0; 100 + } 101 + 102 + static int max77705_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, 103 + int channel, const char **buf) 104 + { 105 + switch (type) { 106 + case hwmon_curr: 107 + switch (attr) { 108 + case hwmon_in_label: 109 + *buf = current_channel_desc[channel].label; 110 + return 0; 111 + default: 112 + return -EOPNOTSUPP; 113 + } 114 + 115 + case hwmon_in: 116 + switch (attr) { 117 + case hwmon_in_label: 118 + *buf = voltage_channel_desc[channel].label; 119 + return 0; 120 + default: 121 + return -EOPNOTSUPP; 122 + } 123 + default: 124 + return -EOPNOTSUPP; 125 + } 126 + } 127 + 128 + static int max77705_read(struct device *dev, enum hwmon_sensor_types type, 129 + u32 attr, int channel, long *val) 130 + { 131 + struct regmap *regmap = dev_get_drvdata(dev); 132 + u8 reg; 133 + u32 res; 134 + 135 + switch (type) { 136 + case hwmon_curr: 137 + switch (attr) { 138 + case hwmon_curr_input: 139 + reg = current_channel_desc[channel].reg; 140 + res = current_channel_desc[channel].resolution; 141 + 142 + return max77705_read_and_convert(regmap, reg, res, true, val); 143 + case hwmon_curr_average: 144 + reg = current_channel_desc[channel].avg_reg; 145 + res = current_channel_desc[channel].resolution; 146 + 147 + return max77705_read_and_convert(regmap, reg, res, true, val); 148 + default: 149 + return -EOPNOTSUPP; 150 + } 151 + 152 + case hwmon_in: 153 + switch (attr) { 154 + case hwmon_in_input: 155 + reg = voltage_channel_desc[channel].reg; 156 + res = voltage_channel_desc[channel].resolution; 157 + 158 + return max77705_read_and_convert(regmap, reg, res, false, val); 159 + default: 160 + return -EOPNOTSUPP; 161 + } 162 + default: 163 + return -EOPNOTSUPP; 164 + } 165 + 166 + return 0; 167 + } 168 + 169 + static const struct hwmon_ops max77705_hwmon_ops = { 170 + .is_visible = max77705_is_visible, 171 + .read = max77705_read, 172 + .read_string = max77705_read_string, 173 + }; 174 + 175 + static const struct hwmon_channel_info *max77705_info[] = { 176 + HWMON_CHANNEL_INFO(in, 177 + HWMON_I_INPUT | HWMON_I_LABEL, 178 + HWMON_I_INPUT | HWMON_I_LABEL 179 + ), 180 + HWMON_CHANNEL_INFO(curr, 181 + HWMON_C_INPUT | HWMON_C_LABEL, 182 + HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL 183 + ), 184 + NULL 185 + }; 186 + 187 + static const struct hwmon_chip_info max77705_chip_info = { 188 + .ops = &max77705_hwmon_ops, 189 + .info = max77705_info, 190 + }; 191 + 192 + static int max77705_hwmon_probe(struct platform_device *pdev) 193 + { 194 + struct device *hwmon_dev; 195 + struct regmap *regmap; 196 + 197 + regmap = dev_get_regmap(pdev->dev.parent, NULL); 198 + if (!regmap) 199 + return -ENODEV; 200 + 201 + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "max77705", regmap, 202 + &max77705_chip_info, NULL); 203 + if (IS_ERR(hwmon_dev)) 204 + return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev), 205 + "Unable to register hwmon device\n"); 206 + 207 + return 0; 208 + }; 209 + 210 + static struct platform_driver max77705_hwmon_driver = { 211 + .driver = { 212 + .name = "max77705-hwmon", 213 + }, 214 + .probe = max77705_hwmon_probe, 215 + }; 216 + 217 + module_platform_driver(max77705_hwmon_driver); 218 + 219 + MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>"); 220 + MODULE_DESCRIPTION("MAX77705 monitor driver"); 221 + MODULE_LICENSE("GPL");