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> 18*bab2243cSGuenter 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 29*bab2243cSGuenter Roeck struct hwmon_device { 30*bab2243cSGuenter Roeck const char *name; 31*bab2243cSGuenter Roeck struct device dev; 32*bab2243cSGuenter Roeck }; 33*bab2243cSGuenter Roeck #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) 34*bab2243cSGuenter Roeck 35*bab2243cSGuenter Roeck static ssize_t 36*bab2243cSGuenter Roeck show_name(struct device *dev, struct device_attribute *attr, char *buf) 37*bab2243cSGuenter Roeck { 38*bab2243cSGuenter Roeck return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); 39*bab2243cSGuenter Roeck } 40*bab2243cSGuenter Roeck static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); 41*bab2243cSGuenter Roeck 42*bab2243cSGuenter Roeck static struct attribute *hwmon_dev_attrs[] = { 43*bab2243cSGuenter Roeck &dev_attr_name.attr, 44*bab2243cSGuenter Roeck NULL 45*bab2243cSGuenter Roeck }; 46*bab2243cSGuenter Roeck 47*bab2243cSGuenter Roeck static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, 48*bab2243cSGuenter Roeck struct attribute *attr, int n) 49*bab2243cSGuenter Roeck { 50*bab2243cSGuenter Roeck struct device *dev = container_of(kobj, struct device, kobj); 51*bab2243cSGuenter Roeck 52*bab2243cSGuenter Roeck if (to_hwmon_device(dev)->name == NULL) 53*bab2243cSGuenter Roeck return 0; 54*bab2243cSGuenter Roeck 55*bab2243cSGuenter Roeck return attr->mode; 56*bab2243cSGuenter Roeck } 57*bab2243cSGuenter Roeck 58*bab2243cSGuenter Roeck static struct attribute_group hwmon_dev_attr_group = { 59*bab2243cSGuenter Roeck .attrs = hwmon_dev_attrs, 60*bab2243cSGuenter Roeck .is_visible = hwmon_dev_name_is_visible, 61*bab2243cSGuenter Roeck }; 62*bab2243cSGuenter Roeck 63*bab2243cSGuenter Roeck static const struct attribute_group *hwmon_dev_attr_groups[] = { 64*bab2243cSGuenter Roeck &hwmon_dev_attr_group, 65*bab2243cSGuenter Roeck NULL 66*bab2243cSGuenter Roeck }; 67*bab2243cSGuenter Roeck 68*bab2243cSGuenter Roeck static void hwmon_dev_release(struct device *dev) 69*bab2243cSGuenter Roeck { 70*bab2243cSGuenter Roeck kfree(to_hwmon_device(dev)); 71*bab2243cSGuenter Roeck } 72*bab2243cSGuenter Roeck 73*bab2243cSGuenter Roeck static struct class hwmon_class = { 74*bab2243cSGuenter Roeck .name = "hwmon", 75*bab2243cSGuenter Roeck .owner = THIS_MODULE, 76*bab2243cSGuenter Roeck .dev_groups = hwmon_dev_attr_groups, 77*bab2243cSGuenter Roeck .dev_release = hwmon_dev_release, 78*bab2243cSGuenter Roeck }; 791236441fSMark M. Hoffman 804ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida); 811236441fSMark M. Hoffman 821236441fSMark M. Hoffman /** 83*bab2243cSGuenter Roeck * hwmon_device_register_with_groups - register w/ hwmon 84*bab2243cSGuenter Roeck * @dev: the parent device 85*bab2243cSGuenter Roeck * @name: hwmon name attribute 86*bab2243cSGuenter Roeck * @drvdata: driver data to attach to created device 87*bab2243cSGuenter Roeck * @groups: List of attribute groups to create 88*bab2243cSGuenter Roeck * 89*bab2243cSGuenter Roeck * hwmon_device_unregister() must be called when the device is no 90*bab2243cSGuenter Roeck * longer needed. 91*bab2243cSGuenter Roeck * 92*bab2243cSGuenter Roeck * Returns the pointer to the new device. 93*bab2243cSGuenter Roeck */ 94*bab2243cSGuenter Roeck struct device * 95*bab2243cSGuenter Roeck hwmon_device_register_with_groups(struct device *dev, const char *name, 96*bab2243cSGuenter Roeck void *drvdata, 97*bab2243cSGuenter Roeck const struct attribute_group **groups) 98*bab2243cSGuenter Roeck { 99*bab2243cSGuenter Roeck struct hwmon_device *hwdev; 100*bab2243cSGuenter Roeck int err, id; 101*bab2243cSGuenter Roeck 102*bab2243cSGuenter Roeck id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); 103*bab2243cSGuenter Roeck if (id < 0) 104*bab2243cSGuenter Roeck return ERR_PTR(id); 105*bab2243cSGuenter Roeck 106*bab2243cSGuenter Roeck hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); 107*bab2243cSGuenter Roeck if (hwdev == NULL) { 108*bab2243cSGuenter Roeck err = -ENOMEM; 109*bab2243cSGuenter Roeck goto ida_remove; 110*bab2243cSGuenter Roeck } 111*bab2243cSGuenter Roeck 112*bab2243cSGuenter Roeck hwdev->name = name; 113*bab2243cSGuenter Roeck hwdev->dev.class = &hwmon_class; 114*bab2243cSGuenter Roeck hwdev->dev.parent = dev; 115*bab2243cSGuenter Roeck hwdev->dev.groups = groups; 116*bab2243cSGuenter Roeck hwdev->dev.of_node = dev ? dev->of_node : NULL; 117*bab2243cSGuenter Roeck dev_set_drvdata(&hwdev->dev, drvdata); 118*bab2243cSGuenter Roeck dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); 119*bab2243cSGuenter Roeck err = device_register(&hwdev->dev); 120*bab2243cSGuenter Roeck if (err) 121*bab2243cSGuenter Roeck goto free; 122*bab2243cSGuenter Roeck 123*bab2243cSGuenter Roeck return &hwdev->dev; 124*bab2243cSGuenter Roeck 125*bab2243cSGuenter Roeck free: 126*bab2243cSGuenter Roeck kfree(hwdev); 127*bab2243cSGuenter Roeck ida_remove: 128*bab2243cSGuenter Roeck ida_simple_remove(&hwmon_ida, id); 129*bab2243cSGuenter Roeck return ERR_PTR(err); 130*bab2243cSGuenter Roeck } 131*bab2243cSGuenter Roeck EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); 132*bab2243cSGuenter Roeck 133*bab2243cSGuenter 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 { 144*bab2243cSGuenter 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 1662958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 1672958b1ecSJean Delvare { 1682958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 1692958b1ecSJean Delvare struct pci_dev *sb; 1702958b1ecSJean Delvare u16 base; 1712958b1ecSJean Delvare u8 enable; 1722958b1ecSJean Delvare 1732958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 1742958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 175d6dab7ddSJean Delvare if (sb) { 176d6dab7ddSJean Delvare if (sb->subsystem_vendor == 0x1462 && /* MSI */ 177d6dab7ddSJean Delvare sb->subsystem_device == 0x0031) { /* MS-7031 */ 1782958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 1792958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 1802958b1ecSJean Delvare 1812958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 1822958b1ecSJean Delvare dev_info(&sb->dev, 1832958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 1842958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 185d6dab7ddSJean Delvare pci_write_config_byte(sb, 0x48, 186d6dab7ddSJean Delvare enable | BIT(2)); 1872958b1ecSJean Delvare } 1882958b1ecSJean Delvare } 189d6dab7ddSJean Delvare pci_dev_put(sb); 190d6dab7ddSJean Delvare } 1912958b1ecSJean Delvare #endif 1922958b1ecSJean Delvare } 1932958b1ecSJean Delvare 1941236441fSMark M. Hoffman static int __init hwmon_init(void) 1951236441fSMark M. Hoffman { 196*bab2243cSGuenter Roeck int err; 197*bab2243cSGuenter Roeck 1982958b1ecSJean Delvare hwmon_pci_quirks(); 1992958b1ecSJean Delvare 200*bab2243cSGuenter Roeck err = class_register(&hwmon_class); 201*bab2243cSGuenter Roeck if (err) { 202*bab2243cSGuenter Roeck pr_err("couldn't register hwmon sysfs class\n"); 203*bab2243cSGuenter Roeck return err; 2041236441fSMark M. Hoffman } 2051236441fSMark M. Hoffman return 0; 2061236441fSMark M. Hoffman } 2071236441fSMark M. Hoffman 2081236441fSMark M. Hoffman static void __exit hwmon_exit(void) 2091236441fSMark M. Hoffman { 210*bab2243cSGuenter Roeck class_unregister(&hwmon_class); 2111236441fSMark M. Hoffman } 2121236441fSMark M. Hoffman 21337f54ee5SDavid Brownell subsys_initcall(hwmon_init); 2141236441fSMark M. Hoffman module_exit(hwmon_exit); 2151236441fSMark M. Hoffman 2161236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 2171236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 2181236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 2191236441fSMark M. Hoffman 220