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
*power_supply_hwmon_info
[] = {
297 HWMON_CHANNEL_INFO(temp
,
311 HWMON_CHANNEL_INFO(curr
,
316 HWMON_CHANNEL_INFO(in
,
324 static const struct hwmon_chip_info power_supply_hwmon_chip_info
= {
325 .ops
= &power_supply_hwmon_ops
,
326 .info
= power_supply_hwmon_info
,
329 static void power_supply_hwmon_bitmap_free(void *data
)
334 int power_supply_add_hwmon_sysfs(struct power_supply
*psy
)
336 const struct power_supply_desc
*desc
= psy
->desc
;
337 struct power_supply_hwmon
*psyhw
;
338 struct device
*dev
= &psy
->dev
;
339 struct device
*hwmon
;
343 if (!devres_open_group(dev
, power_supply_add_hwmon_sysfs
,
347 psyhw
= devm_kzalloc(dev
, sizeof(*psyhw
), GFP_KERNEL
);
354 psyhw
->props
= bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG
+ 1,
361 ret
= devm_add_action_or_reset(dev
, power_supply_hwmon_bitmap_free
,
366 for (i
= 0; i
< desc
->num_properties
; i
++) {
367 const enum power_supply_property prop
= desc
->properties
[i
];
370 case POWER_SUPPLY_PROP_CURRENT_AVG
:
371 case POWER_SUPPLY_PROP_CURRENT_MAX
:
372 case POWER_SUPPLY_PROP_CURRENT_NOW
:
373 case POWER_SUPPLY_PROP_TEMP
:
374 case POWER_SUPPLY_PROP_TEMP_MAX
:
375 case POWER_SUPPLY_PROP_TEMP_MIN
:
376 case POWER_SUPPLY_PROP_TEMP_ALERT_MIN
:
377 case POWER_SUPPLY_PROP_TEMP_ALERT_MAX
:
378 case POWER_SUPPLY_PROP_TEMP_AMBIENT
:
379 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN
:
380 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX
:
381 case POWER_SUPPLY_PROP_VOLTAGE_AVG
:
382 case POWER_SUPPLY_PROP_VOLTAGE_MIN
:
383 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
384 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
385 set_bit(prop
, psyhw
->props
);
392 name
= psy
->desc
->name
;
393 if (strchr(name
, '-')) {
396 new_name
= devm_kstrdup(dev
, name
, GFP_KERNEL
);
401 strreplace(new_name
, '-', '_');
404 hwmon
= devm_hwmon_device_register_with_info(dev
, name
,
406 &power_supply_hwmon_chip_info
,
408 ret
= PTR_ERR_OR_ZERO(hwmon
);
412 devres_close_group(dev
, power_supply_add_hwmon_sysfs
);
415 devres_release_group(dev
, NULL
);
419 void power_supply_remove_hwmon_sysfs(struct power_supply
*psy
)
421 devres_release_group(&psy
->dev
, power_supply_add_hwmon_sysfs
);