xref: /linux/drivers/hwmon/hwmon.c (revision 2958b1ec6be1d71105d67d70de9d7d70f5e97151)
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