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
max77705_read_and_convert(struct regmap * regmap,u8 reg,u32 res,bool is_signed,long * val)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
max77705_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)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
max77705_read_string(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,const char ** buf)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
max77705_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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
max77705_hwmon_probe(struct platform_device * pdev)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