1 // SPDX-License-Identifier: GPL-2.0
3 * power_supply_hwmon.c - power supply hwmon support.
7 #include <linux/hwmon.h>
8 #include <linux/power_supply.h>
9 #include <linux/slab.h>
11 struct power_supply_hwmon {
12 struct power_supply *psy;
16 static const char *const ps_temp_label[] = {
21 static int power_supply_hwmon_in_to_property(u32 attr)
24 case hwmon_in_average:
25 return POWER_SUPPLY_PROP_VOLTAGE_AVG;
27 return POWER_SUPPLY_PROP_VOLTAGE_MIN;
29 return POWER_SUPPLY_PROP_VOLTAGE_MAX;
31 return POWER_SUPPLY_PROP_VOLTAGE_NOW;
37 static int power_supply_hwmon_curr_to_property(u32 attr)
40 case hwmon_curr_average:
41 return POWER_SUPPLY_PROP_CURRENT_AVG;
43 return POWER_SUPPLY_PROP_CURRENT_MAX;
44 case hwmon_curr_input:
45 return POWER_SUPPLY_PROP_CURRENT_NOW;
51 static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
55 case hwmon_temp_input:
56 return POWER_SUPPLY_PROP_TEMP_AMBIENT;
57 case hwmon_temp_min_alarm:
58 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
59 case hwmon_temp_max_alarm:
60 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
66 case hwmon_temp_input:
67 return POWER_SUPPLY_PROP_TEMP;
69 return POWER_SUPPLY_PROP_TEMP_MAX;
71 return POWER_SUPPLY_PROP_TEMP_MIN;
72 case hwmon_temp_min_alarm:
73 return POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
74 case hwmon_temp_max_alarm:
75 return POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
85 power_supply_hwmon_to_property(enum hwmon_sensor_types type,
86 u32 attr, int channel)
90 return power_supply_hwmon_in_to_property(attr);
92 return power_supply_hwmon_curr_to_property(attr);
94 return power_supply_hwmon_temp_to_property(attr, channel);
100 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
103 return type == hwmon_temp && attr == hwmon_temp_label;
106 struct hwmon_type_attr_list {
111 static const u32 ps_temp_attrs[] = {
113 hwmon_temp_min, hwmon_temp_max,
114 hwmon_temp_min_alarm, hwmon_temp_max_alarm,
117 static const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = {
118 [hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) },
121 static bool power_supply_hwmon_has_input(
122 const struct power_supply_hwmon *psyhw,
123 enum hwmon_sensor_types type, int channel)
125 const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type];
128 for (i = 0; i < attr_list->n_attrs; ++i) {
129 int prop = power_supply_hwmon_to_property(type,
130 attr_list->attrs[i], channel);
132 if (prop >= 0 && test_bit(prop, psyhw->props))
139 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
144 return attr == hwmon_in_min ||
145 attr == hwmon_in_max;
147 return attr == hwmon_curr_max;
149 return attr == hwmon_temp_max ||
150 attr == hwmon_temp_min ||
151 attr == hwmon_temp_min_alarm ||
152 attr == hwmon_temp_max_alarm;
158 static umode_t power_supply_hwmon_is_visible(const void *data,
159 enum hwmon_sensor_types type,
160 u32 attr, int channel)
162 const struct power_supply_hwmon *psyhw = data;
165 if (power_supply_hwmon_is_a_label(type, attr)) {
166 if (power_supply_hwmon_has_input(psyhw, type, channel))
172 prop = power_supply_hwmon_to_property(type, attr, channel);
173 if (prop < 0 || !test_bit(prop, psyhw->props))
176 if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
177 power_supply_hwmon_is_writable(type, attr))
183 static int power_supply_hwmon_read_string(struct device *dev,
184 enum hwmon_sensor_types type,
185 u32 attr, int channel,
190 *str = ps_temp_label[channel];
193 /* unreachable, but see:
194 * gcc bug #51513 [1] and clang bug #978 [2]
196 * [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513
197 * [2] https://github.com/ClangBuiltLinux/linux/issues/978
206 power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
207 u32 attr, int channel, long *val)
209 struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
210 struct power_supply *psy = psyhw->psy;
211 union power_supply_propval pspval;
214 prop = power_supply_hwmon_to_property(type, attr, channel);
218 ret = power_supply_get_property(psy, prop, &pspval);
224 * Both voltage and current is reported in units of
225 * microvolts/microamps, so we need to adjust it to
230 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
233 * Temp needs to be converted from 1/10 C to milli-C
236 if (check_mul_overflow(pspval.intval, 100,
244 *val = pspval.intval;
250 power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
251 u32 attr, int channel, long val)
253 struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
254 struct power_supply *psy = psyhw->psy;
255 union power_supply_propval pspval;
258 prop = power_supply_hwmon_to_property(type, attr, channel);
266 * Both voltage and current is reported in units of
267 * microvolts/microamps, so we need to adjust it to
272 if (check_mul_overflow(pspval.intval, 1000,
277 * Temp needs to be converted from 1/10 C to milli-C
280 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
286 return power_supply_set_property(psy, prop, &pspval);
289 static const struct hwmon_ops power_supply_hwmon_ops = {
290 .is_visible = power_supply_hwmon_is_visible,
291 .read = power_supply_hwmon_read,
292 .write = power_supply_hwmon_write,
293 .read_string = power_supply_hwmon_read_string,
296 static const struct hwmon_channel_info * const power_supply_hwmon_info[] = {
297 HWMON_CHANNEL_INFO(temp,
309 HWMON_CHANNEL_INFO(curr,
314 HWMON_CHANNEL_INFO(in,
322 static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
323 .ops = &power_supply_hwmon_ops,
324 .info = power_supply_hwmon_info,
327 int power_supply_add_hwmon_sysfs(struct power_supply *psy)
329 const struct power_supply_desc *desc = psy->desc;
330 struct power_supply_hwmon *psyhw;
331 struct device *dev = &psy->dev;
332 struct device *hwmon;
336 if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
340 psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
347 psyhw->props = devm_bitmap_zalloc(dev,
348 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
355 for (i = 0; i < desc->num_properties; i++) {
356 const enum power_supply_property prop = desc->properties[i];
359 case POWER_SUPPLY_PROP_CURRENT_AVG:
360 case POWER_SUPPLY_PROP_CURRENT_MAX:
361 case POWER_SUPPLY_PROP_CURRENT_NOW:
362 case POWER_SUPPLY_PROP_TEMP:
363 case POWER_SUPPLY_PROP_TEMP_MAX:
364 case POWER_SUPPLY_PROP_TEMP_MIN:
365 case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
366 case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
367 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
368 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
369 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
370 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
371 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
372 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
373 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
374 set_bit(prop, psyhw->props);
381 name = psy->desc->name;
382 if (strchr(name, '-')) {
385 new_name = devm_kstrdup(dev, name, GFP_KERNEL);
390 strreplace(new_name, '-', '_');
393 hwmon = devm_hwmon_device_register_with_info(dev, name,
395 &power_supply_hwmon_chip_info,
397 ret = PTR_ERR_OR_ZERO(hwmon);
401 devres_close_group(dev, power_supply_add_hwmon_sysfs);
404 devres_release_group(dev, NULL);
408 void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
410 devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);