1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  power_supply_hwmon.c - power supply hwmon support.
4  */
5 
6 #include <linux/err.h>
7 #include <linux/hwmon.h>
8 #include <linux/power_supply.h>
9 #include <linux/slab.h>
10 
11 struct power_supply_hwmon {
12 	struct power_supply *psy;
13 	unsigned long *props;
14 };
15 
16 static int power_supply_hwmon_in_to_property(u32 attr)
17 {
18 	switch (attr) {
19 	case hwmon_in_average:
20 		return POWER_SUPPLY_PROP_VOLTAGE_AVG;
21 	case hwmon_in_min:
22 		return POWER_SUPPLY_PROP_VOLTAGE_MIN;
23 	case hwmon_in_max:
24 		return POWER_SUPPLY_PROP_VOLTAGE_MAX;
25 	case hwmon_in_input:
26 		return POWER_SUPPLY_PROP_VOLTAGE_NOW;
27 	default:
28 		return -EINVAL;
29 	}
30 }
31 
32 static int power_supply_hwmon_curr_to_property(u32 attr)
33 {
34 	switch (attr) {
35 	case hwmon_curr_average:
36 		return POWER_SUPPLY_PROP_CURRENT_AVG;
37 	case hwmon_curr_max:
38 		return POWER_SUPPLY_PROP_CURRENT_MAX;
39 	case hwmon_curr_input:
40 		return POWER_SUPPLY_PROP_CURRENT_NOW;
41 	default:
42 		return -EINVAL;
43 	}
44 }
45 
46 static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
47 {
48 	if (channel) {
49 		switch (attr) {
50 		case hwmon_temp_input:
51 			return POWER_SUPPLY_PROP_TEMP_AMBIENT;
52 		case hwmon_temp_min_alarm:
53 			return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
54 		case hwmon_temp_max_alarm:
55 			return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
56 		default:
57 			break;
58 		}
59 	} else {
60 		switch (attr) {
61 		case hwmon_temp_input:
62 			return POWER_SUPPLY_PROP_TEMP;
63 		case hwmon_temp_max:
64 			return POWER_SUPPLY_PROP_TEMP_MAX;
65 		case hwmon_temp_min:
66 			return POWER_SUPPLY_PROP_TEMP_MIN;
67 		case hwmon_temp_min_alarm:
68 			return POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
69 		case hwmon_temp_max_alarm:
70 			return POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
71 		default:
72 			break;
73 		}
74 	}
75 
76 	return -EINVAL;
77 }
78 
79 static int
80 power_supply_hwmon_to_property(enum hwmon_sensor_types type,
81 			       u32 attr, int channel)
82 {
83 	switch (type) {
84 	case hwmon_in:
85 		return power_supply_hwmon_in_to_property(attr);
86 	case hwmon_curr:
87 		return power_supply_hwmon_curr_to_property(attr);
88 	case hwmon_temp:
89 		return power_supply_hwmon_temp_to_property(attr, channel);
90 	default:
91 		return -EINVAL;
92 	}
93 }
94 
95 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
96 					   u32 attr)
97 {
98 	return type == hwmon_temp && attr == hwmon_temp_label;
99 }
100 
101 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
102 					   u32 attr)
103 {
104 	switch (type) {
105 	case hwmon_in:
106 		return attr == hwmon_in_min ||
107 		       attr == hwmon_in_max;
108 	case hwmon_curr:
109 		return attr == hwmon_curr_max;
110 	case hwmon_temp:
111 		return attr == hwmon_temp_max ||
112 		       attr == hwmon_temp_min ||
113 		       attr == hwmon_temp_min_alarm ||
114 		       attr == hwmon_temp_max_alarm;
115 	default:
116 		return false;
117 	}
118 }
119 
120 static umode_t power_supply_hwmon_is_visible(const void *data,
121 					     enum hwmon_sensor_types type,
122 					     u32 attr, int channel)
123 {
124 	const struct power_supply_hwmon *psyhw = data;
125 	int prop;
126 
127 
128 	if (power_supply_hwmon_is_a_label(type, attr))
129 		return 0444;
130 
131 	prop = power_supply_hwmon_to_property(type, attr, channel);
132 	if (prop < 0 || !test_bit(prop, psyhw->props))
133 		return 0;
134 
135 	if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
136 	    power_supply_hwmon_is_writable(type, attr))
137 		return 0644;
138 
139 	return 0444;
140 }
141 
142 static int power_supply_hwmon_read_string(struct device *dev,
143 					  enum hwmon_sensor_types type,
144 					  u32 attr, int channel,
145 					  const char **str)
146 {
147 	*str = channel ? "temp" : "temp ambient";
148 	return 0;
149 }
150 
151 static int
152 power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
153 			u32 attr, int channel, long *val)
154 {
155 	struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
156 	struct power_supply *psy = psyhw->psy;
157 	union power_supply_propval pspval;
158 	int ret, prop;
159 
160 	prop = power_supply_hwmon_to_property(type, attr, channel);
161 	if (prop < 0)
162 		return prop;
163 
164 	ret  = power_supply_get_property(psy, prop, &pspval);
165 	if (ret)
166 		return ret;
167 
168 	switch (type) {
169 	/*
170 	 * Both voltage and current is reported in units of
171 	 * microvolts/microamps, so we need to adjust it to
172 	 * milliamps(volts)
173 	 */
174 	case hwmon_curr:
175 	case hwmon_in:
176 		pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
177 		break;
178 	/*
179 	 * Temp needs to be converted from 1/10 C to milli-C
180 	 */
181 	case hwmon_temp:
182 		if (check_mul_overflow(pspval.intval, 100,
183 				       &pspval.intval))
184 			return -EOVERFLOW;
185 		break;
186 	default:
187 		return -EINVAL;
188 	}
189 
190 	*val = pspval.intval;
191 
192 	return 0;
193 }
194 
195 static int
196 power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
197 			 u32 attr, int channel, long val)
198 {
199 	struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
200 	struct power_supply *psy = psyhw->psy;
201 	union power_supply_propval pspval;
202 	int prop;
203 
204 	prop = power_supply_hwmon_to_property(type, attr, channel);
205 	if (prop < 0)
206 		return prop;
207 
208 	pspval.intval = val;
209 
210 	switch (type) {
211 	/*
212 	 * Both voltage and current is reported in units of
213 	 * microvolts/microamps, so we need to adjust it to
214 	 * milliamps(volts)
215 	 */
216 	case hwmon_curr:
217 	case hwmon_in:
218 		if (check_mul_overflow(pspval.intval, 1000,
219 				       &pspval.intval))
220 			return -EOVERFLOW;
221 		break;
222 	/*
223 	 * Temp needs to be converted from 1/10 C to milli-C
224 	 */
225 	case hwmon_temp:
226 		pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
227 		break;
228 	default:
229 		return -EINVAL;
230 	}
231 
232 	return power_supply_set_property(psy, prop, &pspval);
233 }
234 
235 static const struct hwmon_ops power_supply_hwmon_ops = {
236 	.is_visible	= power_supply_hwmon_is_visible,
237 	.read		= power_supply_hwmon_read,
238 	.write		= power_supply_hwmon_write,
239 	.read_string	= power_supply_hwmon_read_string,
240 };
241 
242 static const struct hwmon_channel_info *power_supply_hwmon_info[] = {
243 	HWMON_CHANNEL_INFO(temp,
244 			   HWMON_T_LABEL     |
245 			   HWMON_T_INPUT     |
246 			   HWMON_T_MAX       |
247 			   HWMON_T_MIN       |
248 			   HWMON_T_MIN_ALARM |
249 			   HWMON_T_MIN_ALARM,
250 
251 			   HWMON_T_LABEL     |
252 			   HWMON_T_INPUT     |
253 			   HWMON_T_MIN_ALARM |
254 			   HWMON_T_LABEL     |
255 			   HWMON_T_MAX_ALARM),
256 
257 	HWMON_CHANNEL_INFO(curr,
258 			   HWMON_C_AVERAGE |
259 			   HWMON_C_MAX     |
260 			   HWMON_C_INPUT),
261 
262 	HWMON_CHANNEL_INFO(in,
263 			   HWMON_I_AVERAGE |
264 			   HWMON_I_MIN     |
265 			   HWMON_I_MAX     |
266 			   HWMON_I_INPUT),
267 	NULL
268 };
269 
270 static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
271 	.ops = &power_supply_hwmon_ops,
272 	.info = power_supply_hwmon_info,
273 };
274 
275 static void power_supply_hwmon_bitmap_free(void *data)
276 {
277 	bitmap_free(data);
278 }
279 
280 int power_supply_add_hwmon_sysfs(struct power_supply *psy)
281 {
282 	const struct power_supply_desc *desc = psy->desc;
283 	struct power_supply_hwmon *psyhw;
284 	struct device *dev = &psy->dev;
285 	struct device *hwmon;
286 	int ret, i;
287 	const char *name;
288 
289 	if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
290 			       GFP_KERNEL))
291 		return -ENOMEM;
292 
293 	psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
294 	if (!psyhw) {
295 		ret = -ENOMEM;
296 		goto error;
297 	}
298 
299 	psyhw->psy = psy;
300 	psyhw->props = bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
301 				     GFP_KERNEL);
302 	if (!psyhw->props) {
303 		ret = -ENOMEM;
304 		goto error;
305 	}
306 
307 	ret = devm_add_action(dev, power_supply_hwmon_bitmap_free,
308 			      psyhw->props);
309 	if (ret)
310 		goto error;
311 
312 	for (i = 0; i < desc->num_properties; i++) {
313 		const enum power_supply_property prop = desc->properties[i];
314 
315 		switch (prop) {
316 		case POWER_SUPPLY_PROP_CURRENT_AVG:
317 		case POWER_SUPPLY_PROP_CURRENT_MAX:
318 		case POWER_SUPPLY_PROP_CURRENT_NOW:
319 		case POWER_SUPPLY_PROP_TEMP:
320 		case POWER_SUPPLY_PROP_TEMP_MAX:
321 		case POWER_SUPPLY_PROP_TEMP_MIN:
322 		case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
323 		case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
324 		case POWER_SUPPLY_PROP_TEMP_AMBIENT:
325 		case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
326 		case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
327 		case POWER_SUPPLY_PROP_VOLTAGE_AVG:
328 		case POWER_SUPPLY_PROP_VOLTAGE_MIN:
329 		case POWER_SUPPLY_PROP_VOLTAGE_MAX:
330 		case POWER_SUPPLY_PROP_VOLTAGE_NOW:
331 			set_bit(prop, psyhw->props);
332 			break;
333 		default:
334 			break;
335 		}
336 	}
337 
338 	name = psy->desc->name;
339 	if (strchr(name, '-')) {
340 		char *new_name;
341 
342 		new_name = devm_kstrdup(dev, name, GFP_KERNEL);
343 		if (!new_name) {
344 			ret = -ENOMEM;
345 			goto error;
346 		}
347 		strreplace(new_name, '-', '_');
348 		name = new_name;
349 	}
350 	hwmon = devm_hwmon_device_register_with_info(dev, name,
351 						psyhw,
352 						&power_supply_hwmon_chip_info,
353 						NULL);
354 	ret = PTR_ERR_OR_ZERO(hwmon);
355 	if (ret)
356 		goto error;
357 
358 	devres_close_group(dev, power_supply_add_hwmon_sysfs);
359 	return 0;
360 error:
361 	devres_release_group(dev, NULL);
362 	return ret;
363 }
364 
365 void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
366 {
367 	devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);
368 }
369