xref: /linux/drivers/hwmon/hwmon.c (revision 4ca5f468cc2a0be1cba585f335dcbe56b40944f2)
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 
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>
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 
30*4ca5f468SJonathan Cameron static DEFINE_IDA(hwmon_ida);
311236441fSMark M. Hoffman 
321236441fSMark M. Hoffman /**
331beeffe4STony Jones  * hwmon_device_register - register w/ hwmon
341236441fSMark M. Hoffman  * @dev: the device to register
351236441fSMark M. Hoffman  *
361beeffe4STony Jones  * hwmon_device_unregister() must be called when the device is no
371236441fSMark M. Hoffman  * longer needed.
381236441fSMark M. Hoffman  *
391beeffe4STony Jones  * Returns the pointer to the new device.
401236441fSMark M. Hoffman  */
411beeffe4STony Jones struct device *hwmon_device_register(struct device *dev)
421236441fSMark M. Hoffman {
431beeffe4STony Jones 	struct device *hwdev;
44*4ca5f468SJonathan Cameron 	int id;
451236441fSMark M. Hoffman 
46*4ca5f468SJonathan Cameron 	id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
47*4ca5f468SJonathan Cameron 	if (id < 0)
48*4ca5f468SJonathan Cameron 		return ERR_PTR(id);
491236441fSMark M. Hoffman 
50a9b12619SGreg Kroah-Hartman 	hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL,
512871f552SGreg Kroah-Hartman 			      HWMON_ID_FORMAT, id);
521236441fSMark M. Hoffman 
53*4ca5f468SJonathan Cameron 	if (IS_ERR(hwdev))
54*4ca5f468SJonathan Cameron 		ida_simple_remove(&hwmon_ida, id);
551236441fSMark M. Hoffman 
561beeffe4STony Jones 	return hwdev;
571236441fSMark M. Hoffman }
581236441fSMark M. Hoffman 
591236441fSMark M. Hoffman /**
601236441fSMark M. Hoffman  * hwmon_device_unregister - removes the previously registered class device
611236441fSMark M. Hoffman  *
621beeffe4STony Jones  * @dev: the class device to destroy
631236441fSMark M. Hoffman  */
641beeffe4STony Jones void hwmon_device_unregister(struct device *dev)
651236441fSMark M. Hoffman {
661236441fSMark M. Hoffman 	int id;
671236441fSMark M. Hoffman 
68739cf3a2SKay Sievers 	if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
691beeffe4STony Jones 		device_unregister(dev);
70*4ca5f468SJonathan Cameron 		ida_simple_remove(&hwmon_ida, id);
711236441fSMark M. Hoffman 	} else
721beeffe4STony Jones 		dev_dbg(dev->parent,
731236441fSMark M. Hoffman 			"hwmon_device_unregister() failed: bad class ID!\n");
741236441fSMark M. Hoffman }
751236441fSMark M. Hoffman 
762958b1ecSJean Delvare static void __init hwmon_pci_quirks(void)
772958b1ecSJean Delvare {
782958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI
792958b1ecSJean Delvare 	struct pci_dev *sb;
802958b1ecSJean Delvare 	u16 base;
812958b1ecSJean Delvare 	u8 enable;
822958b1ecSJean Delvare 
832958b1ecSJean Delvare 	/* Open access to 0x295-0x296 on MSI MS-7031 */
842958b1ecSJean Delvare 	sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
852958b1ecSJean Delvare 	if (sb &&
862958b1ecSJean Delvare 	    (sb->subsystem_vendor == 0x1462 &&	/* MSI */
872958b1ecSJean Delvare 	     sb->subsystem_device == 0x0031)) {	/* MS-7031 */
882958b1ecSJean Delvare 
892958b1ecSJean Delvare 		pci_read_config_byte(sb, 0x48, &enable);
902958b1ecSJean Delvare 		pci_read_config_word(sb, 0x64, &base);
912958b1ecSJean Delvare 
922958b1ecSJean Delvare 		if (base == 0 && !(enable & BIT(2))) {
932958b1ecSJean Delvare 			dev_info(&sb->dev,
942958b1ecSJean Delvare 				 "Opening wide generic port at 0x295\n");
952958b1ecSJean Delvare 			pci_write_config_word(sb, 0x64, 0x295);
962958b1ecSJean Delvare 			pci_write_config_byte(sb, 0x48, enable | BIT(2));
972958b1ecSJean Delvare 		}
982958b1ecSJean Delvare 	}
992958b1ecSJean Delvare #endif
1002958b1ecSJean Delvare }
1012958b1ecSJean Delvare 
1021236441fSMark M. Hoffman static int __init hwmon_init(void)
1031236441fSMark M. Hoffman {
1042958b1ecSJean Delvare 	hwmon_pci_quirks();
1052958b1ecSJean Delvare 
1061236441fSMark M. Hoffman 	hwmon_class = class_create(THIS_MODULE, "hwmon");
1071236441fSMark M. Hoffman 	if (IS_ERR(hwmon_class)) {
108c95df1aeSJoe Perches 		pr_err("couldn't create sysfs class\n");
1091236441fSMark M. Hoffman 		return PTR_ERR(hwmon_class);
1101236441fSMark M. Hoffman 	}
1111236441fSMark M. Hoffman 	return 0;
1121236441fSMark M. Hoffman }
1131236441fSMark M. Hoffman 
1141236441fSMark M. Hoffman static void __exit hwmon_exit(void)
1151236441fSMark M. Hoffman {
1161236441fSMark M. Hoffman 	class_destroy(hwmon_class);
1171236441fSMark M. Hoffman }
1181236441fSMark M. Hoffman 
11937f54ee5SDavid Brownell subsys_initcall(hwmon_init);
1201236441fSMark M. Hoffman module_exit(hwmon_exit);
1211236441fSMark M. Hoffman 
1221236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_register);
1231236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_unregister);
1241236441fSMark M. Hoffman 
1251236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
1261236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support");
1271236441fSMark M. Hoffman MODULE_LICENSE("GPL");
1281236441fSMark M. Hoffman 
129