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 13*c95df1aeSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 14*c95df1aeSJoe Perches 151236441fSMark M. Hoffman #include <linux/module.h> 161236441fSMark M. Hoffman #include <linux/device.h> 171236441fSMark M. Hoffman #include <linux/err.h> 181236441fSMark M. Hoffman #include <linux/kdev_t.h> 191236441fSMark M. Hoffman #include <linux/idr.h> 201236441fSMark M. Hoffman #include <linux/hwmon.h> 218c65b4a6STim Schmielau #include <linux/gfp.h> 22ded2b666SMark M. Hoffman #include <linux/spinlock.h> 232958b1ecSJean Delvare #include <linux/pci.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 281236441fSMark M. Hoffman static struct class *hwmon_class; 291236441fSMark M. Hoffman 301236441fSMark M. Hoffman static DEFINE_IDR(hwmon_idr); 31ded2b666SMark M. Hoffman static DEFINE_SPINLOCK(idr_lock); 321236441fSMark M. Hoffman 331236441fSMark M. Hoffman /** 341beeffe4STony Jones * hwmon_device_register - register w/ hwmon 351236441fSMark M. Hoffman * @dev: the device to register 361236441fSMark M. Hoffman * 371beeffe4STony Jones * hwmon_device_unregister() must be called when the device is no 381236441fSMark M. Hoffman * longer needed. 391236441fSMark M. Hoffman * 401beeffe4STony Jones * Returns the pointer to the new device. 411236441fSMark M. Hoffman */ 421beeffe4STony Jones struct device *hwmon_device_register(struct device *dev) 431236441fSMark M. Hoffman { 441beeffe4STony Jones struct device *hwdev; 45ded2b666SMark M. Hoffman int id, err; 461236441fSMark M. Hoffman 47ded2b666SMark M. Hoffman again: 48ded2b666SMark M. Hoffman if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) 491236441fSMark M. Hoffman return ERR_PTR(-ENOMEM); 501236441fSMark M. Hoffman 51ded2b666SMark M. Hoffman spin_lock(&idr_lock); 52ded2b666SMark M. Hoffman err = idr_get_new(&hwmon_idr, NULL, &id); 53ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 54ded2b666SMark M. Hoffman 55ded2b666SMark M. Hoffman if (unlikely(err == -EAGAIN)) 56ded2b666SMark M. Hoffman goto again; 57ded2b666SMark M. Hoffman else if (unlikely(err)) 58ded2b666SMark M. Hoffman return ERR_PTR(err); 591236441fSMark M. Hoffman 601236441fSMark M. Hoffman id = id & MAX_ID_MASK; 61a9b12619SGreg Kroah-Hartman hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, 622871f552SGreg Kroah-Hartman HWMON_ID_FORMAT, id); 631236441fSMark M. Hoffman 641beeffe4STony Jones if (IS_ERR(hwdev)) { 65ded2b666SMark M. Hoffman spin_lock(&idr_lock); 661236441fSMark M. Hoffman idr_remove(&hwmon_idr, id); 67ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 68ded2b666SMark M. Hoffman } 691236441fSMark M. Hoffman 701beeffe4STony Jones return hwdev; 711236441fSMark M. Hoffman } 721236441fSMark M. Hoffman 731236441fSMark M. Hoffman /** 741236441fSMark M. Hoffman * hwmon_device_unregister - removes the previously registered class device 751236441fSMark M. Hoffman * 761beeffe4STony Jones * @dev: the class device to destroy 771236441fSMark M. Hoffman */ 781beeffe4STony Jones void hwmon_device_unregister(struct device *dev) 791236441fSMark M. Hoffman { 801236441fSMark M. Hoffman int id; 811236441fSMark M. Hoffman 82739cf3a2SKay Sievers if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { 831beeffe4STony Jones device_unregister(dev); 84ded2b666SMark M. Hoffman spin_lock(&idr_lock); 851236441fSMark M. Hoffman idr_remove(&hwmon_idr, id); 86ded2b666SMark M. Hoffman spin_unlock(&idr_lock); 871236441fSMark M. Hoffman } else 881beeffe4STony Jones dev_dbg(dev->parent, 891236441fSMark M. Hoffman "hwmon_device_unregister() failed: bad class ID!\n"); 901236441fSMark M. Hoffman } 911236441fSMark M. Hoffman 922958b1ecSJean Delvare static void __init hwmon_pci_quirks(void) 932958b1ecSJean Delvare { 942958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI 952958b1ecSJean Delvare struct pci_dev *sb; 962958b1ecSJean Delvare u16 base; 972958b1ecSJean Delvare u8 enable; 982958b1ecSJean Delvare 992958b1ecSJean Delvare /* Open access to 0x295-0x296 on MSI MS-7031 */ 1002958b1ecSJean Delvare sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); 1012958b1ecSJean Delvare if (sb && 1022958b1ecSJean Delvare (sb->subsystem_vendor == 0x1462 && /* MSI */ 1032958b1ecSJean Delvare sb->subsystem_device == 0x0031)) { /* MS-7031 */ 1042958b1ecSJean Delvare 1052958b1ecSJean Delvare pci_read_config_byte(sb, 0x48, &enable); 1062958b1ecSJean Delvare pci_read_config_word(sb, 0x64, &base); 1072958b1ecSJean Delvare 1082958b1ecSJean Delvare if (base == 0 && !(enable & BIT(2))) { 1092958b1ecSJean Delvare dev_info(&sb->dev, 1102958b1ecSJean Delvare "Opening wide generic port at 0x295\n"); 1112958b1ecSJean Delvare pci_write_config_word(sb, 0x64, 0x295); 1122958b1ecSJean Delvare pci_write_config_byte(sb, 0x48, enable | BIT(2)); 1132958b1ecSJean Delvare } 1142958b1ecSJean Delvare } 1152958b1ecSJean Delvare #endif 1162958b1ecSJean Delvare } 1172958b1ecSJean Delvare 1181236441fSMark M. Hoffman static int __init hwmon_init(void) 1191236441fSMark M. Hoffman { 1202958b1ecSJean Delvare hwmon_pci_quirks(); 1212958b1ecSJean Delvare 1221236441fSMark M. Hoffman hwmon_class = class_create(THIS_MODULE, "hwmon"); 1231236441fSMark M. Hoffman if (IS_ERR(hwmon_class)) { 124*c95df1aeSJoe Perches pr_err("couldn't create sysfs class\n"); 1251236441fSMark M. Hoffman return PTR_ERR(hwmon_class); 1261236441fSMark M. Hoffman } 1271236441fSMark M. Hoffman return 0; 1281236441fSMark M. Hoffman } 1291236441fSMark M. Hoffman 1301236441fSMark M. Hoffman static void __exit hwmon_exit(void) 1311236441fSMark M. Hoffman { 1321236441fSMark M. Hoffman class_destroy(hwmon_class); 1331236441fSMark M. Hoffman } 1341236441fSMark M. Hoffman 13537f54ee5SDavid Brownell subsys_initcall(hwmon_init); 1361236441fSMark M. Hoffman module_exit(hwmon_exit); 1371236441fSMark M. Hoffman 1381236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_register); 1391236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_unregister); 1401236441fSMark M. Hoffman 1411236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 1421236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); 1431236441fSMark M. Hoffman MODULE_LICENSE("GPL"); 1441236441fSMark M. Hoffman 145