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, ®val); 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"); 222