LCOV - code coverage report
Current view: top level - drivers/power/supply - power_supply_hwmon.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 93 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 10 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  *  power_supply_hwmon.c - power supply hwmon support.
       4             :  */
       5             : 
       6             : #include <linux/err.h>
       7             : #include <linux/hwmon.h>
       8             : #include <linux/power_supply.h>
       9             : #include <linux/slab.h>
      10             : 
      11             : struct power_supply_hwmon {
      12             :         struct power_supply *psy;
      13             :         unsigned long *props;
      14             : };
      15             : 
      16             : static const char *const ps_temp_label[] = {
      17             :         "temp",
      18             :         "ambient temp",
      19             : };
      20             : 
      21             : static int power_supply_hwmon_in_to_property(u32 attr)
      22             : {
      23             :         switch (attr) {
      24             :         case hwmon_in_average:
      25             :                 return POWER_SUPPLY_PROP_VOLTAGE_AVG;
      26             :         case hwmon_in_min:
      27             :                 return POWER_SUPPLY_PROP_VOLTAGE_MIN;
      28             :         case hwmon_in_max:
      29             :                 return POWER_SUPPLY_PROP_VOLTAGE_MAX;
      30             :         case hwmon_in_input:
      31             :                 return POWER_SUPPLY_PROP_VOLTAGE_NOW;
      32             :         default:
      33             :                 return -EINVAL;
      34             :         }
      35             : }
      36             : 
      37             : static int power_supply_hwmon_curr_to_property(u32 attr)
      38             : {
      39             :         switch (attr) {
      40             :         case hwmon_curr_average:
      41             :                 return POWER_SUPPLY_PROP_CURRENT_AVG;
      42             :         case hwmon_curr_max:
      43             :                 return POWER_SUPPLY_PROP_CURRENT_MAX;
      44             :         case hwmon_curr_input:
      45             :                 return POWER_SUPPLY_PROP_CURRENT_NOW;
      46             :         default:
      47             :                 return -EINVAL;
      48             :         }
      49             : }
      50             : 
      51           0 : static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
      52             : {
      53           0 :         if (channel) {
      54           0 :                 switch (attr) {
      55             :                 case hwmon_temp_input:
      56             :                         return POWER_SUPPLY_PROP_TEMP_AMBIENT;
      57             :                 case hwmon_temp_min_alarm:
      58           0 :                         return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
      59             :                 case hwmon_temp_max_alarm:
      60           0 :                         return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
      61             :                 default:
      62             :                         break;
      63             :                 }
      64             :         } else {
      65           0 :                 switch (attr) {
      66             :                 case hwmon_temp_input:
      67             :                         return POWER_SUPPLY_PROP_TEMP;
      68             :                 case hwmon_temp_max:
      69           0 :                         return POWER_SUPPLY_PROP_TEMP_MAX;
      70             :                 case hwmon_temp_min:
      71           0 :                         return POWER_SUPPLY_PROP_TEMP_MIN;
      72             :                 case hwmon_temp_min_alarm:
      73           0 :                         return POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
      74             :                 case hwmon_temp_max_alarm:
      75           0 :                         return POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
      76             :                 default:
      77             :                         break;
      78             :                 }
      79             :         }
      80             : 
      81           0 :         return -EINVAL;
      82             : }
      83             : 
      84             : static int
      85           0 : power_supply_hwmon_to_property(enum hwmon_sensor_types type,
      86             :                                u32 attr, int channel)
      87             : {
      88           0 :         switch (type) {
      89             :         case hwmon_in:
      90             :                 return power_supply_hwmon_in_to_property(attr);
      91             :         case hwmon_curr:
      92             :                 return power_supply_hwmon_curr_to_property(attr);
      93             :         case hwmon_temp:
      94           0 :                 return power_supply_hwmon_temp_to_property(attr, channel);
      95             :         default:
      96             :                 return -EINVAL;
      97             :         }
      98             : }
      99             : 
     100             : static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
     101             :                                            u32 attr)
     102             : {
     103           0 :         return type == hwmon_temp && attr == hwmon_temp_label;
     104             : }
     105             : 
     106             : struct hwmon_type_attr_list {
     107             :         const u32 *attrs;
     108             :         size_t n_attrs;
     109             : };
     110             : 
     111             : static const u32 ps_temp_attrs[] = {
     112             :         hwmon_temp_input,
     113             :         hwmon_temp_min, hwmon_temp_max,
     114             :         hwmon_temp_min_alarm, hwmon_temp_max_alarm,
     115             : };
     116             : 
     117             : static const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = {
     118             :         [hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) },
     119             : };
     120             : 
     121           0 : static bool power_supply_hwmon_has_input(
     122             :         const struct power_supply_hwmon *psyhw,
     123             :         enum hwmon_sensor_types type, int channel)
     124             : {
     125             :         const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type];
     126             :         size_t i;
     127             : 
     128           0 :         for (i = 0; i < attr_list->n_attrs; ++i) {
     129           0 :                 int prop = power_supply_hwmon_to_property(type,
     130           0 :                         attr_list->attrs[i], channel);
     131             : 
     132           0 :                 if (prop >= 0 && test_bit(prop, psyhw->props))
     133             :                         return true;
     134             :         }
     135             : 
     136             :         return false;
     137             : }
     138             : 
     139           0 : static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
     140             :                                            u32 attr)
     141             : {
     142           0 :         switch (type) {
     143             :         case hwmon_in:
     144           0 :                 return attr == hwmon_in_min ||
     145             :                        attr == hwmon_in_max;
     146             :         case hwmon_curr:
     147           0 :                 return attr == hwmon_curr_max;
     148             :         case hwmon_temp:
     149           0 :                 return attr == hwmon_temp_max ||
     150           0 :                        attr == hwmon_temp_min ||
     151           0 :                        attr == hwmon_temp_min_alarm ||
     152             :                        attr == hwmon_temp_max_alarm;
     153             :         default:
     154             :                 return false;
     155             :         }
     156             : }
     157             : 
     158           0 : static umode_t power_supply_hwmon_is_visible(const void *data,
     159             :                                              enum hwmon_sensor_types type,
     160             :                                              u32 attr, int channel)
     161             : {
     162           0 :         const struct power_supply_hwmon *psyhw = data;
     163             :         int prop;
     164             : 
     165           0 :         if (power_supply_hwmon_is_a_label(type, attr)) {
     166           0 :                 if (power_supply_hwmon_has_input(psyhw, type, channel))
     167             :                         return 0444;
     168             :                 else
     169           0 :                         return 0;
     170             :         }
     171             : 
     172           0 :         prop = power_supply_hwmon_to_property(type, attr, channel);
     173           0 :         if (prop < 0 || !test_bit(prop, psyhw->props))
     174             :                 return 0;
     175             : 
     176           0 :         if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
     177           0 :             power_supply_hwmon_is_writable(type, attr))
     178             :                 return 0644;
     179             : 
     180             :         return 0444;
     181             : }
     182             : 
     183           0 : static int power_supply_hwmon_read_string(struct device *dev,
     184             :                                           enum hwmon_sensor_types type,
     185             :                                           u32 attr, int channel,
     186             :                                           const char **str)
     187             : {
     188           0 :         switch (type) {
     189             :         case hwmon_temp:
     190           0 :                 *str = ps_temp_label[channel];
     191           0 :                 break;
     192             :         default:
     193             :                 /* unreachable, but see:
     194             :                  * gcc bug #51513 [1] and clang bug #978 [2]
     195             :                  *
     196             :                  * [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51513
     197             :                  * [2] https://github.com/ClangBuiltLinux/linux/issues/978
     198             :                  */
     199             :                 break;
     200             :         }
     201             : 
     202           0 :         return 0;
     203             : }
     204             : 
     205             : static int
     206           0 : power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
     207             :                         u32 attr, int channel, long *val)
     208             : {
     209           0 :         struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
     210           0 :         struct power_supply *psy = psyhw->psy;
     211             :         union power_supply_propval pspval;
     212             :         int ret, prop;
     213             : 
     214           0 :         prop = power_supply_hwmon_to_property(type, attr, channel);
     215           0 :         if (prop < 0)
     216             :                 return prop;
     217             : 
     218           0 :         ret  = power_supply_get_property(psy, prop, &pspval);
     219           0 :         if (ret)
     220             :                 return ret;
     221             : 
     222           0 :         switch (type) {
     223             :         /*
     224             :          * Both voltage and current is reported in units of
     225             :          * microvolts/microamps, so we need to adjust it to
     226             :          * milliamps(volts)
     227             :          */
     228             :         case hwmon_curr:
     229             :         case hwmon_in:
     230           0 :                 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
     231           0 :                 break;
     232             :         /*
     233             :          * Temp needs to be converted from 1/10 C to milli-C
     234             :          */
     235             :         case hwmon_temp:
     236           0 :                 if (check_mul_overflow(pspval.intval, 100,
     237             :                                        &pspval.intval))
     238             :                         return -EOVERFLOW;
     239             :                 break;
     240             :         default:
     241             :                 return -EINVAL;
     242             :         }
     243             : 
     244           0 :         *val = pspval.intval;
     245             : 
     246           0 :         return 0;
     247             : }
     248             : 
     249             : static int
     250           0 : power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
     251             :                          u32 attr, int channel, long val)
     252             : {
     253           0 :         struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
     254           0 :         struct power_supply *psy = psyhw->psy;
     255             :         union power_supply_propval pspval;
     256             :         int prop;
     257             : 
     258           0 :         prop = power_supply_hwmon_to_property(type, attr, channel);
     259           0 :         if (prop < 0)
     260             :                 return prop;
     261             : 
     262           0 :         pspval.intval = val;
     263             : 
     264           0 :         switch (type) {
     265             :         /*
     266             :          * Both voltage and current is reported in units of
     267             :          * microvolts/microamps, so we need to adjust it to
     268             :          * milliamps(volts)
     269             :          */
     270             :         case hwmon_curr:
     271             :         case hwmon_in:
     272           0 :                 if (check_mul_overflow(pspval.intval, 1000,
     273             :                                        &pspval.intval))
     274             :                         return -EOVERFLOW;
     275             :                 break;
     276             :         /*
     277             :          * Temp needs to be converted from 1/10 C to milli-C
     278             :          */
     279             :         case hwmon_temp:
     280           0 :                 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
     281           0 :                 break;
     282             :         default:
     283             :                 return -EINVAL;
     284             :         }
     285             : 
     286           0 :         return power_supply_set_property(psy, prop, &pspval);
     287             : }
     288             : 
     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,
     294             : };
     295             : 
     296             : static const struct hwmon_channel_info *power_supply_hwmon_info[] = {
     297             :         HWMON_CHANNEL_INFO(temp,
     298             :                            HWMON_T_LABEL     |
     299             :                            HWMON_T_INPUT     |
     300             :                            HWMON_T_MAX       |
     301             :                            HWMON_T_MIN       |
     302             :                            HWMON_T_MIN_ALARM,
     303             : 
     304             :                            HWMON_T_LABEL     |
     305             :                            HWMON_T_INPUT     |
     306             :                            HWMON_T_MIN_ALARM |
     307             :                            HWMON_T_MAX_ALARM),
     308             : 
     309             :         HWMON_CHANNEL_INFO(curr,
     310             :                            HWMON_C_AVERAGE |
     311             :                            HWMON_C_MAX     |
     312             :                            HWMON_C_INPUT),
     313             : 
     314             :         HWMON_CHANNEL_INFO(in,
     315             :                            HWMON_I_AVERAGE |
     316             :                            HWMON_I_MIN     |
     317             :                            HWMON_I_MAX     |
     318             :                            HWMON_I_INPUT),
     319             :         NULL
     320             : };
     321             : 
     322             : static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
     323             :         .ops = &power_supply_hwmon_ops,
     324             :         .info = power_supply_hwmon_info,
     325             : };
     326             : 
     327           0 : int power_supply_add_hwmon_sysfs(struct power_supply *psy)
     328             : {
     329           0 :         const struct power_supply_desc *desc = psy->desc;
     330             :         struct power_supply_hwmon *psyhw;
     331           0 :         struct device *dev = &psy->dev;
     332             :         struct device *hwmon;
     333             :         int ret, i;
     334             :         const char *name;
     335             : 
     336           0 :         if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
     337             :                                GFP_KERNEL))
     338             :                 return -ENOMEM;
     339             : 
     340           0 :         psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
     341           0 :         if (!psyhw) {
     342             :                 ret = -ENOMEM;
     343             :                 goto error;
     344             :         }
     345             : 
     346           0 :         psyhw->psy = psy;
     347           0 :         psyhw->props = devm_bitmap_zalloc(dev,
     348             :                                           POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
     349             :                                           GFP_KERNEL);
     350           0 :         if (!psyhw->props) {
     351             :                 ret = -ENOMEM;
     352             :                 goto error;
     353             :         }
     354             : 
     355           0 :         for (i = 0; i < desc->num_properties; i++) {
     356           0 :                 const enum power_supply_property prop = desc->properties[i];
     357             : 
     358             :                 switch (prop) {
     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           0 :                         set_bit(prop, psyhw->props);
     375             :                         break;
     376             :                 default:
     377             :                         break;
     378             :                 }
     379             :         }
     380             : 
     381           0 :         name = psy->desc->name;
     382           0 :         if (strchr(name, '-')) {
     383             :                 char *new_name;
     384             : 
     385           0 :                 new_name = devm_kstrdup(dev, name, GFP_KERNEL);
     386           0 :                 if (!new_name) {
     387             :                         ret = -ENOMEM;
     388             :                         goto error;
     389             :                 }
     390           0 :                 strreplace(new_name, '-', '_');
     391           0 :                 name = new_name;
     392             :         }
     393           0 :         hwmon = devm_hwmon_device_register_with_info(dev, name,
     394             :                                                 psyhw,
     395             :                                                 &power_supply_hwmon_chip_info,
     396             :                                                 NULL);
     397           0 :         ret = PTR_ERR_OR_ZERO(hwmon);
     398           0 :         if (ret)
     399             :                 goto error;
     400             : 
     401           0 :         devres_close_group(dev, power_supply_add_hwmon_sysfs);
     402           0 :         return 0;
     403             : error:
     404           0 :         devres_release_group(dev, NULL);
     405           0 :         return ret;
     406             : }
     407             : 
     408           0 : void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
     409             : {
     410           0 :         devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);
     411           0 : }

Generated by: LCOV version 1.14