0001
0002
0003
0004
0005
0006 #include <linux/err.h>
0007 #include <linux/hwmon.h>
0008 #include <linux/power_supply.h>
0009 #include <linux/slab.h>
0010
0011 struct power_supply_hwmon {
0012 struct power_supply *psy;
0013 unsigned long *props;
0014 };
0015
0016 static const char *const ps_temp_label[] = {
0017 "temp",
0018 "ambient temp",
0019 };
0020
0021 static int power_supply_hwmon_in_to_property(u32 attr)
0022 {
0023 switch (attr) {
0024 case hwmon_in_average:
0025 return POWER_SUPPLY_PROP_VOLTAGE_AVG;
0026 case hwmon_in_min:
0027 return POWER_SUPPLY_PROP_VOLTAGE_MIN;
0028 case hwmon_in_max:
0029 return POWER_SUPPLY_PROP_VOLTAGE_MAX;
0030 case hwmon_in_input:
0031 return POWER_SUPPLY_PROP_VOLTAGE_NOW;
0032 default:
0033 return -EINVAL;
0034 }
0035 }
0036
0037 static int power_supply_hwmon_curr_to_property(u32 attr)
0038 {
0039 switch (attr) {
0040 case hwmon_curr_average:
0041 return POWER_SUPPLY_PROP_CURRENT_AVG;
0042 case hwmon_curr_max:
0043 return POWER_SUPPLY_PROP_CURRENT_MAX;
0044 case hwmon_curr_input:
0045 return POWER_SUPPLY_PROP_CURRENT_NOW;
0046 default:
0047 return -EINVAL;
0048 }
0049 }
0050
0051 static int power_supply_hwmon_temp_to_property(u32 attr, int channel)
0052 {
0053 if (channel) {
0054 switch (attr) {
0055 case hwmon_temp_input:
0056 return POWER_SUPPLY_PROP_TEMP_AMBIENT;
0057 case hwmon_temp_min_alarm:
0058 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN;
0059 case hwmon_temp_max_alarm:
0060 return POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX;
0061 default:
0062 break;
0063 }
0064 } else {
0065 switch (attr) {
0066 case hwmon_temp_input:
0067 return POWER_SUPPLY_PROP_TEMP;
0068 case hwmon_temp_max:
0069 return POWER_SUPPLY_PROP_TEMP_MAX;
0070 case hwmon_temp_min:
0071 return POWER_SUPPLY_PROP_TEMP_MIN;
0072 case hwmon_temp_min_alarm:
0073 return POWER_SUPPLY_PROP_TEMP_ALERT_MIN;
0074 case hwmon_temp_max_alarm:
0075 return POWER_SUPPLY_PROP_TEMP_ALERT_MAX;
0076 default:
0077 break;
0078 }
0079 }
0080
0081 return -EINVAL;
0082 }
0083
0084 static int
0085 power_supply_hwmon_to_property(enum hwmon_sensor_types type,
0086 u32 attr, int channel)
0087 {
0088 switch (type) {
0089 case hwmon_in:
0090 return power_supply_hwmon_in_to_property(attr);
0091 case hwmon_curr:
0092 return power_supply_hwmon_curr_to_property(attr);
0093 case hwmon_temp:
0094 return power_supply_hwmon_temp_to_property(attr, channel);
0095 default:
0096 return -EINVAL;
0097 }
0098 }
0099
0100 static bool power_supply_hwmon_is_a_label(enum hwmon_sensor_types type,
0101 u32 attr)
0102 {
0103 return type == hwmon_temp && attr == hwmon_temp_label;
0104 }
0105
0106 struct hwmon_type_attr_list {
0107 const u32 *attrs;
0108 size_t n_attrs;
0109 };
0110
0111 static const u32 ps_temp_attrs[] = {
0112 hwmon_temp_input,
0113 hwmon_temp_min, hwmon_temp_max,
0114 hwmon_temp_min_alarm, hwmon_temp_max_alarm,
0115 };
0116
0117 static const struct hwmon_type_attr_list ps_type_attrs[hwmon_max] = {
0118 [hwmon_temp] = { ps_temp_attrs, ARRAY_SIZE(ps_temp_attrs) },
0119 };
0120
0121 static bool power_supply_hwmon_has_input(
0122 const struct power_supply_hwmon *psyhw,
0123 enum hwmon_sensor_types type, int channel)
0124 {
0125 const struct hwmon_type_attr_list *attr_list = &ps_type_attrs[type];
0126 size_t i;
0127
0128 for (i = 0; i < attr_list->n_attrs; ++i) {
0129 int prop = power_supply_hwmon_to_property(type,
0130 attr_list->attrs[i], channel);
0131
0132 if (prop >= 0 && test_bit(prop, psyhw->props))
0133 return true;
0134 }
0135
0136 return false;
0137 }
0138
0139 static bool power_supply_hwmon_is_writable(enum hwmon_sensor_types type,
0140 u32 attr)
0141 {
0142 switch (type) {
0143 case hwmon_in:
0144 return attr == hwmon_in_min ||
0145 attr == hwmon_in_max;
0146 case hwmon_curr:
0147 return attr == hwmon_curr_max;
0148 case hwmon_temp:
0149 return attr == hwmon_temp_max ||
0150 attr == hwmon_temp_min ||
0151 attr == hwmon_temp_min_alarm ||
0152 attr == hwmon_temp_max_alarm;
0153 default:
0154 return false;
0155 }
0156 }
0157
0158 static umode_t power_supply_hwmon_is_visible(const void *data,
0159 enum hwmon_sensor_types type,
0160 u32 attr, int channel)
0161 {
0162 const struct power_supply_hwmon *psyhw = data;
0163 int prop;
0164
0165 if (power_supply_hwmon_is_a_label(type, attr)) {
0166 if (power_supply_hwmon_has_input(psyhw, type, channel))
0167 return 0444;
0168 else
0169 return 0;
0170 }
0171
0172 prop = power_supply_hwmon_to_property(type, attr, channel);
0173 if (prop < 0 || !test_bit(prop, psyhw->props))
0174 return 0;
0175
0176 if (power_supply_property_is_writeable(psyhw->psy, prop) > 0 &&
0177 power_supply_hwmon_is_writable(type, attr))
0178 return 0644;
0179
0180 return 0444;
0181 }
0182
0183 static int power_supply_hwmon_read_string(struct device *dev,
0184 enum hwmon_sensor_types type,
0185 u32 attr, int channel,
0186 const char **str)
0187 {
0188 switch (type) {
0189 case hwmon_temp:
0190 *str = ps_temp_label[channel];
0191 break;
0192 default:
0193
0194
0195
0196
0197
0198
0199 break;
0200 }
0201
0202 return 0;
0203 }
0204
0205 static int
0206 power_supply_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
0207 u32 attr, int channel, long *val)
0208 {
0209 struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
0210 struct power_supply *psy = psyhw->psy;
0211 union power_supply_propval pspval;
0212 int ret, prop;
0213
0214 prop = power_supply_hwmon_to_property(type, attr, channel);
0215 if (prop < 0)
0216 return prop;
0217
0218 ret = power_supply_get_property(psy, prop, &pspval);
0219 if (ret)
0220 return ret;
0221
0222 switch (type) {
0223
0224
0225
0226
0227
0228 case hwmon_curr:
0229 case hwmon_in:
0230 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 1000);
0231 break;
0232
0233
0234
0235 case hwmon_temp:
0236 if (check_mul_overflow(pspval.intval, 100,
0237 &pspval.intval))
0238 return -EOVERFLOW;
0239 break;
0240 default:
0241 return -EINVAL;
0242 }
0243
0244 *val = pspval.intval;
0245
0246 return 0;
0247 }
0248
0249 static int
0250 power_supply_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
0251 u32 attr, int channel, long val)
0252 {
0253 struct power_supply_hwmon *psyhw = dev_get_drvdata(dev);
0254 struct power_supply *psy = psyhw->psy;
0255 union power_supply_propval pspval;
0256 int prop;
0257
0258 prop = power_supply_hwmon_to_property(type, attr, channel);
0259 if (prop < 0)
0260 return prop;
0261
0262 pspval.intval = val;
0263
0264 switch (type) {
0265
0266
0267
0268
0269
0270 case hwmon_curr:
0271 case hwmon_in:
0272 if (check_mul_overflow(pspval.intval, 1000,
0273 &pspval.intval))
0274 return -EOVERFLOW;
0275 break;
0276
0277
0278
0279 case hwmon_temp:
0280 pspval.intval = DIV_ROUND_CLOSEST(pspval.intval, 100);
0281 break;
0282 default:
0283 return -EINVAL;
0284 }
0285
0286 return power_supply_set_property(psy, prop, &pspval);
0287 }
0288
0289 static const struct hwmon_ops power_supply_hwmon_ops = {
0290 .is_visible = power_supply_hwmon_is_visible,
0291 .read = power_supply_hwmon_read,
0292 .write = power_supply_hwmon_write,
0293 .read_string = power_supply_hwmon_read_string,
0294 };
0295
0296 static const struct hwmon_channel_info *power_supply_hwmon_info[] = {
0297 HWMON_CHANNEL_INFO(temp,
0298 HWMON_T_LABEL |
0299 HWMON_T_INPUT |
0300 HWMON_T_MAX |
0301 HWMON_T_MIN |
0302 HWMON_T_MIN_ALARM,
0303
0304 HWMON_T_LABEL |
0305 HWMON_T_INPUT |
0306 HWMON_T_MIN_ALARM |
0307 HWMON_T_MAX_ALARM),
0308
0309 HWMON_CHANNEL_INFO(curr,
0310 HWMON_C_AVERAGE |
0311 HWMON_C_MAX |
0312 HWMON_C_INPUT),
0313
0314 HWMON_CHANNEL_INFO(in,
0315 HWMON_I_AVERAGE |
0316 HWMON_I_MIN |
0317 HWMON_I_MAX |
0318 HWMON_I_INPUT),
0319 NULL
0320 };
0321
0322 static const struct hwmon_chip_info power_supply_hwmon_chip_info = {
0323 .ops = &power_supply_hwmon_ops,
0324 .info = power_supply_hwmon_info,
0325 };
0326
0327 int power_supply_add_hwmon_sysfs(struct power_supply *psy)
0328 {
0329 const struct power_supply_desc *desc = psy->desc;
0330 struct power_supply_hwmon *psyhw;
0331 struct device *dev = &psy->dev;
0332 struct device *hwmon;
0333 int ret, i;
0334 const char *name;
0335
0336 if (!devres_open_group(dev, power_supply_add_hwmon_sysfs,
0337 GFP_KERNEL))
0338 return -ENOMEM;
0339
0340 psyhw = devm_kzalloc(dev, sizeof(*psyhw), GFP_KERNEL);
0341 if (!psyhw) {
0342 ret = -ENOMEM;
0343 goto error;
0344 }
0345
0346 psyhw->psy = psy;
0347 psyhw->props = devm_bitmap_zalloc(dev,
0348 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG + 1,
0349 GFP_KERNEL);
0350 if (!psyhw->props) {
0351 ret = -ENOMEM;
0352 goto error;
0353 }
0354
0355 for (i = 0; i < desc->num_properties; i++) {
0356 const enum power_supply_property prop = desc->properties[i];
0357
0358 switch (prop) {
0359 case POWER_SUPPLY_PROP_CURRENT_AVG:
0360 case POWER_SUPPLY_PROP_CURRENT_MAX:
0361 case POWER_SUPPLY_PROP_CURRENT_NOW:
0362 case POWER_SUPPLY_PROP_TEMP:
0363 case POWER_SUPPLY_PROP_TEMP_MAX:
0364 case POWER_SUPPLY_PROP_TEMP_MIN:
0365 case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
0366 case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
0367 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
0368 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN:
0369 case POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX:
0370 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
0371 case POWER_SUPPLY_PROP_VOLTAGE_MIN:
0372 case POWER_SUPPLY_PROP_VOLTAGE_MAX:
0373 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
0374 set_bit(prop, psyhw->props);
0375 break;
0376 default:
0377 break;
0378 }
0379 }
0380
0381 name = psy->desc->name;
0382 if (strchr(name, '-')) {
0383 char *new_name;
0384
0385 new_name = devm_kstrdup(dev, name, GFP_KERNEL);
0386 if (!new_name) {
0387 ret = -ENOMEM;
0388 goto error;
0389 }
0390 strreplace(new_name, '-', '_');
0391 name = new_name;
0392 }
0393 hwmon = devm_hwmon_device_register_with_info(dev, name,
0394 psyhw,
0395 &power_supply_hwmon_chip_info,
0396 NULL);
0397 ret = PTR_ERR_OR_ZERO(hwmon);
0398 if (ret)
0399 goto error;
0400
0401 devres_close_group(dev, power_supply_add_hwmon_sysfs);
0402 return 0;
0403 error:
0404 devres_release_group(dev, NULL);
0405 return ret;
0406 }
0407
0408 void power_supply_remove_hwmon_sysfs(struct power_supply *psy)
0409 {
0410 devres_release_group(&psy->dev, power_supply_add_hwmon_sysfs);
0411 }