xref: /linux/drivers/hwmon/hwmon.c (revision 839a9eefc918345ae7a7e8c6f583e2e653646d4d)
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 
304ca5f468SJonathan 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;
444ca5f468SJonathan Cameron 	int id;
451236441fSMark M. Hoffman 
464ca5f468SJonathan Cameron 	id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
474ca5f468SJonathan Cameron 	if (id < 0)
484ca5f468SJonathan 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 
534ca5f468SJonathan Cameron 	if (IS_ERR(hwdev))
544ca5f468SJonathan Cameron 		ida_simple_remove(&hwmon_ida, id);
551236441fSMark M. Hoffman 
561beeffe4STony Jones 	return hwdev;
571236441fSMark M. Hoffman }
58*839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_register);
591236441fSMark M. Hoffman 
601236441fSMark M. Hoffman /**
611236441fSMark M. Hoffman  * hwmon_device_unregister - removes the previously registered class device
621236441fSMark M. Hoffman  *
631beeffe4STony Jones  * @dev: the class device to destroy
641236441fSMark M. Hoffman  */
651beeffe4STony Jones void hwmon_device_unregister(struct device *dev)
661236441fSMark M. Hoffman {
671236441fSMark M. Hoffman 	int id;
681236441fSMark M. Hoffman 
69739cf3a2SKay Sievers 	if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
701beeffe4STony Jones 		device_unregister(dev);
714ca5f468SJonathan Cameron 		ida_simple_remove(&hwmon_ida, id);
721236441fSMark M. Hoffman 	} else
731beeffe4STony Jones 		dev_dbg(dev->parent,
741236441fSMark M. Hoffman 			"hwmon_device_unregister() failed: bad class ID!\n");
751236441fSMark M. Hoffman }
76*839a9eefSFrans Meulenbroeks EXPORT_SYMBOL_GPL(hwmon_device_unregister);
771236441fSMark M. Hoffman 
782958b1ecSJean Delvare static void __init hwmon_pci_quirks(void)
792958b1ecSJean Delvare {
802958b1ecSJean Delvare #if defined CONFIG_X86 && defined CONFIG_PCI
812958b1ecSJean Delvare 	struct pci_dev *sb;
822958b1ecSJean Delvare 	u16 base;
832958b1ecSJean Delvare 	u8 enable;
842958b1ecSJean Delvare 
852958b1ecSJean Delvare 	/* Open access to 0x295-0x296 on MSI MS-7031 */
862958b1ecSJean Delvare 	sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
872958b1ecSJean Delvare 	if (sb &&
882958b1ecSJean Delvare 	    (sb->subsystem_vendor == 0x1462 &&	/* MSI */
892958b1ecSJean Delvare 	     sb->subsystem_device == 0x0031)) {	/* MS-7031 */
902958b1ecSJean Delvare 
912958b1ecSJean Delvare 		pci_read_config_byte(sb, 0x48, &enable);
922958b1ecSJean Delvare 		pci_read_config_word(sb, 0x64, &base);
932958b1ecSJean Delvare 
942958b1ecSJean Delvare 		if (base == 0 && !(enable & BIT(2))) {
952958b1ecSJean Delvare 			dev_info(&sb->dev,
962958b1ecSJean Delvare 				 "Opening wide generic port at 0x295\n");
972958b1ecSJean Delvare 			pci_write_config_word(sb, 0x64, 0x295);
982958b1ecSJean Delvare 			pci_write_config_byte(sb, 0x48, enable | BIT(2));
992958b1ecSJean Delvare 		}
1002958b1ecSJean Delvare 	}
1012958b1ecSJean Delvare #endif
1022958b1ecSJean Delvare }
1032958b1ecSJean Delvare 
1041236441fSMark M. Hoffman static int __init hwmon_init(void)
1051236441fSMark M. Hoffman {
1062958b1ecSJean Delvare 	hwmon_pci_quirks();
1072958b1ecSJean Delvare 
1081236441fSMark M. Hoffman 	hwmon_class = class_create(THIS_MODULE, "hwmon");
1091236441fSMark M. Hoffman 	if (IS_ERR(hwmon_class)) {
110c95df1aeSJoe Perches 		pr_err("couldn't create sysfs class\n");
1111236441fSMark M. Hoffman 		return PTR_ERR(hwmon_class);
1121236441fSMark M. Hoffman 	}
1131236441fSMark M. Hoffman 	return 0;
1141236441fSMark M. Hoffman }
1151236441fSMark M. Hoffman 
1161236441fSMark M. Hoffman static void __exit hwmon_exit(void)
1171236441fSMark M. Hoffman {
1181236441fSMark M. Hoffman 	class_destroy(hwmon_class);
1191236441fSMark M. Hoffman }
1201236441fSMark M. Hoffman 
12137f54ee5SDavid Brownell subsys_initcall(hwmon_init);
1221236441fSMark M. Hoffman module_exit(hwmon_exit);
1231236441fSMark M. Hoffman 
1241236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
1251236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support");
1261236441fSMark M. Hoffman MODULE_LICENSE("GPL");
1271236441fSMark M. Hoffman 
128