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>
10 #include "power_supply.h"
12 struct power_supply_hwmon
{
13 struct power_supply
*psy
;
17 static const char *const ps_temp_label
[] = {
22 static int power_supply_hwmon_in_to_property(u32 attr
)
25 case hwmon_in_average
:
26 return POWER_SUPPLY_PROP_VOLTAGE_AVG
;
28 return POWER_SUPPLY_PROP_VOLTAGE_MIN
;
30 return POWER_SUPPLY_PROP_VOLTAGE_MAX
;
32 return POWER_SUPPLY_PROP_VOLTAGE_NOW
;
38 static int power_supply_hwmon_curr_to_property(u32 attr
)
41 case hwmon_curr_average
:
42 return POWER_SUPPLY_PROP_CURRENT_AVG
;
44 return POWER_SUPPLY_PROP_CURRENT_MAX
;
45 case hwmon_curr_input
:
46 return POWER_SUPPLY_PROP_CURRENT_NOW
;
52 static int power_supply_hwmon_power_to_property(u32 attr
)
55 case hwmon_power_input
:
56 return POWER_SUPPLY_PROP_POWER_NOW
;
57 case hwmon_power_average
:
58 return POWER_SUPPLY_PROP_POWER_AVG
;
64 static int power_supply_hwmon_temp_to_property(u32 attr
, int channel
)
68 case hwmon_temp_input
:
69 return POWER_SUPPLY_PROP_TEMP_AMBIENT
;
70 case hwmon_temp_min_alarm
:
71 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN
;
72 case hwmon_temp_max_alarm
:
73 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX
;
79 case hwmon_temp_input
:
80 return POWER_SUPPLY_PROP_TEMP
;
82 return POWER_SUPPLY_PROP_TEMP_MAX
;
84 return POWER_SUPPLY_PROP_TEMP_MIN
;
85 case hwmon_temp_min_alarm
:
86 return POWER_SUPPLY_PROP_TEMP_ALERT_MIN
;
87 case hwmon_temp_max_alarm
:
88 return POWER_SUPPLY_PROP_TEMP_ALERT_MAX
;
98 power_supply_hwmon_to_property(enum hwmon_sensor_types type
,
99 u32 attr
, int channel
)
103 return power_supply_hwmon_in_to_property(attr
);
105 return power_supply_hwmon_curr_to_property(attr
);
107 return power_supply_hwmon_power_to_property(attr
);
109 return power_supply_hwmon_temp_to_property(attr
, channel
);
115 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type
,
118 return type
== hwmon_temp
&& attr
== hwmon_temp_label
;
121 struct hwmon_type_attr_list
{
126 static const u32 ps_temp_attrs
[] = {
128 hwmon_temp_min
, hwmon_temp_max
,
129 hwmon_temp_min_alarm
, hwmon_temp_max_alarm
,
132 static const struct hwmon_type_attr_list ps_type_attrs
[hwmon_max
] = {
133 [hwmon_temp
] = { ps_temp_attrs
, ARRAY_SIZE(ps_temp_attrs
) },
136 static bool power_supply_hwmon_has_input(
137 const struct power_supply_hwmon
*psyhw
,
138 enum hwmon_sensor_types type
, int channel
)
140 const struct hwmon_type_attr_list
*attr_list
= &ps_type_attrs
[type
];
143 for (i
= 0; i
< attr_list
->n_attrs
; ++i
) {
144 int prop
= power_supply_hwmon_to_property(type
,
145 attr_list
->attrs
[i
], channel
);
147 if (prop
>= 0 && test_bit(prop
, psyhw
->props
))
154 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type
,
159 return attr
== hwmon_in_min
||
160 attr
== hwmon_in_max
;
162 return attr
== hwmon_curr_max
;
164 return attr
== hwmon_temp_max
||
165 attr
== hwmon_temp_min
||
166 attr
== hwmon_temp_min_alarm
||
167 attr
== hwmon_temp_max_alarm
;
173 static umode_t
power_supply_hwmon_is_visible(const void *data
,
174 enum hwmon_sensor_types type
,
175 u32 attr
, int channel
)
177 const struct power_supply_hwmon
*psyhw
= data
;
180 if (power_supply_hwmon_is_a_label(type
, attr
)) {
181 if (power_supply_hwmon_has_input(psyhw
, type
, channel
))
187 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
188 if (prop
< 0 || !test_bit(prop
, psyhw
->props
))
191 if (power_supply_property_is_writeable(psyhw
->psy
, prop
) > 0 &&
192 power_supply_hwmon_is_writable(type
, attr
))
198 static int power_supply_hwmon_read_string(struct device
*dev
,
199 enum hwmon_sensor_types type
,
200 u32 attr
, int channel
,
205 *str
= ps_temp_label
[channel
];
208 /* unreachable, but see:
209 * gcc bug #51513 [1] and clang bug #978 [2]
211 * [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513
212 * [2] https://github.com/ClangBuiltLinux/linux/issues/978
221 power_supply_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
222 u32 attr
, int channel
, long *val
)
224 struct power_supply_hwmon
*psyhw
= dev_get_drvdata(dev
);
225 struct power_supply
*psy
= psyhw
->psy
;
226 union power_supply_propval pspval
;
229 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
233 ret
= power_supply_get_property(psy
, prop
, &pspval
);
239 * Both voltage and current is reported in units of
240 * microvolts/microamps, so we need to adjust it to
245 pspval
.intval
= DIV_ROUND_CLOSEST(pspval
.intval
, 1000);
249 * Power properties are already in microwatts.
253 * Temp needs to be converted from 1/10 C to milli-C
256 if (check_mul_overflow(pspval
.intval
, 100,
264 *val
= pspval
.intval
;
270 power_supply_hwmon_write(struct device
*dev
, enum hwmon_sensor_types type
,
271 u32 attr
, int channel
, long val
)
273 struct power_supply_hwmon
*psyhw
= dev_get_drvdata(dev
);
274 struct power_supply
*psy
= psyhw
->psy
;
275 union power_supply_propval pspval
;
278 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
286 * Both voltage and current is reported in units of
287 * microvolts/microamps, so we need to adjust it to
292 if (check_mul_overflow(pspval
.intval
, 1000,
297 * Temp needs to be converted from 1/10 C to milli-C
300 pspval
.intval
= DIV_ROUND_CLOSEST(pspval
.intval
, 100);
306 return power_supply_set_property(psy
, prop
, &pspval
);
309 static const struct hwmon_ops power_supply_hwmon_ops
= {
310 .is_visible
= power_supply_hwmon_is_visible
,
311 .read
= power_supply_hwmon_read
,
312 .write
= power_supply_hwmon_write
,
313 .read_string
= power_supply_hwmon_read_string
,
316 static const struct hwmon_channel_info
* const power_supply_hwmon_info
[] = {
317 HWMON_CHANNEL_INFO(temp
,
330 HWMON_CHANNEL_INFO(curr
,
335 HWMON_CHANNEL_INFO(power
,
339 HWMON_CHANNEL_INFO(in
,
347 static const struct hwmon_chip_info power_supply_hwmon_chip_info
= {
348 .ops
= &power_supply_hwmon_ops
,
349 .info
= power_supply_hwmon_info
,
352 int power_supply_add_hwmon_sysfs(struct power_supply
*psy
)
354 const struct power_supply_desc
*desc
= psy
->desc
;
355 struct power_supply_hwmon
*psyhw
;
356 struct device
*dev
= &psy
->dev
;
357 struct device
*hwmon
;
361 if (!devres_open_group(dev
, power_supply_add_hwmon_sysfs
,
365 psyhw
= devm_kzalloc(dev
, sizeof(*psyhw
), GFP_KERNEL
);
372 psyhw
->props
= devm_bitmap_zalloc(dev
,
373 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG
+ 1,
380 for (i
= 0; i
< desc
->num_properties
; i
++) {
381 const enum power_supply_property prop
= desc
->properties
[i
];
384 case POWER_SUPPLY_PROP_CURRENT_AVG
:
385 case POWER_SUPPLY_PROP_CURRENT_MAX
:
386 case POWER_SUPPLY_PROP_CURRENT_NOW
:
387 case POWER_SUPPLY_PROP_POWER_AVG
:
388 case POWER_SUPPLY_PROP_POWER_NOW
:
389 case POWER_SUPPLY_PROP_TEMP
:
390 case POWER_SUPPLY_PROP_TEMP_MAX
:
391 case POWER_SUPPLY_PROP_TEMP_MIN
:
392 case POWER_SUPPLY_PROP_TEMP_ALERT_MIN
:
393 case POWER_SUPPLY_PROP_TEMP_ALERT_MAX
:
394 case POWER_SUPPLY_PROP_TEMP_AMBIENT
:
395 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN
:
396 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX
:
397 case POWER_SUPPLY_PROP_VOLTAGE_AVG
:
398 case POWER_SUPPLY_PROP_VOLTAGE_MIN
:
399 case POWER_SUPPLY_PROP_VOLTAGE_MAX
:
400 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
401 set_bit(prop
, psyhw
->props
);
408 name
= psy
->desc
->name
;
409 if (strchr(name
, '-')) {
412 new_name
= devm_kstrdup(dev
, name
, GFP_KERNEL
);
417 strreplace(new_name
, '-', '_');
420 hwmon
= devm_hwmon_device_register_with_info(dev
, name
,
422 &power_supply_hwmon_chip_info
,
424 ret
= PTR_ERR_OR_ZERO(hwmon
);
428 devres_close_group(dev
, power_supply_add_hwmon_sysfs
);
431 devres_release_group(dev
, NULL
);
435 void power_supply_remove_hwmon_sysfs(struct power_supply
*psy
)
437 devres_release_group(&psy
->dev
, power_supply_add_hwmon_sysfs
);