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 151236441fSMark M. Hoffman #include <linux/module.h> 161236441fSMark M. Hoffman #include <linux/device.h> 171236441fSMark M. Hoffman #include <linux/err.h> 18bab2243cSGuenter Roeck #include <linux/slab.h> 191236441fSMark M. Hoffman #include <linux/kdev_t.h> 201236441fSMark M. Hoffman #include <linux/idr.h> 211236441fSMark M. Hoffman #include <linux/hwmon.h> 228c65b4a6STim Schmielau #include <linux/gfp.h> 23ded2b666SMark M. Hoffman #include <linux/spinlock.h> 242958b1ecSJean Delvare #include <linux/pci.h> 251236441fSMark M. Hoffman 261236441fSMark M. Hoffman #define HWMON_ID_PREFIX "hwmon" 271236441fSMark M. Hoffman #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" 281236441fSMark M. Hoffman 29bab2243cSGuenter Roeck struct hwmon_device { 30bab2243cSGuenter Roeck const char *name; 31bab2243cSGuenter Roeck struct device dev; 32bab2243cSGuenter Roeck }; 33bab2243cSGuenter Roeck #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 34bab2243cSGuenter Roeck 35bab2243cSGuenter Roeck static ssize_t 36bab2243cSGuenter Roeck show_name(struct device *dev, struct device_attribute *attr, char *buf) 37bab2243cSGuenter Roeck { 38bab2243cSGuenter Roeck return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 39bab2243cSGuenter Roeck } 40bab2243cSGuenter Roeck static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 41bab2243cSGuenter Roeck 42bab2243cSGuenter Roeck static struct attribute *hwmon_dev_attrs[] = { 43bab2243cSGuenter Roeck &dev_attr_name.attr, 44bab2243cSGuenter Roeck NULL 45bab2243cSGuenter Roeck }; 46bab2243cSGuenter Roeck 47bab2243cSGuenter Roeck static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 48bab2243cSGuenter Roeck struct attribute *attr, int n) 49bab2243cSGuenter Roeck { 50bab2243cSGuenter Roeck struct device *dev = container_of(kobj, struct device, kobj); 51bab2243cSGuenter Roeck 52bab2243cSGuenter Roeck if (to_hwmon_device(dev)->name == NULL) 53bab2243cSGuenter Roeck return 0; 54bab2243cSGuenter Roeck 55bab2243cSGuenter Roeck return attr->mode; 56bab2243cSGuenter Roeck } 57bab2243cSGuenter Roeck 58bab2243cSGuenter Roeck static struct attribute_group hwmon_dev_attr_group = { 59bab2243cSGuenter Roeck .attrs = hwmon_dev_attrs, 60bab2243cSGuenter Roeck .is_visible = hwmon_dev_name_is_visible, 61bab2243cSGuenter Roeck }; 62bab2243cSGuenter Roeck 63bab2243cSGuenter Roeck static const struct attribute_group *hwmon_dev_attr_groups[] = { 64bab2243cSGuenter Roeck &hwmon_dev_attr_group, 65bab2243cSGuenter Roeck NULL 66bab2243cSGuenter Roeck }; 67bab2243cSGuenter Roeck 68bab2243cSGuenter Roeck static void hwmon_dev_release(struct device *dev) 69bab2243cSGuenter Roeck { 70bab2243cSGuenter Roeck kfree(to_hwmon_device(dev)); 71bab2243cSGuenter Roeck } 72bab2243cSGuenter Roeck 73bab2243cSGuenter Roeck static struct class hwmon_class = { 74bab2243cSGuenter Roeck .name = "hwmon", 75bab2243cSGuenter Roeck .owner = THIS_MODULE, 76bab2243cSGuenter Roeck .dev_groups = hwmon_dev_attr_groups, 77bab2243cSGuenter Roeck .dev_release = hwmon_dev_release, 78bab2243cSGuenter Roeck }; 791236441fSMark M. Hoffman 804ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida); 811236441fSMark M. Hoffman 821236441fSMark M. Hoffman /** 83bab2243cSGuenter Roeck * hwmon_device_register_with_groups - register w/ hwmon 84bab2243cSGuenter Roeck * @dev: the parent device 85bab2243cSGuenter Roeck * @name: hwmon name attribute 86bab2243cSGuenter Roeck * @drvdata: driver data to attach to created device 87bab2243cSGuenter Roeck * @groups: List of attribute groups to create 88bab2243cSGuenter Roeck * 89bab2243cSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 90bab2243cSGuenter Roeck * longer needed. 91bab2243cSGuenter Roeck * 92bab2243cSGuenter Roeck * Returns the pointer to the new device. 93bab2243cSGuenter Roeck */ 94bab2243cSGuenter Roeck struct device * 95bab2243cSGuenter Roeck hwmon_device_register_with_groups(struct device *dev, const char *name, 96bab2243cSGuenter Roeck void *drvdata, 97bab2243cSGuenter Roeck const struct attribute_group **groups) 98bab2243cSGuenter Roeck { 99bab2243cSGuenter Roeck struct hwmon_device *hwdev; 100bab2243cSGuenter Roeck int err, id; 101bab2243cSGuenter Roeck 102bab2243cSGuenter Roeck id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 103bab2243cSGuenter Roeck if (id < 0) 104bab2243cSGuenter Roeck return ERR_PTR(id); 105bab2243cSGuenter Roeck 106bab2243cSGuenter Roeck hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 107bab2243cSGuenter Roeck if (hwdev == NULL) { 108bab2243cSGuenter Roeck err = -ENOMEM; 109bab2243cSGuenter Roeck goto ida_remove; 110bab2243cSGuenter Roeck } 111bab2243cSGuenter Roeck 112bab2243cSGuenter Roeck hwdev->name = name; 113bab2243cSGuenter Roeck hwdev->dev.class = &hwmon_class; 114bab2243cSGuenter Roeck hwdev->dev.parent = dev; 115bab2243cSGuenter Roeck hwdev->dev.groups = groups; 116bab2243cSGuenter Roeck hwdev->dev.of_node = dev ? dev->of_node : NULL; 117bab2243cSGuenter Roeck dev_set_drvdata(&hwdev->dev, drvdata); 118bab2243cSGuenter Roeck dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); 119bab2243cSGuenter Roeck err = device_register(&hwdev->dev); 120bab2243cSGuenter Roeck if (err) 121bab2243cSGuenter Roeck goto free; 122bab2243cSGuenter Roeck 123bab2243cSGuenter Roeck return &hwdev->dev; 124bab2243cSGuenter Roeck 125bab2243cSGuenter Roeck free: 126bab2243cSGuenter Roeck kfree(hwdev); 127bab2243cSGuenter Roeck ida_remove: 128bab2243cSGuenter Roeck ida_simple_remove(&hwmon_ida, id); 129bab2243cSGuenter Roeck return ERR_PTR(err); 130bab2243cSGuenter Roeck } 131bab2243cSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 132bab2243cSGuenter Roeck 133bab2243cSGuenter Roeck /** 1341beeffe4STony Jones * hwmon_device_register - register w/ hwmon 1351236441fSMark M. Hoffman * @dev: the device to register 1361236441fSMark M. Hoffman * 1371beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 1381236441fSMark M. Hoffman * longer needed. 1391236441fSMark M. Hoffman * 1401beeffe4STony Jones * Returns the pointer to the new device. 1411236441fSMark M. Hoffman */ 1421beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 1431236441fSMark M. Hoffman { 144bab2243cSGuenter Roeck return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); 1451236441fSMark M. Hoffman } 146839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_register); 1471236441fSMark M. Hoffman 1481236441fSMark M. Hoffman /** 1491236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 1501236441fSMark M. Hoffman * 1511beeffe4STony Jones * @dev: the class device to destroy 1521236441fSMark M. Hoffman */ 1531beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 1541236441fSMark M. Hoffman { 1551236441fSMark M. Hoffman int id; 1561236441fSMark M. Hoffman 157739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 1581beeffe4STony Jones device_unregister(dev); 1594ca5f468SJonathan Cameron ida_simple_remove(&hwmon_ida, id); 1601236441fSMark M. Hoffman } else 1611beeffe4STony Jones dev_dbg(dev->parent, 1621236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 1631236441fSMark M. Hoffman } 164839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_unregister); 1651236441fSMark M. Hoffman 166*74188cbaSGuenter Roeck static void devm_hwmon_release(struct device *dev, void *res) 167*74188cbaSGuenter Roeck { 168*74188cbaSGuenter Roeck struct device *hwdev = *(struct device **)res; 169*74188cbaSGuenter Roeck 170*74188cbaSGuenter Roeck hwmon_device_unregister(hwdev); 171*74188cbaSGuenter Roeck } 172*74188cbaSGuenter Roeck 173*74188cbaSGuenter Roeck /** 174*74188cbaSGuenter Roeck * devm_hwmon_device_register_with_groups - register w/ hwmon 175*74188cbaSGuenter Roeck * @dev: the parent device 176*74188cbaSGuenter Roeck * @name: hwmon name attribute 177*74188cbaSGuenter Roeck * @drvdata: driver data to attach to created device 178*74188cbaSGuenter Roeck * @groups: List of attribute groups to create 179*74188cbaSGuenter Roeck * 180*74188cbaSGuenter Roeck * Returns the pointer to the new device. The new device is automatically 181*74188cbaSGuenter Roeck * unregistered with the parent device. 182*74188cbaSGuenter Roeck */ 183*74188cbaSGuenter Roeck struct device * 184*74188cbaSGuenter Roeck devm_hwmon_device_register_with_groups(struct device *dev, const char *name, 185*74188cbaSGuenter Roeck void *drvdata, 186*74188cbaSGuenter Roeck const struct attribute_group **groups) 187*74188cbaSGuenter Roeck { 188*74188cbaSGuenter Roeck struct device **ptr, *hwdev; 189*74188cbaSGuenter Roeck 190*74188cbaSGuenter Roeck if (!dev) 191*74188cbaSGuenter Roeck return ERR_PTR(-EINVAL); 192*74188cbaSGuenter Roeck 193*74188cbaSGuenter Roeck ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 194*74188cbaSGuenter Roeck if (!ptr) 195*74188cbaSGuenter Roeck return ERR_PTR(-ENOMEM); 196*74188cbaSGuenter Roeck 197*74188cbaSGuenter Roeck hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); 198*74188cbaSGuenter Roeck if (IS_ERR(hwdev)) 199*74188cbaSGuenter Roeck goto error; 200*74188cbaSGuenter Roeck 201*74188cbaSGuenter Roeck *ptr = hwdev; 202*74188cbaSGuenter Roeck devres_add(dev, ptr); 203*74188cbaSGuenter Roeck return hwdev; 204*74188cbaSGuenter Roeck 205*74188cbaSGuenter Roeck error: 206*74188cbaSGuenter Roeck devres_free(ptr); 207*74188cbaSGuenter Roeck return hwdev; 208*74188cbaSGuenter Roeck } 209*74188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); 210*74188cbaSGuenter Roeck 211*74188cbaSGuenter Roeck static int devm_hwmon_match(struct device *dev, void *res, void *data) 212*74188cbaSGuenter Roeck { 213*74188cbaSGuenter Roeck struct device **hwdev = res; 214*74188cbaSGuenter Roeck 215*74188cbaSGuenter Roeck return *hwdev == data; 216*74188cbaSGuenter Roeck } 217*74188cbaSGuenter Roeck 218*74188cbaSGuenter Roeck /** 219*74188cbaSGuenter Roeck * devm_hwmon_device_unregister - removes a previously registered hwmon device 220*74188cbaSGuenter Roeck * 221*74188cbaSGuenter Roeck * @dev: the parent device of the device to unregister 222*74188cbaSGuenter Roeck */ 223*74188cbaSGuenter Roeck void devm_hwmon_device_unregister(struct device *dev) 224*74188cbaSGuenter Roeck { 225*74188cbaSGuenter Roeck WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); 226*74188cbaSGuenter Roeck } 227*74188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); 228*74188cbaSGuenter Roeck 2292958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 2302958b1ecSJean Delvare { 2312958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 2322958b1ecSJean Delvare struct pci_dev *sb; 2332958b1ecSJean Delvare u16 base; 2342958b1ecSJean Delvare u8 enable; 2352958b1ecSJean Delvare 2362958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 2372958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 238d6dab7ddSJean Delvare if (sb) { 239d6dab7ddSJean Delvare if (sb->subsystem_vendor == 0x1462 && /* MSI */ 240d6dab7ddSJean Delvare sb->subsystem_device == 0x0031) { /* MS-7031 */ 2412958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 2422958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 2432958b1ecSJean Delvare 2442958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 2452958b1ecSJean Delvare dev_info(&sb->dev, 2462958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 2472958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 248d6dab7ddSJean Delvare pci_write_config_byte(sb, 0x48, 249d6dab7ddSJean Delvare enable | BIT(2)); 2502958b1ecSJean Delvare } 2512958b1ecSJean Delvare } 252d6dab7ddSJean Delvare pci_dev_put(sb); 253d6dab7ddSJean Delvare } 2542958b1ecSJean Delvare #endif 2552958b1ecSJean Delvare } 2562958b1ecSJean Delvare 2571236441fSMark M. Hoffman static int __init hwmon_init(void) 2581236441fSMark M. Hoffman { 259bab2243cSGuenter Roeck int err; 260bab2243cSGuenter Roeck 2612958b1ecSJean Delvare hwmon_pci_quirks(); 2622958b1ecSJean Delvare 263bab2243cSGuenter Roeck err = class_register(&hwmon_class); 264bab2243cSGuenter Roeck if (err) { 265bab2243cSGuenter Roeck pr_err("couldn't register hwmon sysfs class\n"); 266bab2243cSGuenter Roeck return err; 2671236441fSMark M. Hoffman } 2681236441fSMark M. Hoffman return 0; 2691236441fSMark M. Hoffman } 2701236441fSMark M. Hoffman 2711236441fSMark M. Hoffman static void __exit hwmon_exit(void) 2721236441fSMark M. Hoffman { 273bab2243cSGuenter Roeck class_unregister(&hwmon_class); 2741236441fSMark M. Hoffman } 2751236441fSMark M. Hoffman 27637f54ee5SDavid Brownell subsys_initcall(hwmon_init); 2771236441fSMark M. Hoffman module_exit(hwmon_exit); 2781236441fSMark M. Hoffman 2791236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 2801236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 2811236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 2821236441fSMark M. Hoffman 283