2005-07-15 21:38:08 -04:00
/*
2012-01-19 11:02:18 -08:00
* hwmon . c - part of lm_sensors , Linux kernel modules for hardware monitoring
*
* This file defines the sysfs class " hwmon " , for use by sensors drivers .
*
* Copyright ( C ) 2005 Mark M . Hoffman < mhoffman @ lightlink . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*/
2005-07-15 21:38:08 -04:00
2010-10-20 06:51:37 +00:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-07-15 21:38:08 -04:00
# include <linux/module.h>
# include <linux/device.h>
# include <linux/err.h>
2013-07-06 13:57:23 -07:00
# include <linux/slab.h>
2005-07-15 21:38:08 -04:00
# include <linux/kdev_t.h>
# include <linux/idr.h>
# include <linux/hwmon.h>
2005-11-07 00:59:43 -08:00
# include <linux/gfp.h>
2006-03-05 23:13:47 +01:00
# include <linux/spinlock.h>
2009-06-15 18:39:50 +02:00
# include <linux/pci.h>
2005-07-15 21:38:08 -04:00
# define HWMON_ID_PREFIX "hwmon"
# define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
2013-07-06 13:57:23 -07:00
struct hwmon_device {
const char * name ;
struct device dev ;
} ;
# define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
static ssize_t
show_name ( struct device * dev , struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " %s \n " , to_hwmon_device ( dev ) - > name ) ;
}
static DEVICE_ATTR ( name , S_IRUGO , show_name , NULL ) ;
static struct attribute * hwmon_dev_attrs [ ] = {
& dev_attr_name . attr ,
NULL
} ;
static umode_t hwmon_dev_name_is_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
if ( to_hwmon_device ( dev ) - > name = = NULL )
return 0 ;
return attr - > mode ;
}
static struct attribute_group hwmon_dev_attr_group = {
. attrs = hwmon_dev_attrs ,
. is_visible = hwmon_dev_name_is_visible ,
} ;
static const struct attribute_group * hwmon_dev_attr_groups [ ] = {
& hwmon_dev_attr_group ,
NULL
} ;
static void hwmon_dev_release ( struct device * dev )
{
kfree ( to_hwmon_device ( dev ) ) ;
}
static struct class hwmon_class = {
. name = " hwmon " ,
. owner = THIS_MODULE ,
. dev_groups = hwmon_dev_attr_groups ,
. dev_release = hwmon_dev_release ,
} ;
2005-07-15 21:38:08 -04:00
2011-10-31 17:10:09 -07:00
static DEFINE_IDA ( hwmon_ida ) ;
2005-07-15 21:38:08 -04:00
/**
2013-07-06 13:57:23 -07:00
* hwmon_device_register_with_groups - register w / hwmon
* @ dev : the parent device
* @ name : hwmon name attribute
* @ drvdata : driver data to attach to created device
* @ groups : List of attribute groups to create
2005-07-15 21:38:08 -04:00
*
2007-08-20 13:46:20 -07:00
* hwmon_device_unregister ( ) must be called when the device is no
2005-07-15 21:38:08 -04:00
* longer needed .
*
2007-08-20 13:46:20 -07:00
* Returns the pointer to the new device .
2005-07-15 21:38:08 -04:00
*/
2013-07-06 13:57:23 -07:00
struct device *
hwmon_device_register_with_groups ( struct device * dev , const char * name ,
void * drvdata ,
const struct attribute_group * * groups )
2005-07-15 21:38:08 -04:00
{
2013-07-06 13:57:23 -07:00
struct hwmon_device * hwdev ;
int err , id ;
2006-03-05 23:13:47 +01:00
2011-10-31 17:10:09 -07:00
id = ida_simple_get ( & hwmon_ida , 0 , 0 , GFP_KERNEL ) ;
if ( id < 0 )
return ERR_PTR ( id ) ;
2005-07-15 21:38:08 -04:00
2013-07-06 13:57:23 -07:00
hwdev = kzalloc ( sizeof ( * hwdev ) , GFP_KERNEL ) ;
if ( hwdev = = NULL ) {
err = - ENOMEM ;
goto ida_remove ;
}
2005-07-15 21:38:08 -04:00
2013-07-06 13:57:23 -07:00
hwdev - > name = name ;
hwdev - > dev . class = & hwmon_class ;
hwdev - > dev . parent = dev ;
hwdev - > dev . groups = groups ;
hwdev - > dev . of_node = dev ? dev - > of_node : NULL ;
dev_set_drvdata ( & hwdev - > dev , drvdata ) ;
dev_set_name ( & hwdev - > dev , HWMON_ID_FORMAT , id ) ;
err = device_register ( & hwdev - > dev ) ;
if ( err )
goto free ;
return & hwdev - > dev ;
free :
kfree ( hwdev ) ;
ida_remove :
ida_simple_remove ( & hwmon_ida , id ) ;
return ERR_PTR ( err ) ;
}
EXPORT_SYMBOL_GPL ( hwmon_device_register_with_groups ) ;
2005-07-15 21:38:08 -04:00
2013-07-06 13:57:23 -07:00
/**
* hwmon_device_register - register w / hwmon
* @ dev : the device to register
*
* hwmon_device_unregister ( ) must be called when the device is no
* longer needed .
*
* Returns the pointer to the new device .
*/
struct device * hwmon_device_register ( struct device * dev )
{
return hwmon_device_register_with_groups ( dev , NULL , NULL , NULL ) ;
2005-07-15 21:38:08 -04:00
}
2012-01-08 19:34:14 +01:00
EXPORT_SYMBOL_GPL ( hwmon_device_register ) ;
2005-07-15 21:38:08 -04:00
/**
* hwmon_device_unregister - removes the previously registered class device
*
2007-08-20 13:46:20 -07:00
* @ dev : the class device to destroy
2005-07-15 21:38:08 -04:00
*/
2007-08-20 13:46:20 -07:00
void hwmon_device_unregister ( struct device * dev )
2005-07-15 21:38:08 -04:00
{
int id ;
2009-01-06 10:44:41 -08:00
if ( likely ( sscanf ( dev_name ( dev ) , HWMON_ID_FORMAT , & id ) = = 1 ) ) {
2007-08-20 13:46:20 -07:00
device_unregister ( dev ) ;
2011-10-31 17:10:09 -07:00
ida_simple_remove ( & hwmon_ida , id ) ;
2005-07-15 21:38:08 -04:00
} else
2007-08-20 13:46:20 -07:00
dev_dbg ( dev - > parent ,
2005-07-15 21:38:08 -04:00
" hwmon_device_unregister() failed: bad class ID! \n " ) ;
}
2012-01-08 19:34:14 +01:00
EXPORT_SYMBOL_GPL ( hwmon_device_unregister ) ;
2005-07-15 21:38:08 -04:00
2013-07-11 20:00:12 -07:00
static void devm_hwmon_release ( struct device * dev , void * res )
{
struct device * hwdev = * ( struct device * * ) res ;
hwmon_device_unregister ( hwdev ) ;
}
/**
* devm_hwmon_device_register_with_groups - register w / hwmon
* @ dev : the parent device
* @ name : hwmon name attribute
* @ drvdata : driver data to attach to created device
* @ groups : List of attribute groups to create
*
* Returns the pointer to the new device . The new device is automatically
* unregistered with the parent device .
*/
struct device *
devm_hwmon_device_register_with_groups ( struct device * dev , const char * name ,
void * drvdata ,
const struct attribute_group * * groups )
{
struct device * * ptr , * hwdev ;
if ( ! dev )
return ERR_PTR ( - EINVAL ) ;
ptr = devres_alloc ( devm_hwmon_release , sizeof ( * ptr ) , GFP_KERNEL ) ;
if ( ! ptr )
return ERR_PTR ( - ENOMEM ) ;
hwdev = hwmon_device_register_with_groups ( dev , name , drvdata , groups ) ;
if ( IS_ERR ( hwdev ) )
goto error ;
* ptr = hwdev ;
devres_add ( dev , ptr ) ;
return hwdev ;
error :
devres_free ( ptr ) ;
return hwdev ;
}
EXPORT_SYMBOL_GPL ( devm_hwmon_device_register_with_groups ) ;
static int devm_hwmon_match ( struct device * dev , void * res , void * data )
{
struct device * * hwdev = res ;
return * hwdev = = data ;
}
/**
* devm_hwmon_device_unregister - removes a previously registered hwmon device
*
* @ dev : the parent device of the device to unregister
*/
void devm_hwmon_device_unregister ( struct device * dev )
{
WARN_ON ( devres_release ( dev , devm_hwmon_release , devm_hwmon_match , dev ) ) ;
}
EXPORT_SYMBOL_GPL ( devm_hwmon_device_unregister ) ;
2009-06-15 18:39:50 +02:00
static void __init hwmon_pci_quirks ( void )
{
# if defined CONFIG_X86 && defined CONFIG_PCI
struct pci_dev * sb ;
u16 base ;
u8 enable ;
/* Open access to 0x295-0x296 on MSI MS-7031 */
sb = pci_get_device ( PCI_VENDOR_ID_ATI , 0x436c , NULL ) ;
2012-12-19 22:16:59 +01:00
if ( sb ) {
if ( sb - > subsystem_vendor = = 0x1462 & & /* MSI */
sb - > subsystem_device = = 0x0031 ) { /* MS-7031 */
pci_read_config_byte ( sb , 0x48 , & enable ) ;
pci_read_config_word ( sb , 0x64 , & base ) ;
if ( base = = 0 & & ! ( enable & BIT ( 2 ) ) ) {
dev_info ( & sb - > dev ,
" Opening wide generic port at 0x295 \n " ) ;
pci_write_config_word ( sb , 0x64 , 0x295 ) ;
pci_write_config_byte ( sb , 0x48 ,
enable | BIT ( 2 ) ) ;
}
2009-06-15 18:39:50 +02:00
}
2012-12-19 22:16:59 +01:00
pci_dev_put ( sb ) ;
2009-06-15 18:39:50 +02:00
}
# endif
}
2005-07-15 21:38:08 -04:00
static int __init hwmon_init ( void )
{
2013-07-06 13:57:23 -07:00
int err ;
2009-06-15 18:39:50 +02:00
hwmon_pci_quirks ( ) ;
2013-07-06 13:57:23 -07:00
err = class_register ( & hwmon_class ) ;
if ( err ) {
pr_err ( " couldn't register hwmon sysfs class \n " ) ;
return err ;
2005-07-15 21:38:08 -04:00
}
return 0 ;
}
static void __exit hwmon_exit ( void )
{
2013-07-06 13:57:23 -07:00
class_unregister ( & hwmon_class ) ;
2005-07-15 21:38:08 -04:00
}
2007-02-14 21:15:04 +01:00
subsys_initcall ( hwmon_init ) ;
2005-07-15 21:38:08 -04:00
module_exit ( hwmon_exit ) ;
MODULE_AUTHOR ( " Mark M. Hoffman <mhoffman@lightlink.com> " ) ;
MODULE_DESCRIPTION ( " hardware monitoring sysfs/class support " ) ;
MODULE_LICENSE ( " GPL " ) ;