xref: /linux/drivers/hwmon/hwmon.c (revision 2871f55237e2aaa00fd724ce45b47e567974045f)
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>
211236441fSMark M. Hoffman 
221236441fSMark M. Hoffman #define HWMON_ID_PREFIX "hwmon"
231236441fSMark M. Hoffman #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
241236441fSMark M. Hoffman 
251236441fSMark M. Hoffman static struct class *hwmon_class;
261236441fSMark M. Hoffman 
271236441fSMark M. Hoffman static DEFINE_IDR(hwmon_idr);
28ded2b666SMark M. Hoffman static DEFINE_SPINLOCK(idr_lock);
291236441fSMark M. Hoffman 
301236441fSMark M. Hoffman /**
311beeffe4STony Jones  * hwmon_device_register - register w/ hwmon
321236441fSMark M. Hoffman  * @dev: the device to register
331236441fSMark M. Hoffman  *
341beeffe4STony Jones  * hwmon_device_unregister() must be called when the device is no
351236441fSMark M. Hoffman  * longer needed.
361236441fSMark M. Hoffman  *
371beeffe4STony Jones  * Returns the pointer to the new device.
381236441fSMark M. Hoffman  */
391beeffe4STony Jones struct device *hwmon_device_register(struct device *dev)
401236441fSMark M. Hoffman {
411beeffe4STony Jones 	struct device *hwdev;
42ded2b666SMark M. Hoffman 	int id, err;
431236441fSMark M. Hoffman 
44ded2b666SMark M. Hoffman again:
45ded2b666SMark M. Hoffman 	if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0))
461236441fSMark M. Hoffman 		return ERR_PTR(-ENOMEM);
471236441fSMark M. Hoffman 
48ded2b666SMark M. Hoffman 	spin_lock(&idr_lock);
49ded2b666SMark M. Hoffman 	err = idr_get_new(&hwmon_idr, NULL, &id);
50ded2b666SMark M. Hoffman 	spin_unlock(&idr_lock);
51ded2b666SMark M. Hoffman 
52ded2b666SMark M. Hoffman 	if (unlikely(err == -EAGAIN))
53ded2b666SMark M. Hoffman 		goto again;
54ded2b666SMark M. Hoffman 	else if (unlikely(err))
55ded2b666SMark M. Hoffman 		return ERR_PTR(err);
561236441fSMark M. Hoffman 
571236441fSMark M. Hoffman 	id = id & MAX_ID_MASK;
58*2871f552SGreg Kroah-Hartman 	hwdev = device_create_drvdata(hwmon_class, dev, MKDEV(0, 0), NULL,
59*2871f552SGreg Kroah-Hartman 				      HWMON_ID_FORMAT, id);
601236441fSMark M. Hoffman 
611beeffe4STony Jones 	if (IS_ERR(hwdev)) {
62ded2b666SMark M. Hoffman 		spin_lock(&idr_lock);
631236441fSMark M. Hoffman 		idr_remove(&hwmon_idr, id);
64ded2b666SMark M. Hoffman 		spin_unlock(&idr_lock);
65ded2b666SMark M. Hoffman 	}
661236441fSMark M. Hoffman 
671beeffe4STony Jones 	return hwdev;
681236441fSMark M. Hoffman }
691236441fSMark M. Hoffman 
701236441fSMark M. Hoffman /**
711236441fSMark M. Hoffman  * hwmon_device_unregister - removes the previously registered class device
721236441fSMark M. Hoffman  *
731beeffe4STony Jones  * @dev: the class device to destroy
741236441fSMark M. Hoffman  */
751beeffe4STony Jones void hwmon_device_unregister(struct device *dev)
761236441fSMark M. Hoffman {
771236441fSMark M. Hoffman 	int id;
781236441fSMark M. Hoffman 
791beeffe4STony Jones 	if (likely(sscanf(dev->bus_id, HWMON_ID_FORMAT, &id) == 1)) {
801beeffe4STony Jones 		device_unregister(dev);
81ded2b666SMark M. Hoffman 		spin_lock(&idr_lock);
821236441fSMark M. Hoffman 		idr_remove(&hwmon_idr, id);
83ded2b666SMark M. Hoffman 		spin_unlock(&idr_lock);
841236441fSMark M. Hoffman 	} else
851beeffe4STony Jones 		dev_dbg(dev->parent,
861236441fSMark M. Hoffman 			"hwmon_device_unregister() failed: bad class ID!\n");
871236441fSMark M. Hoffman }
881236441fSMark M. Hoffman 
891236441fSMark M. Hoffman static int __init hwmon_init(void)
901236441fSMark M. Hoffman {
911236441fSMark M. Hoffman 	hwmon_class = class_create(THIS_MODULE, "hwmon");
921236441fSMark M. Hoffman 	if (IS_ERR(hwmon_class)) {
931236441fSMark M. Hoffman 		printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n");
941236441fSMark M. Hoffman 		return PTR_ERR(hwmon_class);
951236441fSMark M. Hoffman 	}
961236441fSMark M. Hoffman 	return 0;
971236441fSMark M. Hoffman }
981236441fSMark M. Hoffman 
991236441fSMark M. Hoffman static void __exit hwmon_exit(void)
1001236441fSMark M. Hoffman {
1011236441fSMark M. Hoffman 	class_destroy(hwmon_class);
1021236441fSMark M. Hoffman }
1031236441fSMark M. Hoffman 
10437f54ee5SDavid Brownell subsys_initcall(hwmon_init);
1051236441fSMark M. Hoffman module_exit(hwmon_exit);
1061236441fSMark M. Hoffman 
1071236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_register);
1081236441fSMark M. Hoffman EXPORT_SYMBOL_GPL(hwmon_device_unregister);
1091236441fSMark M. Hoffman 
1101236441fSMark M. Hoffman MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
1111236441fSMark M. Hoffman MODULE_DESCRIPTION("hardware monitoring sysfs/class support");
1121236441fSMark M. Hoffman MODULE_LICENSE("GPL");
1131236441fSMark M. Hoffman 
114