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