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/device.h> 161236441fSMark M. Hoffman #include <linux/err.h> 178c65b4a6STim Schmielau #include <linux/gfp.h> 18*c9ebbe6fSGuenter Roeck #include <linux/hwmon.h> 19*c9ebbe6fSGuenter Roeck #include <linux/idr.h> 20*c9ebbe6fSGuenter Roeck #include <linux/module.h> 212958b1ecSJean Delvare #include <linux/pci.h> 22*c9ebbe6fSGuenter Roeck #include <linux/slab.h> 23648cd48cSGuenter Roeck #include <linux/string.h> 241236441fSMark M. Hoffman 251236441fSMark M. Hoffman #define HWMON_ID_PREFIX "hwmon" 261236441fSMark M. Hoffman #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" 271236441fSMark M. Hoffman 28bab2243cSGuenter Roeck struct hwmon_device { 29bab2243cSGuenter Roeck const char *name; 30bab2243cSGuenter Roeck struct device dev; 31bab2243cSGuenter Roeck }; 32bab2243cSGuenter Roeck #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 33bab2243cSGuenter Roeck 34bab2243cSGuenter Roeck static ssize_t 35bab2243cSGuenter Roeck show_name(struct device *dev, struct device_attribute *attr, char *buf) 36bab2243cSGuenter Roeck { 37bab2243cSGuenter Roeck return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 38bab2243cSGuenter Roeck } 39bab2243cSGuenter Roeck static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 40bab2243cSGuenter Roeck 41bab2243cSGuenter Roeck static struct attribute *hwmon_dev_attrs[] = { 42bab2243cSGuenter Roeck &dev_attr_name.attr, 43bab2243cSGuenter Roeck NULL 44bab2243cSGuenter Roeck }; 45bab2243cSGuenter Roeck 46bab2243cSGuenter Roeck static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 47bab2243cSGuenter Roeck struct attribute *attr, int n) 48bab2243cSGuenter Roeck { 49bab2243cSGuenter Roeck struct device *dev = container_of(kobj, struct device, kobj); 50bab2243cSGuenter Roeck 51bab2243cSGuenter Roeck if (to_hwmon_device(dev)->name == NULL) 52bab2243cSGuenter Roeck return 0; 53bab2243cSGuenter Roeck 54bab2243cSGuenter Roeck return attr->mode; 55bab2243cSGuenter Roeck } 56bab2243cSGuenter Roeck 57bab2243cSGuenter Roeck static struct attribute_group hwmon_dev_attr_group = { 58bab2243cSGuenter Roeck .attrs = hwmon_dev_attrs, 59bab2243cSGuenter Roeck .is_visible = hwmon_dev_name_is_visible, 60bab2243cSGuenter Roeck }; 61bab2243cSGuenter Roeck 62bab2243cSGuenter Roeck static const struct attribute_group *hwmon_dev_attr_groups[] = { 63bab2243cSGuenter Roeck &hwmon_dev_attr_group, 64bab2243cSGuenter Roeck NULL 65bab2243cSGuenter Roeck }; 66bab2243cSGuenter Roeck 67bab2243cSGuenter Roeck static void hwmon_dev_release(struct device *dev) 68bab2243cSGuenter Roeck { 69bab2243cSGuenter Roeck kfree(to_hwmon_device(dev)); 70bab2243cSGuenter Roeck } 71bab2243cSGuenter Roeck 72bab2243cSGuenter Roeck static struct class hwmon_class = { 73bab2243cSGuenter Roeck .name = "hwmon", 74bab2243cSGuenter Roeck .owner = THIS_MODULE, 75bab2243cSGuenter Roeck .dev_groups = hwmon_dev_attr_groups, 76bab2243cSGuenter Roeck .dev_release = hwmon_dev_release, 77bab2243cSGuenter Roeck }; 781236441fSMark M. Hoffman 794ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida); 801236441fSMark M. Hoffman 811236441fSMark M. Hoffman /** 82bab2243cSGuenter Roeck * hwmon_device_register_with_groups - register w/ hwmon 83bab2243cSGuenter Roeck * @dev: the parent device 84bab2243cSGuenter Roeck * @name: hwmon name attribute 85bab2243cSGuenter Roeck * @drvdata: driver data to attach to created device 86bab2243cSGuenter Roeck * @groups: List of attribute groups to create 87bab2243cSGuenter Roeck * 88bab2243cSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 89bab2243cSGuenter Roeck * longer needed. 90bab2243cSGuenter Roeck * 91bab2243cSGuenter Roeck * Returns the pointer to the new device. 92bab2243cSGuenter Roeck */ 93bab2243cSGuenter Roeck struct device * 94bab2243cSGuenter Roeck hwmon_device_register_with_groups(struct device *dev, const char *name, 95bab2243cSGuenter Roeck void *drvdata, 96bab2243cSGuenter Roeck const struct attribute_group **groups) 97bab2243cSGuenter Roeck { 98bab2243cSGuenter Roeck struct hwmon_device *hwdev; 99bab2243cSGuenter Roeck int err, id; 100bab2243cSGuenter Roeck 101648cd48cSGuenter Roeck /* Do not accept invalid characters in hwmon name attribute */ 102648cd48cSGuenter Roeck if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) 103648cd48cSGuenter Roeck return ERR_PTR(-EINVAL); 104648cd48cSGuenter Roeck 105bab2243cSGuenter Roeck id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 106bab2243cSGuenter Roeck if (id < 0) 107bab2243cSGuenter Roeck return ERR_PTR(id); 108bab2243cSGuenter Roeck 109bab2243cSGuenter Roeck hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 110bab2243cSGuenter Roeck if (hwdev == NULL) { 111bab2243cSGuenter Roeck err = -ENOMEM; 112bab2243cSGuenter Roeck goto ida_remove; 113bab2243cSGuenter Roeck } 114bab2243cSGuenter Roeck 115bab2243cSGuenter Roeck hwdev->name = name; 116bab2243cSGuenter Roeck hwdev->dev.class = &hwmon_class; 117bab2243cSGuenter Roeck hwdev->dev.parent = dev; 118bab2243cSGuenter Roeck hwdev->dev.groups = groups; 119bab2243cSGuenter Roeck hwdev->dev.of_node = dev ? dev->of_node : NULL; 120bab2243cSGuenter Roeck dev_set_drvdata(&hwdev->dev, drvdata); 121bab2243cSGuenter Roeck dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); 122bab2243cSGuenter Roeck err = device_register(&hwdev->dev); 123bab2243cSGuenter Roeck if (err) 124bab2243cSGuenter Roeck goto free; 125bab2243cSGuenter Roeck 126bab2243cSGuenter Roeck return &hwdev->dev; 127bab2243cSGuenter Roeck 128bab2243cSGuenter Roeck free: 129bab2243cSGuenter Roeck kfree(hwdev); 130bab2243cSGuenter Roeck ida_remove: 131bab2243cSGuenter Roeck ida_simple_remove(&hwmon_ida, id); 132bab2243cSGuenter Roeck return ERR_PTR(err); 133bab2243cSGuenter Roeck } 134bab2243cSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 135bab2243cSGuenter Roeck 136bab2243cSGuenter Roeck /** 1371beeffe4STony Jones * hwmon_device_register - register w/ hwmon 1381236441fSMark M. Hoffman * @dev: the device to register 1391236441fSMark M. Hoffman * 1401beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 1411236441fSMark M. Hoffman * longer needed. 1421236441fSMark M. Hoffman * 1431beeffe4STony Jones * Returns the pointer to the new device. 1441236441fSMark M. Hoffman */ 1451beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 1461236441fSMark M. Hoffman { 147bab2243cSGuenter Roeck return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); 1481236441fSMark M. Hoffman } 149839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_register); 1501236441fSMark M. Hoffman 1511236441fSMark M. Hoffman /** 1521236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 1531236441fSMark M. Hoffman * 1541beeffe4STony Jones * @dev: the class device to destroy 1551236441fSMark M. Hoffman */ 1561beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 1571236441fSMark M. Hoffman { 1581236441fSMark M. Hoffman int id; 1591236441fSMark M. Hoffman 160739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 1611beeffe4STony Jones device_unregister(dev); 1624ca5f468SJonathan Cameron ida_simple_remove(&hwmon_ida, id); 1631236441fSMark M. Hoffman } else 1641beeffe4STony Jones dev_dbg(dev->parent, 1651236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 1661236441fSMark M. Hoffman } 167839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_unregister); 1681236441fSMark M. Hoffman 16974188cbaSGuenter Roeck static void devm_hwmon_release(struct device *dev, void *res) 17074188cbaSGuenter Roeck { 17174188cbaSGuenter Roeck struct device *hwdev = *(struct device **)res; 17274188cbaSGuenter Roeck 17374188cbaSGuenter Roeck hwmon_device_unregister(hwdev); 17474188cbaSGuenter Roeck } 17574188cbaSGuenter Roeck 17674188cbaSGuenter Roeck /** 17774188cbaSGuenter Roeck * devm_hwmon_device_register_with_groups - register w/ hwmon 17874188cbaSGuenter Roeck * @dev: the parent device 17974188cbaSGuenter Roeck * @name: hwmon name attribute 18074188cbaSGuenter Roeck * @drvdata: driver data to attach to created device 18174188cbaSGuenter Roeck * @groups: List of attribute groups to create 18274188cbaSGuenter Roeck * 18374188cbaSGuenter Roeck * Returns the pointer to the new device. The new device is automatically 18474188cbaSGuenter Roeck * unregistered with the parent device. 18574188cbaSGuenter Roeck */ 18674188cbaSGuenter Roeck struct device * 18774188cbaSGuenter Roeck devm_hwmon_device_register_with_groups(struct device *dev, const char *name, 18874188cbaSGuenter Roeck void *drvdata, 18974188cbaSGuenter Roeck const struct attribute_group **groups) 19074188cbaSGuenter Roeck { 19174188cbaSGuenter Roeck struct device **ptr, *hwdev; 19274188cbaSGuenter Roeck 19374188cbaSGuenter Roeck if (!dev) 19474188cbaSGuenter Roeck return ERR_PTR(-EINVAL); 19574188cbaSGuenter Roeck 19674188cbaSGuenter Roeck ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); 19774188cbaSGuenter Roeck if (!ptr) 19874188cbaSGuenter Roeck return ERR_PTR(-ENOMEM); 19974188cbaSGuenter Roeck 20074188cbaSGuenter Roeck hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); 20174188cbaSGuenter Roeck if (IS_ERR(hwdev)) 20274188cbaSGuenter Roeck goto error; 20374188cbaSGuenter Roeck 20474188cbaSGuenter Roeck *ptr = hwdev; 20574188cbaSGuenter Roeck devres_add(dev, ptr); 20674188cbaSGuenter Roeck return hwdev; 20774188cbaSGuenter Roeck 20874188cbaSGuenter Roeck error: 20974188cbaSGuenter Roeck devres_free(ptr); 21074188cbaSGuenter Roeck return hwdev; 21174188cbaSGuenter Roeck } 21274188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); 21374188cbaSGuenter Roeck 21474188cbaSGuenter Roeck static int devm_hwmon_match(struct device *dev, void *res, void *data) 21574188cbaSGuenter Roeck { 21674188cbaSGuenter Roeck struct device **hwdev = res; 21774188cbaSGuenter Roeck 21874188cbaSGuenter Roeck return *hwdev == data; 21974188cbaSGuenter Roeck } 22074188cbaSGuenter Roeck 22174188cbaSGuenter Roeck /** 22274188cbaSGuenter Roeck * devm_hwmon_device_unregister - removes a previously registered hwmon device 22374188cbaSGuenter Roeck * 22474188cbaSGuenter Roeck * @dev: the parent device of the device to unregister 22574188cbaSGuenter Roeck */ 22674188cbaSGuenter Roeck void devm_hwmon_device_unregister(struct device *dev) 22774188cbaSGuenter Roeck { 22874188cbaSGuenter Roeck WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); 22974188cbaSGuenter Roeck } 23074188cbaSGuenter Roeck EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); 23174188cbaSGuenter Roeck 2322958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 2332958b1ecSJean Delvare { 2342958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 2352958b1ecSJean Delvare struct pci_dev *sb; 2362958b1ecSJean Delvare u16 base; 2372958b1ecSJean Delvare u8 enable; 2382958b1ecSJean Delvare 2392958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 2402958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 241d6dab7ddSJean Delvare if (sb) { 242d6dab7ddSJean Delvare if (sb->subsystem_vendor == 0x1462 && /* MSI */ 243d6dab7ddSJean Delvare sb->subsystem_device == 0x0031) { /* MS-7031 */ 2442958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 2452958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 2462958b1ecSJean Delvare 2472958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 2482958b1ecSJean Delvare dev_info(&sb->dev, 2492958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 2502958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 251d6dab7ddSJean Delvare pci_write_config_byte(sb, 0x48, 252d6dab7ddSJean Delvare enable | BIT(2)); 2532958b1ecSJean Delvare } 2542958b1ecSJean Delvare } 255d6dab7ddSJean Delvare pci_dev_put(sb); 256d6dab7ddSJean Delvare } 2572958b1ecSJean Delvare #endif 2582958b1ecSJean Delvare } 2592958b1ecSJean Delvare 2601236441fSMark M. Hoffman static int __init hwmon_init(void) 2611236441fSMark M. Hoffman { 262bab2243cSGuenter Roeck int err; 263bab2243cSGuenter Roeck 2642958b1ecSJean Delvare hwmon_pci_quirks(); 2652958b1ecSJean Delvare 266bab2243cSGuenter Roeck err = class_register(&hwmon_class); 267bab2243cSGuenter Roeck if (err) { 268bab2243cSGuenter Roeck pr_err("couldn't register hwmon sysfs class\n"); 269bab2243cSGuenter Roeck return err; 2701236441fSMark M. Hoffman } 2711236441fSMark M. Hoffman return 0; 2721236441fSMark M. Hoffman } 2731236441fSMark M. Hoffman 2741236441fSMark M. Hoffman static void __exit hwmon_exit(void) 2751236441fSMark M. Hoffman { 276bab2243cSGuenter Roeck class_unregister(&hwmon_class); 2771236441fSMark M. Hoffman } 2781236441fSMark M. Hoffman 27937f54ee5SDavid Brownell subsys_initcall(hwmon_init); 2801236441fSMark M. Hoffman module_exit(hwmon_exit); 2811236441fSMark M. Hoffman 2821236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 2831236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 2841236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 2851236441fSMark M. Hoffman 286