11236441fSMark M. Hoffman /* 25ed04880SGuenter Roeck * hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring 35ed04880SGuenter Roeck * 45ed04880SGuenter Roeck * This file defines the sysfs class "hwmon", for use by sensors drivers. 55ed04880SGuenter Roeck * 65ed04880SGuenter Roeck * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com> 75ed04880SGuenter Roeck * 85ed04880SGuenter Roeck * This program is free software; you can redistribute it and/or modify 95ed04880SGuenter Roeck * it under the terms of the GNU General Public License as published by 105ed04880SGuenter Roeck * the Free Software Foundation; version 2 of the License. 111236441fSMark M. Hoffman */ 121236441fSMark M. Hoffman 13c95df1aeSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14c95df1aeSJoe Perches 15*d560168bSGuenter Roeck #include <linux/bitops.h> 161236441fSMark M. Hoffman #include <linux/device.h> 171236441fSMark M. Hoffman #include <linux/err.h> 188c65b4a6STim Schmielau #include <linux/gfp.h> 19c9ebbe6fSGuenter Roeck #include <linux/hwmon.h> 20c9ebbe6fSGuenter Roeck #include <linux/idr.h> 21c9ebbe6fSGuenter Roeck #include <linux/module.h> 222958b1ecSJean Delvare #include <linux/pci.h> 23c9ebbe6fSGuenter Roeck #include <linux/slab.h> 24648cd48cSGuenter Roeck #include <linux/string.h> 25*d560168bSGuenter Roeck #include <linux/thermal.h> 261236441fSMark M. Hoffman 271236441fSMark M. Hoffman #define HWMON_ID_PREFIX "hwmon" 281236441fSMark M. Hoffman #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" 291236441fSMark M. Hoffman 30bab2243cSGuenter Roeck struct hwmon_device { 31bab2243cSGuenter Roeck const char *name; 32bab2243cSGuenter Roeck struct device dev; 33*d560168bSGuenter Roeck const struct hwmon_chip_info *chip; 34*d560168bSGuenter Roeck 35*d560168bSGuenter Roeck struct attribute_group group; 36*d560168bSGuenter Roeck const struct attribute_group **groups; 37bab2243cSGuenter Roeck }; 38*d560168bSGuenter Roeck 39bab2243cSGuenter Roeck #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 40bab2243cSGuenter Roeck 41*d560168bSGuenter Roeck struct hwmon_device_attribute { 42*d560168bSGuenter Roeck struct device_attribute dev_attr; 43*d560168bSGuenter Roeck const struct hwmon_ops *ops; 44*d560168bSGuenter Roeck enum hwmon_sensor_types type; 45*d560168bSGuenter Roeck u32 attr; 46*d560168bSGuenter Roeck int index; 47*d560168bSGuenter Roeck }; 48*d560168bSGuenter Roeck 49*d560168bSGuenter Roeck #define to_hwmon_attr(d) \ 50*d560168bSGuenter Roeck container_of(d, struct hwmon_device_attribute, dev_attr) 51*d560168bSGuenter Roeck 52*d560168bSGuenter Roeck /* 53*d560168bSGuenter Roeck * Thermal zone information 54*d560168bSGuenter Roeck * In addition to the reference to the hwmon device, 55*d560168bSGuenter Roeck * also provides the sensor index. 56*d560168bSGuenter Roeck */ 57*d560168bSGuenter Roeck struct hwmon_thermal_data { 58*d560168bSGuenter Roeck struct hwmon_device *hwdev; /* Reference to hwmon device */ 59*d560168bSGuenter Roeck int index; /* sensor index */ 60*d560168bSGuenter Roeck }; 61*d560168bSGuenter Roeck 62bab2243cSGuenter Roeck static ssize_t 63bab2243cSGuenter Roeck show_name(struct device *dev, struct device_attribute *attr, char *buf) 64bab2243cSGuenter Roeck { 65bab2243cSGuenter Roeck return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 66bab2243cSGuenter Roeck } 67bab2243cSGuenter Roeck static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 68bab2243cSGuenter Roeck 69bab2243cSGuenter Roeck static struct attribute *hwmon_dev_attrs[] = { 70bab2243cSGuenter Roeck &dev_attr_name.attr, 71bab2243cSGuenter Roeck NULL 72bab2243cSGuenter Roeck }; 73bab2243cSGuenter Roeck 74bab2243cSGuenter Roeck static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 75bab2243cSGuenter Roeck struct attribute *attr, int n) 76bab2243cSGuenter Roeck { 77bab2243cSGuenter Roeck struct device *dev = container_of(kobj, struct device, kobj); 78bab2243cSGuenter Roeck 79bab2243cSGuenter Roeck if (to_hwmon_device(dev)->name == NULL) 80bab2243cSGuenter Roeck return 0; 81bab2243cSGuenter Roeck 82bab2243cSGuenter Roeck return attr->mode; 83bab2243cSGuenter Roeck } 84bab2243cSGuenter Roeck 85bab2243cSGuenter Roeck static struct attribute_group hwmon_dev_attr_group = { 86bab2243cSGuenter Roeck .attrs = hwmon_dev_attrs, 87bab2243cSGuenter Roeck .is_visible = hwmon_dev_name_is_visible, 88bab2243cSGuenter Roeck }; 89bab2243cSGuenter Roeck 90bab2243cSGuenter Roeck static const struct attribute_group *hwmon_dev_attr_groups[] = { 91bab2243cSGuenter Roeck &hwmon_dev_attr_group, 92bab2243cSGuenter Roeck NULL 93bab2243cSGuenter Roeck }; 94bab2243cSGuenter Roeck 95bab2243cSGuenter Roeck static void hwmon_dev_release(struct device *dev) 96bab2243cSGuenter Roeck { 97bab2243cSGuenter Roeck kfree(to_hwmon_device(dev)); 98bab2243cSGuenter Roeck } 99bab2243cSGuenter Roeck 100bab2243cSGuenter Roeck static struct class hwmon_class = { 101bab2243cSGuenter Roeck .name = "hwmon", 102bab2243cSGuenter Roeck .owner = THIS_MODULE, 103bab2243cSGuenter Roeck .dev_groups = hwmon_dev_attr_groups, 104bab2243cSGuenter Roeck .dev_release = hwmon_dev_release, 105bab2243cSGuenter Roeck }; 1061236441fSMark M. Hoffman 1074ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida); 1081236441fSMark M. Hoffman 109*d560168bSGuenter Roeck /* Thermal zone handling */ 110*d560168bSGuenter Roeck 111*d560168bSGuenter Roeck #if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) 112*d560168bSGuenter Roeck static int hwmon_thermal_get_temp(void *data, int *temp) 113*d560168bSGuenter Roeck { 114*d560168bSGuenter Roeck struct hwmon_thermal_data *tdata = data; 115*d560168bSGuenter Roeck struct hwmon_device *hwdev = tdata->hwdev; 116*d560168bSGuenter Roeck int ret; 117*d560168bSGuenter Roeck long t; 118*d560168bSGuenter Roeck 119*d560168bSGuenter Roeck ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input, 120*d560168bSGuenter Roeck tdata->index, &t); 121*d560168bSGuenter Roeck if (ret < 0) 122*d560168bSGuenter Roeck return ret; 123*d560168bSGuenter Roeck 124*d560168bSGuenter Roeck *temp = t; 125*d560168bSGuenter Roeck 126*d560168bSGuenter Roeck return 0; 127*d560168bSGuenter Roeck } 128*d560168bSGuenter Roeck 129*d560168bSGuenter Roeck static struct thermal_zone_of_device_ops hwmon_thermal_ops = { 130*d560168bSGuenter Roeck .get_temp = hwmon_thermal_get_temp, 131*d560168bSGuenter Roeck }; 132*d560168bSGuenter Roeck 133*d560168bSGuenter Roeck static int hwmon_thermal_add_sensor(struct device *dev, 134*d560168bSGuenter Roeck struct hwmon_device *hwdev, int index) 135*d560168bSGuenter Roeck { 136*d560168bSGuenter Roeck struct hwmon_thermal_data *tdata; 137*d560168bSGuenter Roeck 138*d560168bSGuenter Roeck tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); 139*d560168bSGuenter Roeck if (!tdata) 140*d560168bSGuenter Roeck return -ENOMEM; 141*d560168bSGuenter Roeck 142*d560168bSGuenter Roeck tdata->hwdev = hwdev; 143*d560168bSGuenter Roeck tdata->index = index; 144*d560168bSGuenter Roeck 145*d560168bSGuenter Roeck devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata, 146*d560168bSGuenter Roeck &hwmon_thermal_ops); 147*d560168bSGuenter Roeck 148*d560168bSGuenter Roeck return 0; 149*d560168bSGuenter Roeck } 150*d560168bSGuenter Roeck #else 151*d560168bSGuenter Roeck static int hwmon_thermal_add_sensor(struct device *dev, 152*d560168bSGuenter Roeck struct hwmon_device *hwdev, int index) 153*d560168bSGuenter Roeck { 154*d560168bSGuenter Roeck return 0; 155*d560168bSGuenter Roeck } 156*d560168bSGuenter Roeck #endif /* IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) */ 157*d560168bSGuenter Roeck 158*d560168bSGuenter Roeck /* sysfs attribute management */ 159*d560168bSGuenter Roeck 160*d560168bSGuenter Roeck static ssize_t hwmon_attr_show(struct device *dev, 161*d560168bSGuenter Roeck struct device_attribute *devattr, char *buf) 162*d560168bSGuenter Roeck { 163*d560168bSGuenter Roeck struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); 164*d560168bSGuenter Roeck long val; 165*d560168bSGuenter Roeck int ret; 166*d560168bSGuenter Roeck 167*d560168bSGuenter Roeck ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index, 168*d560168bSGuenter Roeck &val); 169*d560168bSGuenter Roeck if (ret < 0) 170*d560168bSGuenter Roeck return ret; 171*d560168bSGuenter Roeck 172*d560168bSGuenter Roeck return sprintf(buf, "%ld\n", val); 173*d560168bSGuenter Roeck } 174*d560168bSGuenter Roeck 175*d560168bSGuenter Roeck static ssize_t hwmon_attr_store(struct device *dev, 176*d560168bSGuenter Roeck struct device_attribute *devattr, 177*d560168bSGuenter Roeck const char *buf, size_t count) 178*d560168bSGuenter Roeck { 179*d560168bSGuenter Roeck struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); 180*d560168bSGuenter Roeck long val; 181*d560168bSGuenter Roeck int ret; 182*d560168bSGuenter Roeck 183*d560168bSGuenter Roeck ret = kstrtol(buf, 10, &val); 184*d560168bSGuenter Roeck if (ret < 0) 185*d560168bSGuenter Roeck return ret; 186*d560168bSGuenter Roeck 187*d560168bSGuenter Roeck ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index, 188*d560168bSGuenter Roeck val); 189*d560168bSGuenter Roeck if (ret < 0) 190*d560168bSGuenter Roeck return ret; 191*d560168bSGuenter Roeck 192*d560168bSGuenter Roeck return count; 193*d560168bSGuenter Roeck } 194*d560168bSGuenter Roeck 195*d560168bSGuenter Roeck static int hwmon_attr_base(enum hwmon_sensor_types type) 196*d560168bSGuenter Roeck { 197*d560168bSGuenter Roeck if (type == hwmon_in) 198*d560168bSGuenter Roeck return 0; 199*d560168bSGuenter Roeck return 1; 200*d560168bSGuenter Roeck } 201*d560168bSGuenter Roeck 202*d560168bSGuenter Roeck static struct attribute *hwmon_genattr(struct device *dev, 203*d560168bSGuenter Roeck const void *drvdata, 204*d560168bSGuenter Roeck enum hwmon_sensor_types type, 205*d560168bSGuenter Roeck u32 attr, 206*d560168bSGuenter Roeck int index, 207*d560168bSGuenter Roeck const char *template, 208*d560168bSGuenter Roeck const struct hwmon_ops *ops) 209*d560168bSGuenter Roeck { 210*d560168bSGuenter Roeck struct hwmon_device_attribute *hattr; 211*d560168bSGuenter Roeck struct device_attribute *dattr; 212*d560168bSGuenter Roeck struct attribute *a; 213*d560168bSGuenter Roeck umode_t mode; 214*d560168bSGuenter Roeck char *name; 215*d560168bSGuenter Roeck 216*d560168bSGuenter Roeck /* The attribute is invisible if there is no template string */ 217*d560168bSGuenter Roeck if (!template) 218*d560168bSGuenter Roeck return ERR_PTR(-ENOENT); 219*d560168bSGuenter Roeck 220*d560168bSGuenter Roeck mode = ops->is_visible(drvdata, type, attr, index); 221*d560168bSGuenter Roeck if (!mode) 222*d560168bSGuenter Roeck return ERR_PTR(-ENOENT); 223*d560168bSGuenter Roeck 224*d560168bSGuenter Roeck if ((mode & S_IRUGO) && !ops->read) 225*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 226*d560168bSGuenter Roeck if ((mode & S_IWUGO) && !ops->write) 227*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 228*d560168bSGuenter Roeck 229*d560168bSGuenter Roeck if (type == hwmon_chip) { 230*d560168bSGuenter Roeck name = (char *)template; 231*d560168bSGuenter Roeck } else { 232*d560168bSGuenter Roeck name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL); 233*d560168bSGuenter Roeck if (!name) 234*d560168bSGuenter Roeck return ERR_PTR(-ENOMEM); 235*d560168bSGuenter Roeck scnprintf(name, strlen(template) + 16, template, 236*d560168bSGuenter Roeck index + hwmon_attr_base(type)); 237*d560168bSGuenter Roeck } 238*d560168bSGuenter Roeck 239*d560168bSGuenter Roeck hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL); 240*d560168bSGuenter Roeck if (!hattr) 241*d560168bSGuenter Roeck return ERR_PTR(-ENOMEM); 242*d560168bSGuenter Roeck 243*d560168bSGuenter Roeck hattr->type = type; 244*d560168bSGuenter Roeck hattr->attr = attr; 245*d560168bSGuenter Roeck hattr->index = index; 246*d560168bSGuenter Roeck hattr->ops = ops; 247*d560168bSGuenter Roeck 248*d560168bSGuenter Roeck dattr = &hattr->dev_attr; 249*d560168bSGuenter Roeck dattr->show = hwmon_attr_show; 250*d560168bSGuenter Roeck dattr->store = hwmon_attr_store; 251*d560168bSGuenter Roeck 252*d560168bSGuenter Roeck a = &dattr->attr; 253*d560168bSGuenter Roeck sysfs_attr_init(a); 254*d560168bSGuenter Roeck a->name = name; 255*d560168bSGuenter Roeck a->mode = mode; 256*d560168bSGuenter Roeck 257*d560168bSGuenter Roeck return a; 258*d560168bSGuenter Roeck } 259*d560168bSGuenter Roeck 260*d560168bSGuenter Roeck static const char * const hwmon_chip_attr_templates[] = { 261*d560168bSGuenter Roeck [hwmon_chip_temp_reset_history] = "temp_reset_history", 262*d560168bSGuenter Roeck [hwmon_chip_update_interval] = "update_interval", 263*d560168bSGuenter Roeck [hwmon_chip_alarms] = "alarms", 264*d560168bSGuenter Roeck }; 265*d560168bSGuenter Roeck 266*d560168bSGuenter Roeck static const char * const hwmon_temp_attr_templates[] = { 267*d560168bSGuenter Roeck [hwmon_temp_input] = "temp%d_input", 268*d560168bSGuenter Roeck [hwmon_temp_type] = "temp%d_type", 269*d560168bSGuenter Roeck [hwmon_temp_lcrit] = "temp%d_lcrit", 270*d560168bSGuenter Roeck [hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst", 271*d560168bSGuenter Roeck [hwmon_temp_min] = "temp%d_min", 272*d560168bSGuenter Roeck [hwmon_temp_min_hyst] = "temp%d_min_hyst", 273*d560168bSGuenter Roeck [hwmon_temp_max] = "temp%d_max", 274*d560168bSGuenter Roeck [hwmon_temp_max_hyst] = "temp%d_max_hyst", 275*d560168bSGuenter Roeck [hwmon_temp_crit] = "temp%d_crit", 276*d560168bSGuenter Roeck [hwmon_temp_crit_hyst] = "temp%d_crit_hyst", 277*d560168bSGuenter Roeck [hwmon_temp_emergency] = "temp%d_emergency", 278*d560168bSGuenter Roeck [hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst", 279*d560168bSGuenter Roeck [hwmon_temp_alarm] = "temp%d_alarm", 280*d560168bSGuenter Roeck [hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm", 281*d560168bSGuenter Roeck [hwmon_temp_min_alarm] = "temp%d_min_alarm", 282*d560168bSGuenter Roeck [hwmon_temp_max_alarm] = "temp%d_max_alarm", 283*d560168bSGuenter Roeck [hwmon_temp_crit_alarm] = "temp%d_crit_alarm", 284*d560168bSGuenter Roeck [hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm", 285*d560168bSGuenter Roeck [hwmon_temp_fault] = "temp%d_fault", 286*d560168bSGuenter Roeck [hwmon_temp_offset] = "temp%d_offset", 287*d560168bSGuenter Roeck [hwmon_temp_label] = "temp%d_label", 288*d560168bSGuenter Roeck [hwmon_temp_lowest] = "temp%d_lowest", 289*d560168bSGuenter Roeck [hwmon_temp_highest] = "temp%d_highest", 290*d560168bSGuenter Roeck [hwmon_temp_reset_history] = "temp%d_reset_history", 291*d560168bSGuenter Roeck }; 292*d560168bSGuenter Roeck 293*d560168bSGuenter Roeck static const char * const *__templates[] = { 294*d560168bSGuenter Roeck [hwmon_chip] = hwmon_chip_attr_templates, 295*d560168bSGuenter Roeck [hwmon_temp] = hwmon_temp_attr_templates, 296*d560168bSGuenter Roeck }; 297*d560168bSGuenter Roeck 298*d560168bSGuenter Roeck static const int __templates_size[] = { 299*d560168bSGuenter Roeck [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates), 300*d560168bSGuenter Roeck [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates), 301*d560168bSGuenter Roeck }; 302*d560168bSGuenter Roeck 303*d560168bSGuenter Roeck static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) 304*d560168bSGuenter Roeck { 305*d560168bSGuenter Roeck int i, n; 306*d560168bSGuenter Roeck 307*d560168bSGuenter Roeck for (i = n = 0; info->config[i]; i++) 308*d560168bSGuenter Roeck n += hweight32(info->config[i]); 309*d560168bSGuenter Roeck 310*d560168bSGuenter Roeck return n; 311*d560168bSGuenter Roeck } 312*d560168bSGuenter Roeck 313*d560168bSGuenter Roeck static int hwmon_genattrs(struct device *dev, 314*d560168bSGuenter Roeck const void *drvdata, 315*d560168bSGuenter Roeck struct attribute **attrs, 316*d560168bSGuenter Roeck const struct hwmon_ops *ops, 317*d560168bSGuenter Roeck const struct hwmon_channel_info *info) 318*d560168bSGuenter Roeck { 319*d560168bSGuenter Roeck const char * const *templates; 320*d560168bSGuenter Roeck int template_size; 321*d560168bSGuenter Roeck int i, aindex = 0; 322*d560168bSGuenter Roeck 323*d560168bSGuenter Roeck if (info->type >= ARRAY_SIZE(__templates)) 324*d560168bSGuenter Roeck return -EINVAL; 325*d560168bSGuenter Roeck 326*d560168bSGuenter Roeck templates = __templates[info->type]; 327*d560168bSGuenter Roeck template_size = __templates_size[info->type]; 328*d560168bSGuenter Roeck 329*d560168bSGuenter Roeck for (i = 0; info->config[i]; i++) { 330*d560168bSGuenter Roeck u32 attr_mask = info->config[i]; 331*d560168bSGuenter Roeck u32 attr; 332*d560168bSGuenter Roeck 333*d560168bSGuenter Roeck while (attr_mask) { 334*d560168bSGuenter Roeck struct attribute *a; 335*d560168bSGuenter Roeck 336*d560168bSGuenter Roeck attr = __ffs(attr_mask); 337*d560168bSGuenter Roeck attr_mask &= ~BIT(attr); 338*d560168bSGuenter Roeck if (attr >= template_size) 339*d560168bSGuenter Roeck return -EINVAL; 340*d560168bSGuenter Roeck a = hwmon_genattr(dev, drvdata, info->type, attr, i, 341*d560168bSGuenter Roeck templates[attr], ops); 342*d560168bSGuenter Roeck if (IS_ERR(a)) { 343*d560168bSGuenter Roeck if (PTR_ERR(a) != -ENOENT) 344*d560168bSGuenter Roeck return PTR_ERR(a); 345*d560168bSGuenter Roeck continue; 346*d560168bSGuenter Roeck } 347*d560168bSGuenter Roeck attrs[aindex++] = a; 348*d560168bSGuenter Roeck } 349*d560168bSGuenter Roeck } 350*d560168bSGuenter Roeck return aindex; 351*d560168bSGuenter Roeck } 352*d560168bSGuenter Roeck 353*d560168bSGuenter Roeck static struct attribute ** 354*d560168bSGuenter Roeck __hwmon_create_attrs(struct device *dev, const void *drvdata, 355*d560168bSGuenter Roeck const struct hwmon_chip_info *chip) 356*d560168bSGuenter Roeck { 357*d560168bSGuenter Roeck int ret, i, aindex = 0, nattrs = 0; 358*d560168bSGuenter Roeck struct attribute **attrs; 359*d560168bSGuenter Roeck 360*d560168bSGuenter Roeck for (i = 0; chip->info[i]; i++) 361*d560168bSGuenter Roeck nattrs += hwmon_num_channel_attrs(chip->info[i]); 362*d560168bSGuenter Roeck 363*d560168bSGuenter Roeck if (nattrs == 0) 364*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 365*d560168bSGuenter Roeck 366*d560168bSGuenter Roeck attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL); 367*d560168bSGuenter Roeck if (!attrs) 368*d560168bSGuenter Roeck return ERR_PTR(-ENOMEM); 369*d560168bSGuenter Roeck 370*d560168bSGuenter Roeck for (i = 0; chip->info[i]; i++) { 371*d560168bSGuenter Roeck ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops, 372*d560168bSGuenter Roeck chip->info[i]); 373*d560168bSGuenter Roeck if (ret < 0) 374*d560168bSGuenter Roeck return ERR_PTR(ret); 375*d560168bSGuenter Roeck aindex += ret; 376*d560168bSGuenter Roeck } 377*d560168bSGuenter Roeck 378*d560168bSGuenter Roeck return attrs; 379*d560168bSGuenter Roeck } 380*d560168bSGuenter Roeck 381*d560168bSGuenter Roeck static struct device * 382*d560168bSGuenter Roeck __hwmon_device_register(struct device *dev, const char *name, void *drvdata, 383*d560168bSGuenter Roeck const struct hwmon_chip_info *chip, 384*d560168bSGuenter Roeck const struct attribute_group **groups) 385*d560168bSGuenter Roeck { 386*d560168bSGuenter Roeck struct hwmon_device *hwdev; 387*d560168bSGuenter Roeck struct device *hdev; 388*d560168bSGuenter Roeck int i, j, err, id; 389*d560168bSGuenter Roeck 390*d560168bSGuenter Roeck /* Do not accept invalid characters in hwmon name attribute */ 391*d560168bSGuenter Roeck if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) 392*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 393*d560168bSGuenter Roeck 394*d560168bSGuenter Roeck id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 395*d560168bSGuenter Roeck if (id < 0) 396*d560168bSGuenter Roeck return ERR_PTR(id); 397*d560168bSGuenter Roeck 398*d560168bSGuenter Roeck hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 399*d560168bSGuenter Roeck if (hwdev == NULL) { 400*d560168bSGuenter Roeck err = -ENOMEM; 401*d560168bSGuenter Roeck goto ida_remove; 402*d560168bSGuenter Roeck } 403*d560168bSGuenter Roeck 404*d560168bSGuenter Roeck hdev = &hwdev->dev; 405*d560168bSGuenter Roeck 406*d560168bSGuenter Roeck if (chip && chip->ops->is_visible) { 407*d560168bSGuenter Roeck struct attribute **attrs; 408*d560168bSGuenter Roeck int ngroups = 2; 409*d560168bSGuenter Roeck 410*d560168bSGuenter Roeck if (groups) 411*d560168bSGuenter Roeck for (i = 0; groups[i]; i++) 412*d560168bSGuenter Roeck ngroups++; 413*d560168bSGuenter Roeck 414*d560168bSGuenter Roeck hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups), 415*d560168bSGuenter Roeck GFP_KERNEL); 416*d560168bSGuenter Roeck if (!hwdev->groups) 417*d560168bSGuenter Roeck return ERR_PTR(-ENOMEM); 418*d560168bSGuenter Roeck 419*d560168bSGuenter Roeck attrs = __hwmon_create_attrs(dev, drvdata, chip); 420*d560168bSGuenter Roeck if (IS_ERR(attrs)) { 421*d560168bSGuenter Roeck err = PTR_ERR(attrs); 422*d560168bSGuenter Roeck goto free_hwmon; 423*d560168bSGuenter Roeck } 424*d560168bSGuenter Roeck 425*d560168bSGuenter Roeck hwdev->group.attrs = attrs; 426*d560168bSGuenter Roeck ngroups = 0; 427*d560168bSGuenter Roeck hwdev->groups[ngroups++] = &hwdev->group; 428*d560168bSGuenter Roeck 429*d560168bSGuenter Roeck if (groups) { 430*d560168bSGuenter Roeck for (i = 0; groups[i]; i++) 431*d560168bSGuenter Roeck hwdev->groups[ngroups++] = groups[i]; 432*d560168bSGuenter Roeck } 433*d560168bSGuenter Roeck 434*d560168bSGuenter Roeck hdev->groups = hwdev->groups; 435*d560168bSGuenter Roeck } else { 436*d560168bSGuenter Roeck hdev->groups = groups; 437*d560168bSGuenter Roeck } 438*d560168bSGuenter Roeck 439*d560168bSGuenter Roeck hwdev->name = name; 440*d560168bSGuenter Roeck hdev->class = &hwmon_class; 441*d560168bSGuenter Roeck hdev->parent = dev; 442*d560168bSGuenter Roeck hdev->of_node = dev ? dev->of_node : NULL; 443*d560168bSGuenter Roeck hwdev->chip = chip; 444*d560168bSGuenter Roeck dev_set_drvdata(hdev, drvdata); 445*d560168bSGuenter Roeck dev_set_name(hdev, HWMON_ID_FORMAT, id); 446*d560168bSGuenter Roeck err = device_register(hdev); 447*d560168bSGuenter Roeck if (err) 448*d560168bSGuenter Roeck goto free_hwmon; 449*d560168bSGuenter Roeck 450*d560168bSGuenter Roeck if (chip && chip->ops->is_visible && chip->ops->read && 451*d560168bSGuenter Roeck chip->info[0]->type == hwmon_chip && 452*d560168bSGuenter Roeck (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { 453*d560168bSGuenter Roeck const struct hwmon_channel_info **info = chip->info; 454*d560168bSGuenter Roeck 455*d560168bSGuenter Roeck for (i = 1; info[i]; i++) { 456*d560168bSGuenter Roeck if (info[i]->type != hwmon_temp) 457*d560168bSGuenter Roeck continue; 458*d560168bSGuenter Roeck 459*d560168bSGuenter Roeck for (j = 0; info[i]->config[j]; j++) { 460*d560168bSGuenter Roeck if (!chip->ops->is_visible(drvdata, hwmon_temp, 461*d560168bSGuenter Roeck hwmon_temp_input, j)) 462*d560168bSGuenter Roeck continue; 463*d560168bSGuenter Roeck if (info[i]->config[j] & HWMON_T_INPUT) 464*d560168bSGuenter Roeck hwmon_thermal_add_sensor(dev, hwdev, j); 465*d560168bSGuenter Roeck } 466*d560168bSGuenter Roeck } 467*d560168bSGuenter Roeck } 468*d560168bSGuenter Roeck 469*d560168bSGuenter Roeck return hdev; 470*d560168bSGuenter Roeck 471*d560168bSGuenter Roeck free_hwmon: 472*d560168bSGuenter Roeck kfree(hwdev); 473*d560168bSGuenter Roeck ida_remove: 474*d560168bSGuenter Roeck ida_simple_remove(&hwmon_ida, id); 475*d560168bSGuenter Roeck return ERR_PTR(err); 476*d560168bSGuenter Roeck } 477*d560168bSGuenter Roeck 4781236441fSMark M. Hoffman /** 479bab2243cSGuenter Roeck * hwmon_device_register_with_groups - register w/ hwmon 480bab2243cSGuenter Roeck * @dev: the parent device 481bab2243cSGuenter Roeck * @name: hwmon name attribute 482bab2243cSGuenter Roeck * @drvdata: driver data to attach to created device 483bab2243cSGuenter Roeck * @groups: List of attribute groups to create 484bab2243cSGuenter Roeck * 485bab2243cSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 486bab2243cSGuenter Roeck * longer needed. 487bab2243cSGuenter Roeck * 488bab2243cSGuenter Roeck * Returns the pointer to the new device. 489bab2243cSGuenter Roeck */ 490bab2243cSGuenter Roeck struct device * 491bab2243cSGuenter Roeck hwmon_device_register_with_groups(struct device *dev, const char *name, 492bab2243cSGuenter Roeck void *drvdata, 493bab2243cSGuenter Roeck const struct attribute_group **groups) 494bab2243cSGuenter Roeck { 495*d560168bSGuenter Roeck return __hwmon_device_register(dev, name, drvdata, NULL, groups); 496bab2243cSGuenter Roeck } 497bab2243cSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 498bab2243cSGuenter Roeck 499bab2243cSGuenter Roeck /** 500*d560168bSGuenter Roeck * hwmon_device_register_with_info - register w/ hwmon 501*d560168bSGuenter Roeck * @dev: the parent device 502*d560168bSGuenter Roeck * @name: hwmon name attribute 503*d560168bSGuenter Roeck * @drvdata: driver data to attach to created device 504*d560168bSGuenter Roeck * @info: Pointer to hwmon chip information 505*d560168bSGuenter Roeck * @groups - pointer to list of driver specific attribute groups 506*d560168bSGuenter Roeck * 507*d560168bSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 508*d560168bSGuenter Roeck * longer needed. 509*d560168bSGuenter Roeck * 510*d560168bSGuenter Roeck * Returns the pointer to the new device. 511*d560168bSGuenter Roeck */ 512*d560168bSGuenter Roeck struct device * 513*d560168bSGuenter Roeck hwmon_device_register_with_info(struct device *dev, const char *name, 514*d560168bSGuenter Roeck void *drvdata, 515*d560168bSGuenter Roeck const struct hwmon_chip_info *chip, 516*d560168bSGuenter Roeck const struct attribute_group **groups) 517*d560168bSGuenter Roeck { 518*d560168bSGuenter Roeck if (chip && (!chip->ops || !chip->info)) 519*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 520*d560168bSGuenter Roeck 521*d560168bSGuenter Roeck return __hwmon_device_register(dev, name, drvdata, chip, groups); 522*d560168bSGuenter Roeck } 523*d560168bSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); 524*d560168bSGuenter Roeck 525*d560168bSGuenter Roeck /** 5261beeffe4STony Jones * hwmon_device_register - register w/ hwmon 5271236441fSMark M. Hoffman * @dev: the device to register 5281236441fSMark M. Hoffman * 5291beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 5301236441fSMark M. Hoffman * longer needed. 5311236441fSMark M. Hoffman * 5321beeffe4STony Jones * Returns the pointer to the new device. 5331236441fSMark M. Hoffman */ 5341beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 5351236441fSMark M. Hoffman { 536bab2243cSGuenter Roeck return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); 5371236441fSMark M. Hoffman } 538839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_register); 5391236441fSMark M. Hoffman 5401236441fSMark M. Hoffman /** 5411236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 5421236441fSMark M. Hoffman * 5431beeffe4STony Jones * @dev: the class device to destroy 5441236441fSMark M. Hoffman */ 5451beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 5461236441fSMark M. Hoffman { 5471236441fSMark M. Hoffman int id; 5481236441fSMark M. Hoffman 549739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 5501beeffe4STony Jones device_unregister(dev); 5514ca5f468SJonathan Cameron ida_simple_remove(&hwmon_ida, id); 5521236441fSMark M. Hoffman } else 5531beeffe4STony Jones dev_dbg(dev->parent, 5541236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 5551236441fSMark M. Hoffman } 556839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_unregister); 5571236441fSMark M. Hoffman 55874188cbaSGuenter Roeck static void devm_hwmon_release(struct device *dev, void *res) 55974188cbaSGuenter Roeck { 56074188cbaSGuenter Roeck struct device *hwdev = *(struct device **)res; 56174188cbaSGuenter Roeck 56274188cbaSGuenter Roeck hwmon_device_unregister(hwdev); 56374188cbaSGuenter Roeck } 56474188cbaSGuenter Roeck 56574188cbaSGuenter Roeck /** 56674188cbaSGuenter Roeck * devm_hwmon_device_register_with_groups - register w/ hwmon 56774188cbaSGuenter Roeck * @dev: the parent device 56874188cbaSGuenter Roeck * @name: hwmon name attribute 56974188cbaSGuenter Roeck * @drvdata: driver data to attach to created device 57074188cbaSGuenter Roeck * @groups: List of attribute groups to create 57174188cbaSGuenter Roeck * 57274188cbaSGuenter Roeck * Returns the pointer to the new device. The new device is automatically 57374188cbaSGuenter Roeck * unregistered with the parent device. 57474188cbaSGuenter Roeck */ 57574188cbaSGuenter Roeck struct device * 57674188cbaSGuenter Roeck devm_hwmon_device_register_with_groups(struct device *dev, const char *name, 57774188cbaSGuenter Roeck void *drvdata, 57874188cbaSGuenter Roeck const struct attribute_group **groups) 57974188cbaSGuenter Roeck { 58074188cbaSGuenter Roeck struct device **ptr, *hwdev; 58174188cbaSGuenter Roeck 58274188cbaSGuenter Roeck if (!dev) 58374188cbaSGuenter Roeck return ERR_PTR(-EINVAL); 58474188cbaSGuenter Roeck 58574188cbaSGuenter Roeck ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 58674188cbaSGuenter Roeck if (!ptr) 58774188cbaSGuenter Roeck return ERR_PTR(-ENOMEM); 58874188cbaSGuenter Roeck 58974188cbaSGuenter Roeck hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); 59074188cbaSGuenter Roeck if (IS_ERR(hwdev)) 59174188cbaSGuenter Roeck goto error; 59274188cbaSGuenter Roeck 59374188cbaSGuenter Roeck *ptr = hwdev; 59474188cbaSGuenter Roeck devres_add(dev, ptr); 59574188cbaSGuenter Roeck return hwdev; 59674188cbaSGuenter Roeck 59774188cbaSGuenter Roeck error: 59874188cbaSGuenter Roeck devres_free(ptr); 59974188cbaSGuenter Roeck return hwdev; 60074188cbaSGuenter Roeck } 60174188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); 60274188cbaSGuenter Roeck 603*d560168bSGuenter Roeck /** 604*d560168bSGuenter Roeck * devm_hwmon_device_register_with_info - register w/ hwmon 605*d560168bSGuenter Roeck * @dev: the parent device 606*d560168bSGuenter Roeck * @name: hwmon name attribute 607*d560168bSGuenter Roeck * @drvdata: driver data to attach to created device 608*d560168bSGuenter Roeck * @info: Pointer to hwmon chip information 609*d560168bSGuenter Roeck * @groups - pointer to list of driver specific attribute groups 610*d560168bSGuenter Roeck * 611*d560168bSGuenter Roeck * Returns the pointer to the new device. The new device is automatically 612*d560168bSGuenter Roeck * unregistered with the parent device. 613*d560168bSGuenter Roeck */ 614*d560168bSGuenter Roeck struct device * 615*d560168bSGuenter Roeck devm_hwmon_device_register_with_info(struct device *dev, const char *name, 616*d560168bSGuenter Roeck void *drvdata, 617*d560168bSGuenter Roeck const struct hwmon_chip_info *chip, 618*d560168bSGuenter Roeck const struct attribute_group **groups) 619*d560168bSGuenter Roeck { 620*d560168bSGuenter Roeck struct device **ptr, *hwdev; 621*d560168bSGuenter Roeck 622*d560168bSGuenter Roeck if (!dev) 623*d560168bSGuenter Roeck return ERR_PTR(-EINVAL); 624*d560168bSGuenter Roeck 625*d560168bSGuenter Roeck ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 626*d560168bSGuenter Roeck if (!ptr) 627*d560168bSGuenter Roeck return ERR_PTR(-ENOMEM); 628*d560168bSGuenter Roeck 629*d560168bSGuenter Roeck hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip, 630*d560168bSGuenter Roeck groups); 631*d560168bSGuenter Roeck if (IS_ERR(hwdev)) 632*d560168bSGuenter Roeck goto error; 633*d560168bSGuenter Roeck 634*d560168bSGuenter Roeck *ptr = hwdev; 635*d560168bSGuenter Roeck devres_add(dev, ptr); 636*d560168bSGuenter Roeck 637*d560168bSGuenter Roeck return hwdev; 638*d560168bSGuenter Roeck 639*d560168bSGuenter Roeck error: 640*d560168bSGuenter Roeck devres_free(ptr); 641*d560168bSGuenter Roeck return hwdev; 642*d560168bSGuenter Roeck } 643*d560168bSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info); 644*d560168bSGuenter Roeck 64574188cbaSGuenter Roeck static int devm_hwmon_match(struct device *dev, void *res, void *data) 64674188cbaSGuenter Roeck { 64774188cbaSGuenter Roeck struct device **hwdev = res; 64874188cbaSGuenter Roeck 64974188cbaSGuenter Roeck return *hwdev == data; 65074188cbaSGuenter Roeck } 65174188cbaSGuenter Roeck 65274188cbaSGuenter Roeck /** 65374188cbaSGuenter Roeck * devm_hwmon_device_unregister - removes a previously registered hwmon device 65474188cbaSGuenter Roeck * 65574188cbaSGuenter Roeck * @dev: the parent device of the device to unregister 65674188cbaSGuenter Roeck */ 65774188cbaSGuenter Roeck void devm_hwmon_device_unregister(struct device *dev) 65874188cbaSGuenter Roeck { 65974188cbaSGuenter Roeck WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); 66074188cbaSGuenter Roeck } 66174188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); 66274188cbaSGuenter Roeck 6632958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 6642958b1ecSJean Delvare { 6652958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 6662958b1ecSJean Delvare struct pci_dev *sb; 6672958b1ecSJean Delvare u16 base; 6682958b1ecSJean Delvare u8 enable; 6692958b1ecSJean Delvare 6702958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 6712958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 672d6dab7ddSJean Delvare if (sb) { 673d6dab7ddSJean Delvare if (sb->subsystem_vendor == 0x1462 && /* MSI */ 674d6dab7ddSJean Delvare sb->subsystem_device == 0x0031) { /* MS-7031 */ 6752958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 6762958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 6772958b1ecSJean Delvare 6782958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 6792958b1ecSJean Delvare dev_info(&sb->dev, 6802958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 6812958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 682d6dab7ddSJean Delvare pci_write_config_byte(sb, 0x48, 683d6dab7ddSJean Delvare enable | BIT(2)); 6842958b1ecSJean Delvare } 6852958b1ecSJean Delvare } 686d6dab7ddSJean Delvare pci_dev_put(sb); 687d6dab7ddSJean Delvare } 6882958b1ecSJean Delvare #endif 6892958b1ecSJean Delvare } 6902958b1ecSJean Delvare 6911236441fSMark M. Hoffman static int __init hwmon_init(void) 6921236441fSMark M. Hoffman { 693bab2243cSGuenter Roeck int err; 694bab2243cSGuenter Roeck 6952958b1ecSJean Delvare hwmon_pci_quirks(); 6962958b1ecSJean Delvare 697bab2243cSGuenter Roeck err = class_register(&hwmon_class); 698bab2243cSGuenter Roeck if (err) { 699bab2243cSGuenter Roeck pr_err("couldn't register hwmon sysfs class\n"); 700bab2243cSGuenter Roeck return err; 7011236441fSMark M. Hoffman } 7021236441fSMark M. Hoffman return 0; 7031236441fSMark M. Hoffman } 7041236441fSMark M. Hoffman 7051236441fSMark M. Hoffman static void __exit hwmon_exit(void) 7061236441fSMark M. Hoffman { 707bab2243cSGuenter Roeck class_unregister(&hwmon_class); 7081236441fSMark M. Hoffman } 7091236441fSMark M. Hoffman 71037f54ee5SDavid Brownell subsys_initcall(hwmon_init); 7111236441fSMark M. Hoffman module_exit(hwmon_exit); 7121236441fSMark M. Hoffman 7131236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 7141236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 7151236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 7161236441fSMark M. Hoffman 717