11236441fSMark M. Hoffman /* 21236441fSMark M. Hoffman hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring 31236441fSMark M. Hoffman 41236441fSMark M. Hoffman This file defines the sysfs class "hwmon", for use by sensors drivers. 51236441fSMark M. Hoffman 61236441fSMark M. Hoffman Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com> 71236441fSMark M. Hoffman 81236441fSMark M. Hoffman This program is free software; you can redistribute it and/or modify 91236441fSMark M. Hoffman it under the terms of the GNU General Public License as published by 101236441fSMark M. Hoffman the Free Software Foundation; version 2 of the License. 111236441fSMark M. Hoffman */ 121236441fSMark M. Hoffman 131236441fSMark M. Hoffman #include <linux/module.h> 141236441fSMark M. Hoffman #include <linux/device.h> 151236441fSMark M. Hoffman #include <linux/err.h> 161236441fSMark M. Hoffman #include <linux/kdev_t.h> 171236441fSMark M. Hoffman #include <linux/idr.h> 181236441fSMark M. Hoffman #include <linux/hwmon.h> 198c65b4a6STim Schmielau #include <linux/gfp.h> 20ded2b666SMark M. Hoffman #include <linux/spinlock.h> 21*2958b1ecSJean Delvare #include <linux/pci.h> 221236441fSMark M. Hoffman 231236441fSMark M. Hoffman #define HWMON_ID_PREFIX "hwmon" 241236441fSMark M. Hoffman #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" 251236441fSMark M. Hoffman 261236441fSMark M. Hoffman static struct class *hwmon_class; 271236441fSMark M. Hoffman 281236441fSMark M. Hoffman static DEFINE_IDR(hwmon_idr); 29ded2b666SMark M. Hoffman static DEFINE_SPINLOCK(idr_lock); 301236441fSMark M. Hoffman 311236441fSMark M. Hoffman /** 321beeffe4STony Jones * hwmon_device_register - register w/ hwmon 331236441fSMark M. Hoffman * @dev: the device to register 341236441fSMark M. Hoffman * 351beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 361236441fSMark M. Hoffman * longer needed. 371236441fSMark M. Hoffman * 381beeffe4STony Jones * Returns the pointer to the new device. 391236441fSMark M. Hoffman */ 401beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 411236441fSMark M. Hoffman { 421beeffe4STony Jones struct device *hwdev; 43ded2b666SMark M. Hoffman int id, err; 441236441fSMark M. Hoffman 45ded2b666SMark M. Hoffman again: 46ded2b666SMark M. Hoffman if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) 471236441fSMark M. Hoffman return ERR_PTR(-ENOMEM); 481236441fSMark M. Hoffman 49ded2b666SMark M. Hoffman spin_lock(&idr_lock); 50ded2b666SMark M. Hoffman err = idr_get_new(&hwmon_idr, NULL, &id); 51ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 52ded2b666SMark M. Hoffman 53ded2b666SMark M. Hoffman if (unlikely(err == -EAGAIN)) 54ded2b666SMark M. Hoffman goto again; 55ded2b666SMark M. Hoffman else if (unlikely(err)) 56ded2b666SMark M. Hoffman return ERR_PTR(err); 571236441fSMark M. Hoffman 581236441fSMark M. Hoffman id = id & MAX_ID_MASK; 59a9b12619SGreg Kroah-Hartman hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, 602871f552SGreg Kroah-Hartman HWMON_ID_FORMAT, id); 611236441fSMark M. Hoffman 621beeffe4STony Jones if (IS_ERR(hwdev)) { 63ded2b666SMark M. Hoffman spin_lock(&idr_lock); 641236441fSMark M. Hoffman idr_remove(&hwmon_idr, id); 65ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 66ded2b666SMark M. Hoffman } 671236441fSMark M. Hoffman 681beeffe4STony Jones return hwdev; 691236441fSMark M. Hoffman } 701236441fSMark M. Hoffman 711236441fSMark M. Hoffman /** 721236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 731236441fSMark M. Hoffman * 741beeffe4STony Jones * @dev: the class device to destroy 751236441fSMark M. Hoffman */ 761beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 771236441fSMark M. Hoffman { 781236441fSMark M. Hoffman int id; 791236441fSMark M. Hoffman 80739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 811beeffe4STony Jones device_unregister(dev); 82ded2b666SMark M. Hoffman spin_lock(&idr_lock); 831236441fSMark M. Hoffman idr_remove(&hwmon_idr, id); 84ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 851236441fSMark M. Hoffman } else 861beeffe4STony Jones dev_dbg(dev->parent, 871236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 881236441fSMark M. Hoffman } 891236441fSMark M. Hoffman 90*2958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 91*2958b1ecSJean Delvare { 92*2958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 93*2958b1ecSJean Delvare struct pci_dev *sb; 94*2958b1ecSJean Delvare u16 base; 95*2958b1ecSJean Delvare u8 enable; 96*2958b1ecSJean Delvare 97*2958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 98*2958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 99*2958b1ecSJean Delvare if (sb && 100*2958b1ecSJean Delvare (sb->subsystem_vendor == 0x1462 && /* MSI */ 101*2958b1ecSJean Delvare sb->subsystem_device == 0x0031)) { /* MS-7031 */ 102*2958b1ecSJean Delvare 103*2958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 104*2958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 105*2958b1ecSJean Delvare 106*2958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 107*2958b1ecSJean Delvare dev_info(&sb->dev, 108*2958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 109*2958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 110*2958b1ecSJean Delvare pci_write_config_byte(sb, 0x48, enable | BIT(2)); 111*2958b1ecSJean Delvare } 112*2958b1ecSJean Delvare } 113*2958b1ecSJean Delvare #endif 114*2958b1ecSJean Delvare } 115*2958b1ecSJean Delvare 1161236441fSMark M. Hoffman static int __init hwmon_init(void) 1171236441fSMark M. Hoffman { 118*2958b1ecSJean Delvare hwmon_pci_quirks(); 119*2958b1ecSJean Delvare 1201236441fSMark M. Hoffman hwmon_class = class_create(THIS_MODULE, "hwmon"); 1211236441fSMark M. Hoffman if (IS_ERR(hwmon_class)) { 1221236441fSMark M. Hoffman printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n"); 1231236441fSMark M. Hoffman return PTR_ERR(hwmon_class); 1241236441fSMark M. Hoffman } 1251236441fSMark M. Hoffman return 0; 1261236441fSMark M. Hoffman } 1271236441fSMark M. Hoffman 1281236441fSMark M. Hoffman static void __exit hwmon_exit(void) 1291236441fSMark M. Hoffman { 1301236441fSMark M. Hoffman class_destroy(hwmon_class); 1311236441fSMark M. Hoffman } 1321236441fSMark M. Hoffman 13337f54ee5SDavid Brownell subsys_initcall(hwmon_init); 1341236441fSMark M. Hoffman module_exit(hwmon_exit); 1351236441fSMark M. Hoffman 1361236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_register); 1371236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_unregister); 1381236441fSMark M. Hoffman 1391236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 1401236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 1411236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 1421236441fSMark M. Hoffman 143