2005-04-17 02:20:36 +04:00
/*
* w83l785ts . c - Part of lm_sensors , Linux kernel modules for hardware
* monitoring
2009-12-09 22:35:56 +03:00
* Copyright ( C ) 2003 - 2009 Jean Delvare < khali @ linux - fr . org >
2005-04-17 02:20:36 +04:00
*
* Inspired from the lm83 driver . The W83L785TS - S is a sensor chip made
* by Winbond . It reports a single external temperature with a 1 deg
* resolution and a 3 deg accuracy . Datasheet can be obtained from
* Winbond ' s website at :
* http : //www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
*
* Ported to Linux 2.6 by Wolfgang Ziegler < nuppla @ gmx . at > and Jean Delvare
* < khali @ linux - fr . org > .
*
* Thanks to James Bolt < james @ evilpenguin . com > for benchmarking the read
* error handling mechanism .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/jiffies.h>
# include <linux/i2c.h>
2005-07-16 05:39:18 +04:00
# include <linux/hwmon.h>
2005-09-25 18:41:18 +04:00
# include <linux/hwmon-sysfs.h>
2005-07-16 05:39:18 +04:00
# include <linux/err.h>
2006-01-19 01:19:26 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
/* How many retries on register read error */
# define MAX_RETRIES 5
/*
* Address to scan
* Address is fully defined internally and cannot be changed .
*/
2008-02-18 06:28:03 +03:00
static const unsigned short normal_i2c [ ] = { 0x2e , I2C_CLIENT_END } ;
2005-04-17 02:20:36 +04:00
/*
* The W83L785TS - S registers
* Manufacturer ID is 0x5CA3 for Winbond .
*/
# define W83L785TS_REG_MAN_ID1 0x4D
# define W83L785TS_REG_MAN_ID2 0x4C
# define W83L785TS_REG_CHIP_ID 0x4E
# define W83L785TS_REG_CONFIG 0x40
# define W83L785TS_REG_TYPE 0x52
# define W83L785TS_REG_TEMP 0x27
# define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */
/*
* Conversions
* The W83L785TS - S uses signed 8 - bit values .
*/
2005-09-25 18:45:03 +04:00
# define TEMP_FROM_REG(val) ((val) * 1000)
2005-04-17 02:20:36 +04:00
/*
* Functions declaration
*/
2008-07-16 21:30:18 +04:00
static int w83l785ts_probe ( struct i2c_client * client ,
const struct i2c_device_id * id ) ;
2009-12-14 23:17:23 +03:00
static int w83l785ts_detect ( struct i2c_client * client ,
2008-07-16 21:30:18 +04:00
struct i2c_board_info * info ) ;
static int w83l785ts_remove ( struct i2c_client * client ) ;
2005-04-17 02:20:36 +04:00
static u8 w83l785ts_read_value ( struct i2c_client * client , u8 reg , u8 defval ) ;
static struct w83l785ts_data * w83l785ts_update_device ( struct device * dev ) ;
/*
* Driver data ( common to all clients )
*/
2012-01-08 22:34:16 +04:00
2008-07-16 21:30:18 +04:00
static const struct i2c_device_id w83l785ts_id [ ] = {
2009-12-14 23:17:26 +03:00
{ " w83l785ts " , 0 } ,
2008-07-16 21:30:18 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , w83l785ts_id ) ;
2005-04-17 02:20:36 +04:00
static struct i2c_driver w83l785ts_driver = {
2008-07-16 21:30:18 +04:00
. class = I2C_CLASS_HWMON ,
2005-11-26 22:37:41 +03:00
. driver = {
. name = " w83l785ts " ,
} ,
2008-07-16 21:30:18 +04:00
. probe = w83l785ts_probe ,
. remove = w83l785ts_remove ,
. id_table = w83l785ts_id ,
. detect = w83l785ts_detect ,
2009-12-14 23:17:25 +03:00
. address_list = normal_i2c ,
2005-04-17 02:20:36 +04:00
} ;
/*
* Client data ( each client gets its own )
*/
struct w83l785ts_data {
2007-08-21 00:46:20 +04:00
struct device * hwmon_dev ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2005-04-17 02:20:36 +04:00
char valid ; /* zero until following fields are valid */
unsigned long last_updated ; /* in jiffies */
/* registers values */
2012-01-19 23:02:28 +04:00
s8 temp [ 2 ] ; /* 0: input, 1: critical limit */
2005-04-17 02:20:36 +04:00
} ;
/*
* Sysfs stuff
*/
2005-09-25 18:41:18 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * devattr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2005-09-25 18:41:18 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2005-04-17 02:20:36 +04:00
struct w83l785ts_data * data = w83l785ts_update_device ( dev ) ;
2005-09-25 18:41:18 +04:00
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 18:41:18 +04:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IRUGO , show_temp , NULL , 1 ) ;
2005-04-17 02:20:36 +04:00
/*
* Real code
*/
2008-07-16 21:30:18 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2009-12-14 23:17:23 +03:00
static int w83l785ts_detect ( struct i2c_client * client ,
2008-07-16 21:30:18 +04:00
struct i2c_board_info * info )
2005-04-17 02:20:36 +04:00
{
2009-12-09 22:35:56 +03:00
struct i2c_adapter * adapter = client - > adapter ;
u16 man_id ;
u8 chip_id ;
2005-04-17 02:20:36 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
2008-07-16 21:30:18 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2009-12-09 22:35:56 +03:00
/* detection */
if ( ( w83l785ts_read_value ( client , W83L785TS_REG_CONFIG , 0 ) & 0x80 )
| | ( w83l785ts_read_value ( client , W83L785TS_REG_TYPE , 0 ) & 0xFC ) ) {
dev_dbg ( & adapter - > dev ,
" W83L785TS-S detection failed at 0x%02x \n " ,
client - > addr ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2009-12-09 22:35:56 +03:00
/* Identification */
man_id = ( w83l785ts_read_value ( client , W83L785TS_REG_MAN_ID1 , 0 ) < < 8 )
+ w83l785ts_read_value ( client , W83L785TS_REG_MAN_ID2 , 0 ) ;
chip_id = w83l785ts_read_value ( client , W83L785TS_REG_CHIP_ID , 0 ) ;
if ( man_id ! = 0x5CA3 /* Winbond */
| | chip_id ! = 0x70 ) { /* W83L785TS-S */
dev_dbg ( & adapter - > dev ,
" Unsupported chip (man_id=0x%04X, chip_id=0x%02X) \n " ,
man_id , chip_id ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2008-07-16 21:30:18 +04:00
strlcpy ( info - > type , " w83l785ts " , I2C_NAME_SIZE ) ;
return 0 ;
}
2012-06-02 22:48:01 +04:00
static int w83l785ts_probe ( struct i2c_client * client ,
2008-07-16 21:30:18 +04:00
const struct i2c_device_id * id )
{
struct w83l785ts_data * data ;
2012-06-02 22:48:01 +04:00
struct device * dev = & client - > dev ;
2012-06-18 19:26:05 +04:00
int err ;
2008-07-16 21:30:18 +04:00
2012-06-18 19:26:05 +04:00
data = devm_kzalloc ( dev , sizeof ( struct w83l785ts_data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2008-07-16 21:30:18 +04:00
2012-06-02 22:48:01 +04:00
i2c_set_clientdata ( client , data ) ;
2005-04-17 02:20:36 +04:00
data - > valid = 0 ;
2006-01-19 01:19:26 +03:00
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
/* Default values in case the first read fails (unlikely). */
2005-09-25 18:41:18 +04:00
data - > temp [ 1 ] = data - > temp [ 0 ] = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Initialize the W83L785TS chip
* Nothing yet , assume it is already started .
*/
2012-06-02 22:48:01 +04:00
err = device_create_file ( dev , & sensor_dev_attr_temp1_input . dev_attr ) ;
2006-09-24 23:23:26 +04:00
if ( err )
2012-06-18 19:26:05 +04:00
return err ;
2006-09-24 23:23:26 +04:00
2012-06-02 22:48:01 +04:00
err = device_create_file ( dev , & sensor_dev_attr_temp1_max . dev_attr ) ;
2006-09-24 23:23:26 +04:00
if ( err )
goto exit_remove ;
2005-04-17 02:20:36 +04:00
/* Register sysfs hooks */
2012-06-02 22:48:01 +04:00
data - > hwmon_dev = hwmon_device_register ( dev ) ;
2007-08-21 00:46:20 +04:00
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
2006-09-24 23:23:26 +04:00
goto exit_remove ;
2005-07-16 05:39:18 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
2006-09-24 23:23:26 +04:00
exit_remove :
2012-06-02 22:48:01 +04:00
device_remove_file ( dev , & sensor_dev_attr_temp1_input . dev_attr ) ;
device_remove_file ( dev , & sensor_dev_attr_temp1_max . dev_attr ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2008-07-16 21:30:18 +04:00
static int w83l785ts_remove ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2005-07-16 05:39:18 +04:00
struct w83l785ts_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
2007-08-21 00:46:20 +04:00
hwmon_device_unregister ( data - > hwmon_dev ) ;
2006-09-24 23:23:26 +04:00
device_remove_file ( & client - > dev ,
& sensor_dev_attr_temp1_input . dev_attr ) ;
device_remove_file ( & client - > dev ,
& sensor_dev_attr_temp1_max . dev_attr ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static u8 w83l785ts_read_value ( struct i2c_client * client , u8 reg , u8 defval )
{
int value , i ;
2008-07-16 21:30:18 +04:00
struct device * dev ;
const char * prefix ;
2012-01-19 23:02:28 +04:00
/*
* We might be called during detection , at which point the client
* isn ' t yet fully initialized , so we can ' t use dev_dbg on it
*/
2008-07-16 21:30:18 +04:00
if ( i2c_get_clientdata ( client ) ) {
dev = & client - > dev ;
prefix = " " ;
} else {
dev = & client - > adapter - > dev ;
prefix = " w83l785ts: " ;
}
2005-04-17 02:20:36 +04:00
2012-01-19 23:02:28 +04:00
/*
* Frequent read errors have been reported on Asus boards , so we
2005-04-17 02:20:36 +04:00
* retry on read errors . If it still fails ( unlikely ) , return the
2012-01-19 23:02:28 +04:00
* default value requested by the caller .
*/
2005-04-17 02:20:36 +04:00
for ( i = 1 ; i < = MAX_RETRIES ; i + + ) {
value = i2c_smbus_read_byte_data ( client , reg ) ;
if ( value > = 0 ) {
2008-07-16 21:30:18 +04:00
dev_dbg ( dev , " %sRead 0x%02x from register 0x%02x. \n " ,
prefix , value , reg ) ;
2005-04-17 02:20:36 +04:00
return value ;
}
2008-07-16 21:30:18 +04:00
dev_dbg ( dev , " %sRead failed, will retry in %d. \n " , prefix , i ) ;
2005-04-17 02:20:36 +04:00
msleep ( i ) ;
}
2008-07-16 21:30:18 +04:00
dev_err ( dev , " %sCouldn't read value from register 0x%02x. \n " , prefix ,
2008-02-12 13:17:26 +03:00
reg ) ;
2005-04-17 02:20:36 +04:00
return defval ;
}
static struct w83l785ts_data * w83l785ts_update_device ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct w83l785ts_data * data = i2c_get_clientdata ( client ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ! data - > valid | | time_after ( jiffies , data - > last_updated + HZ * 2 ) ) {
dev_dbg ( & client - > dev , " Updating w83l785ts data. \n " ) ;
2005-09-25 18:41:18 +04:00
data - > temp [ 0 ] = w83l785ts_read_value ( client ,
W83L785TS_REG_TEMP , data - > temp [ 0 ] ) ;
data - > temp [ 1 ] = w83l785ts_read_value ( client ,
W83L785TS_REG_TEMP_OVER , data - > temp [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return data ;
}
2012-01-20 11:38:18 +04:00
module_i2c_driver ( w83l785ts_driver ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Jean Delvare <khali@linux-fr.org> " ) ;
MODULE_DESCRIPTION ( " W83L785TS-S driver " ) ;
MODULE_LICENSE ( " GPL " ) ;