root/drivers/power/supply/power_supply_hwmon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. power_supply_hwmon_in_to_property
  2. power_supply_hwmon_curr_to_property
  3. power_supply_hwmon_temp_to_property
  4. power_supply_hwmon_to_property
  5. power_supply_hwmon_is_a_label
  6. power_supply_hwmon_is_writable
  7. power_supply_hwmon_is_visible
  8. power_supply_hwmon_read_string
  9. power_supply_hwmon_read
  10. power_supply_hwmon_write
  11. power_supply_hwmon_bitmap_free
  12. power_supply_add_hwmon_sysfs
  13. power_supply_remove_hwmon_sysfs

   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 int power_supply_hwmon_in_to_property(u32 attr)
  17 {
  18         switch (attr) {
  19         case hwmon_in_average:
  20                 return POWER_SUPPLY_PROP_VOLTAGE_AVG;
  21         case hwmon_in_min:
  22                 return POWER_SUPPLY_PROP_VOLTAGE_MIN;
  23         case hwmon_in_max:
  24                 return POWER_SUPPLY_PROP_VOLTAGE_MAX;
  25         case hwmon_in_input:
  26                 return POWER_SUPPLY_PROP_VOLTAGE_NOW;
  27         default:
  28                 return -EINVAL;
  29         }
  30 }
  31 
  32 static int power_supply_hwmon_curr_to_property(u32 attr)
  33 {
  34         switch (attr) {
  35         case hwmon_curr_average:
  36                 return POWER_SUPPLY_PROP_CURRENT_AVG;
  37         case hwmon_curr_max:
  38                 return POWER_SUPPLY_PROP_CURRENT_MAX;
  39         case hwmon_curr_input:
  40                 return POWER_SUPPLY_PROP_CURRENT_NOW;
  41         default:
  42                 return -EINVAL;
  43         }
  44 }
  45 
  46 static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
  47 {
  48         if (channel) {
  49                 switch (attr) {
  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;
  56                 default:
  57                         break;
  58                 }
  59         } else {
  60                 switch (attr) {
  61                 case hwmon_temp_input:
  62                         return POWER_SUPPLY_PROP_TEMP;
  63                 case hwmon_temp_max:
  64                         return POWER_SUPPLY_PROP_TEMP_MAX;
  65                 case hwmon_temp_min:
  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;
  71                 default:
  72                         break;
  73                 }
  74         }
  75 
  76         return -EINVAL;
  77 }
  78 
  79 static int
  80 power_supply_hwmon_to_property(enum hwmon_sensor_types type,
  81                                u32 attr, int channel)
  82 {
  83         switch (type) {
  84         case hwmon_in:
  85                 return power_supply_hwmon_in_to_property(attr);
  86         case hwmon_curr:
  87                 return power_supply_hwmon_curr_to_property(attr);
  88         case hwmon_temp:
  89                 return power_supply_hwmon_temp_to_property(attr, channel);
  90         default:
  91                 return -EINVAL;
  92         }
  93 }
  94 
  95 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
  96                                            u32 attr)
  97 {
  98         return type == hwmon_temp && attr == hwmon_temp_label;
  99 }
 100 
 101 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
 102                                            u32 attr)
 103 {
 104         switch (type) {
 105         case hwmon_in:
 106                 return attr == hwmon_in_min ||
 107                        attr == hwmon_in_max;
 108         case hwmon_curr:
 109                 return attr == hwmon_curr_max;
 110         case hwmon_temp:
 111                 return attr == hwmon_temp_max ||
 112                        attr == hwmon_temp_min ||
 113                        attr == hwmon_temp_min_alarm ||
 114                        attr == hwmon_temp_max_alarm;
 115         default:
 116                 return false;
 117         }
 118 }
 119 
 120 static umode_t power_supply_hwmon_is_visible(const void *data,
 121                                              enum hwmon_sensor_types type,
 122                                              u32 attr, int channel)
 123 {
 124         const struct power_supply_hwmon *psyhw = data;
 125         int prop;
 126 
 127 
 128         if (power_supply_hwmon_is_a_label(type, attr))
 129                 return 0444;
 130 
 131         prop = power_supply_hwmon_to_property(type, attr, channel);
 132         if (prop < 0 || !test_bit(prop, psyhw->props))
 133                 return 0;
 134 
 135         if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
 136             power_supply_hwmon_is_writable(type, attr))
 137                 return 0644;
 138 
 139         return 0444;
 140 }
 141 
 142 static int power_supply_hwmon_read_string(struct device *dev,
 143                                           enum hwmon_sensor_types type,
 144                                           u32 attr, int channel,
 145                                           const char **str)
 146 {
 147         *str = channel ? "temp" : "temp ambient";
 148         return 0;
 149 }
 150 
 151 static int
 152 power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 153                         u32 attr, int channel, long *val)
 154 {
 155         struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
 156         struct power_supply *psy = psyhw->psy;
 157         union power_supply_propval pspval;
 158         int ret, prop;
 159 
 160         prop = power_supply_hwmon_to_property(type, attr, channel);
 161         if (prop < 0)
 162                 return prop;
 163 
 164         ret  = power_supply_get_property(psy, prop, &pspval);
 165         if (ret)
 166                 return ret;
 167 
 168         switch (type) {
 169         /*
 170          * Both voltage and current is reported in units of
 171          * microvolts/microamps, so we need to adjust it to
 172          * milliamps(volts)
 173          */
 174         case hwmon_curr:
 175         case hwmon_in:
 176                 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
 177                 break;
 178         /*
 179          * Temp needs to be converted from 1/10 C to milli-C
 180          */
 181         case hwmon_temp:
 182                 if (check_mul_overflow(pspval.intval, 100,
 183                                        &pspval.intval))
 184                         return -EOVERFLOW;
 185                 break;
 186         default:
 187                 return -EINVAL;
 188         }
 189 
 190         *val = pspval.intval;
 191 
 192         return 0;
 193 }
 194 
 195 static int
 196 power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 197                          u32 attr, int channel, long val)
 198 {
 199         struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
 200         struct power_supply *psy = psyhw->psy;
 201         union power_supply_propval pspval;
 202         int prop;
 203 
 204         prop = power_supply_hwmon_to_property(type, attr, channel);
 205         if (prop < 0)
 206                 return prop;
 207 
 208         pspval.intval = val;
 209 
 210         switch (type) {
 211         /*
 212          * Both voltage and current is reported in units of
 213          * microvolts/microamps, so we need to adjust it to
 214          * milliamps(volts)
 215          */
 216         case hwmon_curr:
 217         case hwmon_in:
 218                 if (check_mul_overflow(pspval.intval, 1000,
 219                                        &pspval.intval))
 220                         return -EOVERFLOW;
 221                 break;
 222         /*
 223          * Temp needs to be converted from 1/10 C to milli-C
 224          */
 225         case hwmon_temp:
 226                 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
 227                 break;
 228         default:
 229                 return -EINVAL;
 230         }
 231 
 232         return power_supply_set_property(psy, prop, &pspval);
 233 }
 234 
 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,
 240 };
 241 
 242 static const struct hwmon_channel_info *power_supply_hwmon_info[] = {
 243         HWMON_CHANNEL_INFO(temp,
 244                            HWMON_T_LABEL     |
 245                            HWMON_T_INPUT     |
 246                            HWMON_T_MAX       |
 247                            HWMON_T_MIN       |
 248                            HWMON_T_MIN_ALARM |
 249                            HWMON_T_MIN_ALARM,
 250 
 251                            HWMON_T_LABEL     |
 252                            HWMON_T_INPUT     |
 253                            HWMON_T_MIN_ALARM |
 254                            HWMON_T_LABEL     |
 255                            HWMON_T_MAX_ALARM),
 256 
 257         HWMON_CHANNEL_INFO(curr,
 258                            HWMON_C_AVERAGE |
 259                            HWMON_C_MAX     |
 260                            HWMON_C_INPUT),
 261 
 262         HWMON_CHANNEL_INFO(in,
 263                            HWMON_I_AVERAGE |
 264                            HWMON_I_MIN     |
 265                            HWMON_I_MAX     |
 266                            HWMON_I_INPUT),
 267         NULL
 268 };
 269 
 270 static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
 271         .ops = &power_supply_hwmon_ops,
 272         .info = power_supply_hwmon_info,
 273 };
 274 
 275 static void power_supply_hwmon_bitmap_free(void *data)
 276 {
 277         bitmap_free(data);
 278 }
 279 
 280 int power_supply_add_hwmon_sysfs(struct power_supply *psy)
 281 {
 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;
 286         int ret, i;
 287         const char *name;
 288 
 289         if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
 290                                GFP_KERNEL))
 291                 return -ENOMEM;
 292 
 293         psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
 294         if (!psyhw) {
 295                 ret = -ENOMEM;
 296                 goto error;
 297         }
 298 
 299         psyhw->psy = psy;
 300         psyhw->props = bitmap_zalloc(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
 301                                      GFP_KERNEL);
 302         if (!psyhw->props) {
 303                 ret = -ENOMEM;
 304                 goto error;
 305         }
 306 
 307         ret = devm_add_action(dev, power_supply_hwmon_bitmap_free,
 308                               psyhw->props);
 309         if (ret)
 310                 goto error;
 311 
 312         for (i = 0; i < desc->num_properties; i++) {
 313                 const enum power_supply_property prop = desc->properties[i];
 314 
 315                 switch (prop) {
 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);
 332                         break;
 333                 default:
 334                         break;
 335                 }
 336         }
 337 
 338         name = psy->desc->name;
 339         if (strchr(name, '-')) {
 340                 char *new_name;
 341 
 342                 new_name = devm_kstrdup(dev, name, GFP_KERNEL);
 343                 if (!new_name) {
 344                         ret = -ENOMEM;
 345                         goto error;
 346                 }
 347                 strreplace(new_name, '-', '_');
 348                 name = new_name;
 349         }
 350         hwmon = devm_hwmon_device_register_with_info(dev, name,
 351                                                 psyhw,
 352                                                 &power_supply_hwmon_chip_info,
 353                                                 NULL);
 354         ret = PTR_ERR_OR_ZERO(hwmon);
 355         if (ret)
 356                 goto error;
 357 
 358         devres_close_group(dev, power_supply_add_hwmon_sysfs);
 359         return 0;
 360 error:
 361         devres_release_group(dev, NULL);
 362         return ret;
 363 }
 364 
 365 void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
 366 {
 367         devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);
 368 }

/* [<][>][^][v][top][bottom][index][help] */