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 int power_supply_hwmon_in_to_property(u32 attr
)
19 case hwmon_in_average
:
20 return POWER_SUPPLY_PROP_VOLTAGE_AVG
;
22 return POWER_SUPPLY_PROP_VOLTAGE_MIN
;
24 return POWER_SUPPLY_PROP_VOLTAGE_MAX
;
26 return POWER_SUPPLY_PROP_VOLTAGE_NOW
;
32 static int power_supply_hwmon_curr_to_property(u32 attr
)
35 case hwmon_curr_average
:
36 return POWER_SUPPLY_PROP_CURRENT_AVG
;
38 return POWER_SUPPLY_PROP_CURRENT_MAX
;
39 case hwmon_curr_input
:
40 return POWER_SUPPLY_PROP_CURRENT_NOW
;
46 static int power_supply_hwmon_temp_to_property(u32 attr
, int channel
)
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
;
61 case hwmon_temp_input
:
62 return POWER_SUPPLY_PROP_TEMP
;
64 return POWER_SUPPLY_PROP_TEMP_MAX
;
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
;
80 power_supply_hwmon_to_property(enum hwmon_sensor_types type
,
81 u32 attr
, int channel
)
85 return power_supply_hwmon_in_to_property(attr
);
87 return power_supply_hwmon_curr_to_property(attr
);
89 return power_supply_hwmon_temp_to_property(attr
, channel
);
95 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type
,
98 return type
== hwmon_temp
&& attr
== hwmon_temp_label
;
101 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type
,
106 return attr
== hwmon_in_min
||
107 attr
== hwmon_in_max
;
109 return attr
== hwmon_curr_max
;
111 return attr
== hwmon_temp_max
||
112 attr
== hwmon_temp_min
||
113 attr
== hwmon_temp_min_alarm
||
114 attr
== hwmon_temp_max_alarm
;
120 static umode_t
power_supply_hwmon_is_visible(const void *data
,
121 enum hwmon_sensor_types type
,
122 u32 attr
, int channel
)
124 const struct power_supply_hwmon
*psyhw
= data
;
128 if (power_supply_hwmon_is_a_label(type
, attr
))
131 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
132 if (prop
< 0 || !test_bit(prop
, psyhw
->props
))
135 if (power_supply_property_is_writeable(psyhw
->psy
, prop
) > 0 &&
136 power_supply_hwmon_is_writable(type
, attr
))
142 static int power_supply_hwmon_read_string(struct device
*dev
,
143 enum hwmon_sensor_types type
,
144 u32 attr
, int channel
,
147 *str
= channel
? "temp" : "temp ambient";
152 power_supply_hwmon_read(struct device
*dev
, enum hwmon_sensor_types type
,
153 u32 attr
, int channel
, long *val
)
155 struct power_supply_hwmon
*psyhw
= dev_get_drvdata(dev
);
156 struct power_supply
*psy
= psyhw
->psy
;
157 union power_supply_propval pspval
;
160 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
164 ret
= power_supply_get_property(psy
, prop
, &pspval
);
170 * Both voltage and current is reported in units of
171 * microvolts/microamps, so we need to adjust it to
176 pspval
.intval
= DIV_ROUND_CLOSEST(pspval
.intval
, 1000);
179 * Temp needs to be converted from 1/10 C to milli-C
182 if (check_mul_overflow(pspval
.intval
, 100,
190 *val
= pspval
.intval
;
196 power_supply_hwmon_write(struct device
*dev
, enum hwmon_sensor_types type
,
197 u32 attr
, int channel
, long val
)
199 struct power_supply_hwmon
*psyhw
= dev_get_drvdata(dev
);
200 struct power_supply
*psy
= psyhw
->psy
;
201 union power_supply_propval pspval
;
204 prop
= power_supply_hwmon_to_property(type
, attr
, channel
);
212 * Both voltage and current is reported in units of
213 * microvolts/microamps, so we need to adjust it to
218 if (check_mul_overflow(pspval
.intval
, 1000,
223 * Temp needs to be converted from 1/10 C to milli-C
226 pspval
.intval
= DIV_ROUND_CLOSEST(pspval
.intval
, 100);
232 return power_supply_set_property(psy
, prop
, &pspval
);
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
,
242 static const struct hwmon_channel_info
*power_supply_hwmon_info
[] = {
243 HWMON_CHANNEL_INFO(temp
,
257 HWMON_CHANNEL_INFO(curr
,
262 HWMON_CHANNEL_INFO(in
,
270 static const struct hwmon_chip_info power_supply_hwmon_chip_info
= {
271 .ops
= &power_supply_hwmon_ops
,
272 .info
= power_supply_hwmon_info
,
275 static void power_supply_hwmon_bitmap_free(void *data
)
280 int power_supply_add_hwmon_sysfs(struct power_supply
*psy
)
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
;
289 if (!devres_open_group(dev
, power_supply_add_hwmon_sysfs
,
293 psyhw
= devm_kzalloc(dev
, sizeof(*psyhw
), GFP_KERNEL
);
300 psyhw
->props
= bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG
+ 1,
307 ret
= devm_add_action(dev
, power_supply_hwmon_bitmap_free
,
312 for (i
= 0; i
< desc
->num_properties
; i
++) {
313 const enum power_supply_property prop
= desc
->properties
[i
];
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
);
338 name
= psy
->desc
->name
;
339 if (strchr(name
, '-')) {
342 new_name
= devm_kstrdup(dev
, name
, GFP_KERNEL
);
347 strreplace(new_name
, '-', '_');
350 hwmon
= devm_hwmon_device_register_with_info(dev
, name
,
352 &power_supply_hwmon_chip_info
,
354 ret
= PTR_ERR_OR_ZERO(hwmon
);
358 devres_close_group(dev
, power_supply_add_hwmon_sysfs
);
361 devres_release_group(dev
, NULL
);
365 void power_supply_remove_hwmon_sysfs(struct power_supply
*psy
)
367 devres_release_group(&psy
->dev
, power_supply_add_hwmon_sysfs
);