18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * power_supply_hwmon.c - power supply hwmon support. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/err.h> 78c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 88c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct power_supply_hwmon { 128c2ecf20Sopenharmony_ci struct power_supply *psy; 138c2ecf20Sopenharmony_ci unsigned long *props; 148c2ecf20Sopenharmony_ci}; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic const char *const ps_temp_label[] = { 178c2ecf20Sopenharmony_ci "temp", 188c2ecf20Sopenharmony_ci "ambient temp", 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int power_supply_hwmon_in_to_property(u32 attr) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci switch (attr) { 248c2ecf20Sopenharmony_ci case hwmon_in_average: 258c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_VOLTAGE_AVG; 268c2ecf20Sopenharmony_ci case hwmon_in_min: 278c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_VOLTAGE_MIN; 288c2ecf20Sopenharmony_ci case hwmon_in_max: 298c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_VOLTAGE_MAX; 308c2ecf20Sopenharmony_ci case hwmon_in_input: 318c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_VOLTAGE_NOW; 328c2ecf20Sopenharmony_ci default: 338c2ecf20Sopenharmony_ci return -EINVAL; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int power_supply_hwmon_curr_to_property(u32 attr) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci switch (attr) { 408c2ecf20Sopenharmony_ci case hwmon_curr_average: 418c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_CURRENT_AVG; 428c2ecf20Sopenharmony_ci case hwmon_curr_max: 438c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_CURRENT_MAX; 448c2ecf20Sopenharmony_ci case hwmon_curr_input: 458c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_CURRENT_NOW; 468c2ecf20Sopenharmony_ci default: 478c2ecf20Sopenharmony_ci return -EINVAL; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int power_supply_hwmon_temp_to_property(u32 attr, int channel) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (channel) { 548c2ecf20Sopenharmony_ci switch (attr) { 558c2ecf20Sopenharmony_ci case hwmon_temp_input: 568c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_AMBIENT; 578c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 588c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN; 598c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 608c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX; 618c2ecf20Sopenharmony_ci default: 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } else { 658c2ecf20Sopenharmony_ci switch (attr) { 668c2ecf20Sopenharmony_ci case hwmon_temp_input: 678c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP; 688c2ecf20Sopenharmony_ci case hwmon_temp_max: 698c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_MAX; 708c2ecf20Sopenharmony_ci case hwmon_temp_min: 718c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_MIN; 728c2ecf20Sopenharmony_ci case hwmon_temp_min_alarm: 738c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_ALERT_MIN; 748c2ecf20Sopenharmony_ci case hwmon_temp_max_alarm: 758c2ecf20Sopenharmony_ci return POWER_SUPPLY_PROP_TEMP_ALERT_MAX; 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return -EINVAL; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int 858c2ecf20Sopenharmony_cipower_supply_hwmon_to_property(enum hwmon_sensor_types type, 868c2ecf20Sopenharmony_ci u32 attr, int channel) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci switch (type) { 898c2ecf20Sopenharmony_ci case hwmon_in: 908c2ecf20Sopenharmony_ci return power_supply_hwmon_in_to_property(attr); 918c2ecf20Sopenharmony_ci case hwmon_curr: 928c2ecf20Sopenharmony_ci return power_supply_hwmon_curr_to_property(attr); 938c2ecf20Sopenharmony_ci case hwmon_temp: 948c2ecf20Sopenharmony_ci return power_supply_hwmon_temp_to_property(attr, channel); 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type, 1018c2ecf20Sopenharmony_ci u32 attr) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return type == hwmon_temp && attr == hwmon_temp_label; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct hwmon_type_attr_list { 1078c2ecf20Sopenharmony_ci const u32 *attrs; 1088c2ecf20Sopenharmony_ci size_t n_attrs; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const u32 ps_temp_attrs[] = { 1128c2ecf20Sopenharmony_ci hwmon_temp_input, 1138c2ecf20Sopenharmony_ci hwmon_temp_min, hwmon_temp_max, 1148c2ecf20Sopenharmony_ci hwmon_temp_min_alarm, hwmon_temp_max_alarm, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = { 1188c2ecf20Sopenharmony_ci [hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) }, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic bool power_supply_hwmon_has_input( 1228c2ecf20Sopenharmony_ci const struct power_supply_hwmon *psyhw, 1238c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, int channel) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type]; 1268c2ecf20Sopenharmony_ci size_t i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (i = 0; i < attr_list->n_attrs; ++i) { 1298c2ecf20Sopenharmony_ci int prop = power_supply_hwmon_to_property(type, 1308c2ecf20Sopenharmony_ci attr_list->attrs[i], channel); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (prop >= 0 && test_bit(prop, psyhw->props)) 1338c2ecf20Sopenharmony_ci return true; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return false; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type, 1408c2ecf20Sopenharmony_ci u32 attr) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci switch (type) { 1438c2ecf20Sopenharmony_ci case hwmon_in: 1448c2ecf20Sopenharmony_ci return attr == hwmon_in_min || 1458c2ecf20Sopenharmony_ci attr == hwmon_in_max; 1468c2ecf20Sopenharmony_ci case hwmon_curr: 1478c2ecf20Sopenharmony_ci return attr == hwmon_curr_max; 1488c2ecf20Sopenharmony_ci case hwmon_temp: 1498c2ecf20Sopenharmony_ci return attr == hwmon_temp_max || 1508c2ecf20Sopenharmony_ci attr == hwmon_temp_min || 1518c2ecf20Sopenharmony_ci attr == hwmon_temp_min_alarm || 1528c2ecf20Sopenharmony_ci attr == hwmon_temp_max_alarm; 1538c2ecf20Sopenharmony_ci default: 1548c2ecf20Sopenharmony_ci return false; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic umode_t power_supply_hwmon_is_visible(const void *data, 1598c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 1608c2ecf20Sopenharmony_ci u32 attr, int channel) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci const struct power_supply_hwmon *psyhw = data; 1638c2ecf20Sopenharmony_ci int prop; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (power_supply_hwmon_is_a_label(type, attr)) { 1668c2ecf20Sopenharmony_ci if (power_supply_hwmon_has_input(psyhw, type, channel)) 1678c2ecf20Sopenharmony_ci return 0444; 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci prop = power_supply_hwmon_to_property(type, attr, channel); 1738c2ecf20Sopenharmony_ci if (prop < 0 || !test_bit(prop, psyhw->props)) 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 && 1778c2ecf20Sopenharmony_ci power_supply_hwmon_is_writable(type, attr)) 1788c2ecf20Sopenharmony_ci return 0644; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return 0444; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int power_supply_hwmon_read_string(struct device *dev, 1848c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 1858c2ecf20Sopenharmony_ci u32 attr, int channel, 1868c2ecf20Sopenharmony_ci const char **str) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci switch (type) { 1898c2ecf20Sopenharmony_ci case hwmon_temp: 1908c2ecf20Sopenharmony_ci *str = ps_temp_label[channel]; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci default: 1938c2ecf20Sopenharmony_ci /* unreachable, but see: 1948c2ecf20Sopenharmony_ci * gcc bug #51513 [1] and clang bug #978 [2] 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513 1978c2ecf20Sopenharmony_ci * [2] https://github.com/ClangBuiltLinux/linux/issues/978 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int 2068c2ecf20Sopenharmony_cipower_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 2078c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct power_supply_hwmon *psyhw = dev_get_drvdata(dev); 2108c2ecf20Sopenharmony_ci struct power_supply *psy = psyhw->psy; 2118c2ecf20Sopenharmony_ci union power_supply_propval pspval; 2128c2ecf20Sopenharmony_ci int ret, prop; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci prop = power_supply_hwmon_to_property(type, attr, channel); 2158c2ecf20Sopenharmony_ci if (prop < 0) 2168c2ecf20Sopenharmony_ci return prop; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, prop, &pspval); 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci switch (type) { 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * Both voltage and current is reported in units of 2258c2ecf20Sopenharmony_ci * microvolts/microamps, so we need to adjust it to 2268c2ecf20Sopenharmony_ci * milliamps(volts) 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci case hwmon_curr: 2298c2ecf20Sopenharmony_ci case hwmon_in: 2308c2ecf20Sopenharmony_ci pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Temp needs to be converted from 1/10 C to milli-C 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci case hwmon_temp: 2368c2ecf20Sopenharmony_ci if (check_mul_overflow(pspval.intval, 100, 2378c2ecf20Sopenharmony_ci &pspval.intval)) 2388c2ecf20Sopenharmony_ci return -EOVERFLOW; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci default: 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci *val = pspval.intval; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int 2508c2ecf20Sopenharmony_cipower_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 2518c2ecf20Sopenharmony_ci u32 attr, int channel, long val) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct power_supply_hwmon *psyhw = dev_get_drvdata(dev); 2548c2ecf20Sopenharmony_ci struct power_supply *psy = psyhw->psy; 2558c2ecf20Sopenharmony_ci union power_supply_propval pspval; 2568c2ecf20Sopenharmony_ci int prop; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci prop = power_supply_hwmon_to_property(type, attr, channel); 2598c2ecf20Sopenharmony_ci if (prop < 0) 2608c2ecf20Sopenharmony_ci return prop; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci pspval.intval = val; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci switch (type) { 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * Both voltage and current is reported in units of 2678c2ecf20Sopenharmony_ci * microvolts/microamps, so we need to adjust it to 2688c2ecf20Sopenharmony_ci * milliamps(volts) 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci case hwmon_curr: 2718c2ecf20Sopenharmony_ci case hwmon_in: 2728c2ecf20Sopenharmony_ci if (check_mul_overflow(pspval.intval, 1000, 2738c2ecf20Sopenharmony_ci &pspval.intval)) 2748c2ecf20Sopenharmony_ci return -EOVERFLOW; 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * Temp needs to be converted from 1/10 C to milli-C 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci case hwmon_temp: 2808c2ecf20Sopenharmony_ci pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci default: 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return power_supply_set_property(psy, prop, &pspval); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct hwmon_ops power_supply_hwmon_ops = { 2908c2ecf20Sopenharmony_ci .is_visible = power_supply_hwmon_is_visible, 2918c2ecf20Sopenharmony_ci .read = power_supply_hwmon_read, 2928c2ecf20Sopenharmony_ci .write = power_supply_hwmon_write, 2938c2ecf20Sopenharmony_ci .read_string = power_supply_hwmon_read_string, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *power_supply_hwmon_info[] = { 2978c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, 2988c2ecf20Sopenharmony_ci HWMON_T_LABEL | 2998c2ecf20Sopenharmony_ci HWMON_T_INPUT | 3008c2ecf20Sopenharmony_ci HWMON_T_MAX | 3018c2ecf20Sopenharmony_ci HWMON_T_MIN | 3028c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | 3038c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM, 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci HWMON_T_LABEL | 3068c2ecf20Sopenharmony_ci HWMON_T_INPUT | 3078c2ecf20Sopenharmony_ci HWMON_T_MIN_ALARM | 3088c2ecf20Sopenharmony_ci HWMON_T_LABEL | 3098c2ecf20Sopenharmony_ci HWMON_T_MAX_ALARM), 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(curr, 3128c2ecf20Sopenharmony_ci HWMON_C_AVERAGE | 3138c2ecf20Sopenharmony_ci HWMON_C_MAX | 3148c2ecf20Sopenharmony_ci HWMON_C_INPUT), 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(in, 3178c2ecf20Sopenharmony_ci HWMON_I_AVERAGE | 3188c2ecf20Sopenharmony_ci HWMON_I_MIN | 3198c2ecf20Sopenharmony_ci HWMON_I_MAX | 3208c2ecf20Sopenharmony_ci HWMON_I_INPUT), 3218c2ecf20Sopenharmony_ci NULL 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info power_supply_hwmon_chip_info = { 3258c2ecf20Sopenharmony_ci .ops = &power_supply_hwmon_ops, 3268c2ecf20Sopenharmony_ci .info = power_supply_hwmon_info, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void power_supply_hwmon_bitmap_free(void *data) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci bitmap_free(data); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciint power_supply_add_hwmon_sysfs(struct power_supply *psy) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci const struct power_supply_desc *desc = psy->desc; 3378c2ecf20Sopenharmony_ci struct power_supply_hwmon *psyhw; 3388c2ecf20Sopenharmony_ci struct device *dev = &psy->dev; 3398c2ecf20Sopenharmony_ci struct device *hwmon; 3408c2ecf20Sopenharmony_ci int ret, i; 3418c2ecf20Sopenharmony_ci const char *name; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!devres_open_group(dev, power_supply_add_hwmon_sysfs, 3448c2ecf20Sopenharmony_ci GFP_KERNEL)) 3458c2ecf20Sopenharmony_ci return -ENOMEM; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!psyhw) { 3498c2ecf20Sopenharmony_ci ret = -ENOMEM; 3508c2ecf20Sopenharmony_ci goto error; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci psyhw->psy = psy; 3548c2ecf20Sopenharmony_ci psyhw->props = bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1, 3558c2ecf20Sopenharmony_ci GFP_KERNEL); 3568c2ecf20Sopenharmony_ci if (!psyhw->props) { 3578c2ecf20Sopenharmony_ci ret = -ENOMEM; 3588c2ecf20Sopenharmony_ci goto error; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, power_supply_hwmon_bitmap_free, 3628c2ecf20Sopenharmony_ci psyhw->props); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci goto error; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < desc->num_properties; i++) { 3678c2ecf20Sopenharmony_ci const enum power_supply_property prop = desc->properties[i]; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci switch (prop) { 3708c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 3718c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 3728c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 3738c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 3748c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_MAX: 3758c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_MIN: 3768c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: 3778c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: 3788c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_AMBIENT: 3798c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: 3808c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: 3818c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 3828c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN: 3838c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 3848c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 3858c2ecf20Sopenharmony_ci set_bit(prop, psyhw->props); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci name = psy->desc->name; 3938c2ecf20Sopenharmony_ci if (strchr(name, '-')) { 3948c2ecf20Sopenharmony_ci char *new_name; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci new_name = devm_kstrdup(dev, name, GFP_KERNEL); 3978c2ecf20Sopenharmony_ci if (!new_name) { 3988c2ecf20Sopenharmony_ci ret = -ENOMEM; 3998c2ecf20Sopenharmony_ci goto error; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci strreplace(new_name, '-', '_'); 4028c2ecf20Sopenharmony_ci name = new_name; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci hwmon = devm_hwmon_device_register_with_info(dev, name, 4058c2ecf20Sopenharmony_ci psyhw, 4068c2ecf20Sopenharmony_ci &power_supply_hwmon_chip_info, 4078c2ecf20Sopenharmony_ci NULL); 4088c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(hwmon); 4098c2ecf20Sopenharmony_ci if (ret) 4108c2ecf20Sopenharmony_ci goto error; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci devres_close_group(dev, power_supply_add_hwmon_sysfs); 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_cierror: 4158c2ecf20Sopenharmony_ci devres_release_group(dev, NULL); 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_civoid power_supply_remove_hwmon_sysfs(struct power_supply *psy) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs); 4228c2ecf20Sopenharmony_ci} 423