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> 25*648cd48cSGuenter Roeck #include <linux/string.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; 33bab2243cSGuenter Roeck }; 34bab2243cSGuenter Roeck #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 35bab2243cSGuenter Roeck 36bab2243cSGuenter Roeck static ssize_t 37bab2243cSGuenter Roeck show_name(struct device *dev, struct device_attribute *attr, char *buf) 38bab2243cSGuenter Roeck { 39bab2243cSGuenter Roeck return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 40bab2243cSGuenter Roeck } 41bab2243cSGuenter Roeck static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 42bab2243cSGuenter Roeck 43bab2243cSGuenter Roeck static struct attribute *hwmon_dev_attrs[] = { 44bab2243cSGuenter Roeck &dev_attr_name.attr, 45bab2243cSGuenter Roeck NULL 46bab2243cSGuenter Roeck }; 47bab2243cSGuenter Roeck 48bab2243cSGuenter Roeck static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 49bab2243cSGuenter Roeck struct attribute *attr, int n) 50bab2243cSGuenter Roeck { 51bab2243cSGuenter Roeck struct device *dev = container_of(kobj, struct device, kobj); 52bab2243cSGuenter Roeck 53bab2243cSGuenter Roeck if (to_hwmon_device(dev)->name == NULL) 54bab2243cSGuenter Roeck return 0; 55bab2243cSGuenter Roeck 56bab2243cSGuenter Roeck return attr->mode; 57bab2243cSGuenter Roeck } 58bab2243cSGuenter Roeck 59bab2243cSGuenter Roeck static struct attribute_group hwmon_dev_attr_group = { 60bab2243cSGuenter Roeck .attrs = hwmon_dev_attrs, 61bab2243cSGuenter Roeck .is_visible = hwmon_dev_name_is_visible, 62bab2243cSGuenter Roeck }; 63bab2243cSGuenter Roeck 64bab2243cSGuenter Roeck static const struct attribute_group *hwmon_dev_attr_groups[] = { 65bab2243cSGuenter Roeck &hwmon_dev_attr_group, 66bab2243cSGuenter Roeck NULL 67bab2243cSGuenter Roeck }; 68bab2243cSGuenter Roeck 69bab2243cSGuenter Roeck static void hwmon_dev_release(struct device *dev) 70bab2243cSGuenter Roeck { 71bab2243cSGuenter Roeck kfree(to_hwmon_device(dev)); 72bab2243cSGuenter Roeck } 73bab2243cSGuenter Roeck 74bab2243cSGuenter Roeck static struct class hwmon_class = { 75bab2243cSGuenter Roeck .name = "hwmon", 76bab2243cSGuenter Roeck .owner = THIS_MODULE, 77bab2243cSGuenter Roeck .dev_groups = hwmon_dev_attr_groups, 78bab2243cSGuenter Roeck .dev_release = hwmon_dev_release, 79bab2243cSGuenter Roeck }; 801236441fSMark M. Hoffman 814ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida); 821236441fSMark M. Hoffman 831236441fSMark M. Hoffman /** 84bab2243cSGuenter Roeck * hwmon_device_register_with_groups - register w/ hwmon 85bab2243cSGuenter Roeck * @dev: the parent device 86bab2243cSGuenter Roeck * @name: hwmon name attribute 87bab2243cSGuenter Roeck * @drvdata: driver data to attach to created device 88bab2243cSGuenter Roeck * @groups: List of attribute groups to create 89bab2243cSGuenter Roeck * 90bab2243cSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 91bab2243cSGuenter Roeck * longer needed. 92bab2243cSGuenter Roeck * 93bab2243cSGuenter Roeck * Returns the pointer to the new device. 94bab2243cSGuenter Roeck */ 95bab2243cSGuenter Roeck struct device * 96bab2243cSGuenter Roeck hwmon_device_register_with_groups(struct device *dev, const char *name, 97bab2243cSGuenter Roeck void *drvdata, 98bab2243cSGuenter Roeck const struct attribute_group **groups) 99bab2243cSGuenter Roeck { 100bab2243cSGuenter Roeck struct hwmon_device *hwdev; 101bab2243cSGuenter Roeck int err, id; 102bab2243cSGuenter Roeck 103*648cd48cSGuenter Roeck /* Do not accept invalid characters in hwmon name attribute */ 104*648cd48cSGuenter Roeck if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) 105*648cd48cSGuenter Roeck return ERR_PTR(-EINVAL); 106*648cd48cSGuenter Roeck 107bab2243cSGuenter Roeck id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 108bab2243cSGuenter Roeck if (id < 0) 109bab2243cSGuenter Roeck return ERR_PTR(id); 110bab2243cSGuenter Roeck 111bab2243cSGuenter Roeck hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 112bab2243cSGuenter Roeck if (hwdev == NULL) { 113bab2243cSGuenter Roeck err = -ENOMEM; 114bab2243cSGuenter Roeck goto ida_remove; 115bab2243cSGuenter Roeck } 116bab2243cSGuenter Roeck 117bab2243cSGuenter Roeck hwdev->name = name; 118bab2243cSGuenter Roeck hwdev->dev.class = &hwmon_class; 119bab2243cSGuenter Roeck hwdev->dev.parent = dev; 120bab2243cSGuenter Roeck hwdev->dev.groups = groups; 121bab2243cSGuenter Roeck hwdev->dev.of_node = dev ? dev->of_node : NULL; 122bab2243cSGuenter Roeck dev_set_drvdata(&hwdev->dev, drvdata); 123bab2243cSGuenter Roeck dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); 124bab2243cSGuenter Roeck err = device_register(&hwdev->dev); 125bab2243cSGuenter Roeck if (err) 126bab2243cSGuenter Roeck goto free; 127bab2243cSGuenter Roeck 128bab2243cSGuenter Roeck return &hwdev->dev; 129bab2243cSGuenter Roeck 130bab2243cSGuenter Roeck free: 131bab2243cSGuenter Roeck kfree(hwdev); 132bab2243cSGuenter Roeck ida_remove: 133bab2243cSGuenter Roeck ida_simple_remove(&hwmon_ida, id); 134bab2243cSGuenter Roeck return ERR_PTR(err); 135bab2243cSGuenter Roeck } 136bab2243cSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 137bab2243cSGuenter Roeck 138bab2243cSGuenter Roeck /** 1391beeffe4STony Jones * hwmon_device_register - register w/ hwmon 1401236441fSMark M. Hoffman * @dev: the device to register 1411236441fSMark M. Hoffman * 1421beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 1431236441fSMark M. Hoffman * longer needed. 1441236441fSMark M. Hoffman * 1451beeffe4STony Jones * Returns the pointer to the new device. 1461236441fSMark M. Hoffman */ 1471beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 1481236441fSMark M. Hoffman { 149bab2243cSGuenter Roeck return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); 1501236441fSMark M. Hoffman } 151839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_register); 1521236441fSMark M. Hoffman 1531236441fSMark M. Hoffman /** 1541236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 1551236441fSMark M. Hoffman * 1561beeffe4STony Jones * @dev: the class device to destroy 1571236441fSMark M. Hoffman */ 1581beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 1591236441fSMark M. Hoffman { 1601236441fSMark M. Hoffman int id; 1611236441fSMark M. Hoffman 162739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 1631beeffe4STony Jones device_unregister(dev); 1644ca5f468SJonathan Cameron ida_simple_remove(&hwmon_ida, id); 1651236441fSMark M. Hoffman } else 1661beeffe4STony Jones dev_dbg(dev->parent, 1671236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 1681236441fSMark M. Hoffman } 169839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_unregister); 1701236441fSMark M. Hoffman 17174188cbaSGuenter Roeck static void devm_hwmon_release(struct device *dev, void *res) 17274188cbaSGuenter Roeck { 17374188cbaSGuenter Roeck struct device *hwdev = *(struct device **)res; 17474188cbaSGuenter Roeck 17574188cbaSGuenter Roeck hwmon_device_unregister(hwdev); 17674188cbaSGuenter Roeck } 17774188cbaSGuenter Roeck 17874188cbaSGuenter Roeck /** 17974188cbaSGuenter Roeck * devm_hwmon_device_register_with_groups - register w/ hwmon 18074188cbaSGuenter Roeck * @dev: the parent device 18174188cbaSGuenter Roeck * @name: hwmon name attribute 18274188cbaSGuenter Roeck * @drvdata: driver data to attach to created device 18374188cbaSGuenter Roeck * @groups: List of attribute groups to create 18474188cbaSGuenter Roeck * 18574188cbaSGuenter Roeck * Returns the pointer to the new device. The new device is automatically 18674188cbaSGuenter Roeck * unregistered with the parent device. 18774188cbaSGuenter Roeck */ 18874188cbaSGuenter Roeck struct device * 18974188cbaSGuenter Roeck devm_hwmon_device_register_with_groups(struct device *dev, const char *name, 19074188cbaSGuenter Roeck void *drvdata, 19174188cbaSGuenter Roeck const struct attribute_group **groups) 19274188cbaSGuenter Roeck { 19374188cbaSGuenter Roeck struct device **ptr, *hwdev; 19474188cbaSGuenter Roeck 19574188cbaSGuenter Roeck if (!dev) 19674188cbaSGuenter Roeck return ERR_PTR(-EINVAL); 19774188cbaSGuenter Roeck 19874188cbaSGuenter Roeck ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 19974188cbaSGuenter Roeck if (!ptr) 20074188cbaSGuenter Roeck return ERR_PTR(-ENOMEM); 20174188cbaSGuenter Roeck 20274188cbaSGuenter Roeck hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); 20374188cbaSGuenter Roeck if (IS_ERR(hwdev)) 20474188cbaSGuenter Roeck goto error; 20574188cbaSGuenter Roeck 20674188cbaSGuenter Roeck *ptr = hwdev; 20774188cbaSGuenter Roeck devres_add(dev, ptr); 20874188cbaSGuenter Roeck return hwdev; 20974188cbaSGuenter Roeck 21074188cbaSGuenter Roeck error: 21174188cbaSGuenter Roeck devres_free(ptr); 21274188cbaSGuenter Roeck return hwdev; 21374188cbaSGuenter Roeck } 21474188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); 21574188cbaSGuenter Roeck 21674188cbaSGuenter Roeck static int devm_hwmon_match(struct device *dev, void *res, void *data) 21774188cbaSGuenter Roeck { 21874188cbaSGuenter Roeck struct device **hwdev = res; 21974188cbaSGuenter Roeck 22074188cbaSGuenter Roeck return *hwdev == data; 22174188cbaSGuenter Roeck } 22274188cbaSGuenter Roeck 22374188cbaSGuenter Roeck /** 22474188cbaSGuenter Roeck * devm_hwmon_device_unregister - removes a previously registered hwmon device 22574188cbaSGuenter Roeck * 22674188cbaSGuenter Roeck * @dev: the parent device of the device to unregister 22774188cbaSGuenter Roeck */ 22874188cbaSGuenter Roeck void devm_hwmon_device_unregister(struct device *dev) 22974188cbaSGuenter Roeck { 23074188cbaSGuenter Roeck WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); 23174188cbaSGuenter Roeck } 23274188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); 23374188cbaSGuenter Roeck 2342958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 2352958b1ecSJean Delvare { 2362958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 2372958b1ecSJean Delvare struct pci_dev *sb; 2382958b1ecSJean Delvare u16 base; 2392958b1ecSJean Delvare u8 enable; 2402958b1ecSJean Delvare 2412958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 2422958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 243d6dab7ddSJean Delvare if (sb) { 244d6dab7ddSJean Delvare if (sb->subsystem_vendor == 0x1462 && /* MSI */ 245d6dab7ddSJean Delvare sb->subsystem_device == 0x0031) { /* MS-7031 */ 2462958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 2472958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 2482958b1ecSJean Delvare 2492958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 2502958b1ecSJean Delvare dev_info(&sb->dev, 2512958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 2522958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 253d6dab7ddSJean Delvare pci_write_config_byte(sb, 0x48, 254d6dab7ddSJean Delvare enable | BIT(2)); 2552958b1ecSJean Delvare } 2562958b1ecSJean Delvare } 257d6dab7ddSJean Delvare pci_dev_put(sb); 258d6dab7ddSJean Delvare } 2592958b1ecSJean Delvare #endif 2602958b1ecSJean Delvare } 2612958b1ecSJean Delvare 2621236441fSMark M. Hoffman static int __init hwmon_init(void) 2631236441fSMark M. Hoffman { 264bab2243cSGuenter Roeck int err; 265bab2243cSGuenter Roeck 2662958b1ecSJean Delvare hwmon_pci_quirks(); 2672958b1ecSJean Delvare 268bab2243cSGuenter Roeck err = class_register(&hwmon_class); 269bab2243cSGuenter Roeck if (err) { 270bab2243cSGuenter Roeck pr_err("couldn't register hwmon sysfs class\n"); 271bab2243cSGuenter Roeck return err; 2721236441fSMark M. Hoffman } 2731236441fSMark M. Hoffman return 0; 2741236441fSMark M. Hoffman } 2751236441fSMark M. Hoffman 2761236441fSMark M. Hoffman static void __exit hwmon_exit(void) 2771236441fSMark M. Hoffman { 278bab2243cSGuenter Roeck class_unregister(&hwmon_class); 2791236441fSMark M. Hoffman } 2801236441fSMark M. Hoffman 28137f54ee5SDavid Brownell subsys_initcall(hwmon_init); 2821236441fSMark M. Hoffman module_exit(hwmon_exit); 2831236441fSMark M. Hoffman 2841236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 2851236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 2861236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 2871236441fSMark M. Hoffman 288