2008-08-07 00:41:05 +04:00
/*
* An hwmon driver for the Analog Devices AD7414
*
* Copyright 2006 Stefan Roese < sr at denx . de > , DENX Software Engineering
*
* Copyright ( c ) 2008 PIKA Technologies
* Sean MacLennan < smaclennan @ pikatech . com >
*
* Copyright ( c ) 2008 Spansion Inc .
* Frank Edelhaeuser < frank . edelhaeuser at spansion . com >
* ( converted to " new style " I2C driver model , removed checkpatch . pl warnings )
*
* Based on ad7418 . c
* Copyright 2006 Tower Technologies , Alessandro Zummo < a . zummo at towertech . it >
*
* 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 ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/jiffies.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/sysfs.h>
/* AD7414 registers */
# define AD7414_REG_TEMP 0x00
# define AD7414_REG_CONF 0x01
# define AD7414_REG_T_HIGH 0x02
# define AD7414_REG_T_LOW 0x03
static u8 AD7414_REG_LIMIT [ ] = { AD7414_REG_T_HIGH , AD7414_REG_T_LOW } ;
struct ad7414_data {
struct device * hwmon_dev ;
struct mutex lock ; /* atomic read data updates */
char valid ; /* !=0 if following fields are valid */
unsigned long next_update ; /* In jiffies */
s16 temp_input ; /* Register values */
s8 temps [ ARRAY_SIZE ( AD7414_REG_LIMIT ) ] ;
} ;
/* REG: (0.25C/bit, two's complement) << 6 */
static inline int ad7414_temp_from_reg ( s16 reg )
{
/* use integer division instead of equivalent right shift to
* guarantee arithmetic shift and preserve the sign
*/
return ( ( int ) reg / 64 ) * 250 ;
}
static inline int ad7414_read ( struct i2c_client * client , u8 reg )
{
if ( reg = = AD7414_REG_TEMP ) {
int value = i2c_smbus_read_word_data ( client , reg ) ;
return ( value < 0 ) ? value : swab16 ( value ) ;
} else
return i2c_smbus_read_byte_data ( client , reg ) ;
}
static inline int ad7414_write ( struct i2c_client * client , u8 reg , u8 value )
{
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
2008-09-20 12:25:20 +04:00
static struct ad7414_data * ad7414_update_device ( struct device * dev )
2008-08-07 00:41:05 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct ad7414_data * data = i2c_get_clientdata ( client ) ;
mutex_lock ( & data - > lock ) ;
if ( time_after ( jiffies , data - > next_update ) | | ! data - > valid ) {
int value , i ;
dev_dbg ( & client - > dev , " starting ad7414 update \n " ) ;
value = ad7414_read ( client , AD7414_REG_TEMP ) ;
if ( value < 0 )
dev_dbg ( & client - > dev , " AD7414_REG_TEMP err %d \n " ,
value ) ;
else
data - > temp_input = value ;
for ( i = 0 ; i < ARRAY_SIZE ( AD7414_REG_LIMIT ) ; + + i ) {
value = ad7414_read ( client , AD7414_REG_LIMIT [ i ] ) ;
if ( value < 0 )
dev_dbg ( & client - > dev , " AD7414 reg %d err %d \n " ,
AD7414_REG_LIMIT [ i ] , value ) ;
else
data - > temps [ i ] = value ;
}
data - > next_update = jiffies + HZ + HZ / 2 ;
data - > valid = 1 ;
}
mutex_unlock ( & data - > lock ) ;
return data ;
}
static ssize_t show_temp_input ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct ad7414_data * data = ad7414_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , ad7414_temp_from_reg ( data - > temp_input ) ) ;
}
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp_input , NULL , 0 ) ;
static ssize_t show_max_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int index = to_sensor_dev_attr ( attr ) - > index ;
struct ad7414_data * data = ad7414_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , data - > temps [ index ] * 1000 ) ;
}
static ssize_t set_max_min ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct ad7414_data * data = i2c_get_clientdata ( client ) ;
int index = to_sensor_dev_attr ( attr ) - > index ;
u8 reg = AD7414_REG_LIMIT [ index ] ;
long temp = simple_strtol ( buf , NULL , 10 ) ;
temp = SENSORS_LIMIT ( temp , - 40000 , 85000 ) ;
temp = ( temp + ( temp < 0 ? - 500 : 500 ) ) / 1000 ;
mutex_lock ( & data - > lock ) ;
data - > temps [ index ] = temp ;
ad7414_write ( client , reg , temp ) ;
mutex_unlock ( & data - > lock ) ;
return count ;
}
static SENSOR_DEVICE_ATTR ( temp1_max , S_IWUSR | S_IRUGO ,
show_max_min , set_max_min , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_min , S_IWUSR | S_IRUGO ,
show_max_min , set_max_min , 1 ) ;
static ssize_t show_alarm ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
int bitnr = to_sensor_dev_attr ( attr ) - > index ;
struct ad7414_data * data = ad7414_update_device ( dev ) ;
int value = ( data - > temp_input > > bitnr ) & 1 ;
return sprintf ( buf , " %d \n " , value ) ;
}
static SENSOR_DEVICE_ATTR ( temp1_min_alarm , S_IRUGO , show_alarm , NULL , 3 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_alarm , S_IRUGO , show_alarm , NULL , 4 ) ;
static struct attribute * ad7414_attributes [ ] = {
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_min_alarm . dev_attr . attr ,
NULL
} ;
static const struct attribute_group ad7414_group = {
. attrs = ad7414_attributes ,
} ;
static int ad7414_probe ( struct i2c_client * client ,
const struct i2c_device_id * dev_id )
{
struct ad7414_data * data ;
int conf ;
int err = 0 ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_READ_WORD_DATA ) )
goto exit ;
data = kzalloc ( sizeof ( struct ad7414_data ) , GFP_KERNEL ) ;
if ( ! data ) {
err = - ENOMEM ;
goto exit ;
}
i2c_set_clientdata ( client , data ) ;
mutex_init ( & data - > lock ) ;
dev_info ( & client - > dev , " chip found \n " ) ;
/* Make sure the chip is powered up. */
conf = i2c_smbus_read_byte_data ( client , AD7414_REG_CONF ) ;
if ( conf < 0 )
dev_warn ( & client - > dev ,
" ad7414_probe unable to read config register. \n " ) ;
else {
conf & = ~ ( 1 < < 7 ) ;
i2c_smbus_write_byte_data ( client , AD7414_REG_CONF , conf ) ;
}
/* Register sysfs hooks */
err = sysfs_create_group ( & client - > dev . kobj , & ad7414_group ) ;
if ( err )
goto exit_free ;
data - > hwmon_dev = hwmon_device_register ( & client - > dev ) ;
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
goto exit_remove ;
}
return 0 ;
exit_remove :
sysfs_remove_group ( & client - > dev . kobj , & ad7414_group ) ;
exit_free :
kfree ( data ) ;
exit :
return err ;
}
static int __devexit ad7414_remove ( struct i2c_client * client )
{
struct ad7414_data * data = i2c_get_clientdata ( client ) ;
hwmon_device_unregister ( data - > hwmon_dev ) ;
sysfs_remove_group ( & client - > dev . kobj , & ad7414_group ) ;
kfree ( data ) ;
return 0 ;
}
static const struct i2c_device_id ad7414_id [ ] = {
{ " ad7414 " , 0 } ,
{ }
} ;
static struct i2c_driver ad7414_driver = {
. driver = {
. name = " ad7414 " ,
} ,
. probe = ad7414_probe ,
. remove = __devexit_p ( ad7414_remove ) ,
. id_table = ad7414_id ,
} ;
static int __init ad7414_init ( void )
{
return i2c_add_driver ( & ad7414_driver ) ;
}
module_init ( ad7414_init ) ;
static void __exit ad7414_exit ( void )
{
i2c_del_driver ( & ad7414_driver ) ;
}
module_exit ( ad7414_exit ) ;
MODULE_AUTHOR ( " Stefan Roese <sr at denx.de>, "
" Frank Edelhaeuser <frank.edelhaeuser at spansion.com> " ) ;
MODULE_DESCRIPTION ( " AD7414 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;