2010-08-10 04:21:08 +04:00
/*
* jc42 . c - driver for Jedec JC42 .4 compliant temperature sensors
*
* Copyright ( c ) 2010 Ericsson AB .
*
* Derived from lm77 . c by Andras BALI < drewie @ freemail . hu > .
*
* JC42 .4 compliant temperature sensors are typically used on memory modules .
*
* 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/init.h>
# include <linux/slab.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>
/* Addresses to scan */
static const unsigned short normal_i2c [ ] = {
0x18 , 0x19 , 0x1a , 0x1b , 0x1c , 0x1d , 0x1e , 0x1f , I2C_CLIENT_END } ;
/* JC42 registers. All registers are 16 bit. */
# define JC42_REG_CAP 0x00
# define JC42_REG_CONFIG 0x01
# define JC42_REG_TEMP_UPPER 0x02
# define JC42_REG_TEMP_LOWER 0x03
# define JC42_REG_TEMP_CRITICAL 0x04
# define JC42_REG_TEMP 0x05
# define JC42_REG_MANID 0x06
# define JC42_REG_DEVICEID 0x07
/* Status bits in temperature register */
# define JC42_ALARM_CRIT_BIT 15
# define JC42_ALARM_MAX_BIT 14
# define JC42_ALARM_MIN_BIT 13
/* Configuration register defines */
# define JC42_CFG_CRIT_ONLY (1 << 2)
2011-02-16 16:02:38 +03:00
# define JC42_CFG_TCRIT_LOCK (1 << 6)
# define JC42_CFG_EVENT_LOCK (1 << 7)
2010-08-10 04:21:08 +04:00
# define JC42_CFG_SHUTDOWN (1 << 8)
# define JC42_CFG_HYST_SHIFT 9
2012-07-27 00:13:47 +04:00
# define JC42_CFG_HYST_MASK (0x03 << 9)
2010-08-10 04:21:08 +04:00
/* Capabilities */
# define JC42_CAP_RANGE (1 << 2)
/* Manufacturer IDs */
# define ADT_MANID 0x11d4 /* Analog Devices */
2012-03-05 23:13:52 +04:00
# define ATMEL_MANID 0x001f /* Atmel */
2014-04-16 09:07:30 +04:00
# define ATMEL_MANID2 0x1114 /* Atmel */
2010-08-10 04:21:08 +04:00
# define MAX_MANID 0x004d /* Maxim */
# define IDT_MANID 0x00b3 /* IDT */
# define MCP_MANID 0x0054 /* Microchip */
# define NXP_MANID 0x1131 /* NXP Semiconductors */
# define ONS_MANID 0x1b09 /* ON Semiconductor */
# define STM_MANID 0x104a /* ST Microelectronics */
/* Supported chips */
/* Analog Devices */
# define ADT7408_DEVID 0x0801
# define ADT7408_DEVID_MASK 0xffff
2012-03-05 23:13:52 +04:00
/* Atmel */
# define AT30TS00_DEVID 0x8201
# define AT30TS00_DEVID_MASK 0xffff
2014-04-16 09:07:30 +04:00
# define AT30TSE004_DEVID 0x2200
# define AT30TSE004_DEVID_MASK 0xffff
2010-08-10 04:21:08 +04:00
/* IDT */
# define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */
# define TS3000B3_DEVID_MASK 0xffff
2012-03-05 23:13:52 +04:00
# define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */
# define TS3000GB2_DEVID_MASK 0xffff
2010-08-10 04:21:08 +04:00
/* Maxim */
# define MAX6604_DEVID 0x3e00
# define MAX6604_DEVID_MASK 0xffff
/* Microchip */
2012-03-05 23:13:52 +04:00
# define MCP9804_DEVID 0x0200
# define MCP9804_DEVID_MASK 0xfffc
2010-08-10 04:21:08 +04:00
# define MCP98242_DEVID 0x2000
# define MCP98242_DEVID_MASK 0xfffc
# define MCP98243_DEVID 0x2100
# define MCP98243_DEVID_MASK 0xfffc
2013-01-29 08:35:19 +04:00
# define MCP98244_DEVID 0x2200
# define MCP98244_DEVID_MASK 0xfffc
2010-08-10 04:21:08 +04:00
# define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */
# define MCP9843_DEVID_MASK 0xfffe
/* NXP */
# define SE97_DEVID 0xa200
# define SE97_DEVID_MASK 0xfffc
# define SE98_DEVID 0xa100
# define SE98_DEVID_MASK 0xfffc
/* ON Semiconductor */
# define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */
# define CAT6095_DEVID_MASK 0xffe0
/* ST Microelectronics */
# define STTS424_DEVID 0x0101
# define STTS424_DEVID_MASK 0xffff
# define STTS424E_DEVID 0x0000
# define STTS424E_DEVID_MASK 0xfffe
2012-03-05 17:32:00 +04:00
# define STTS2002_DEVID 0x0300
# define STTS2002_DEVID_MASK 0xffff
2014-04-16 09:07:30 +04:00
# define STTS2004_DEVID 0x2201
# define STTS2004_DEVID_MASK 0xffff
2012-03-05 17:32:00 +04:00
# define STTS3000_DEVID 0x0200
# define STTS3000_DEVID_MASK 0xffff
2010-08-10 04:21:08 +04:00
static u16 jc42_hysteresis [ ] = { 0 , 1500 , 3000 , 6000 } ;
struct jc42_chips {
u16 manid ;
u16 devid ;
u16 devid_mask ;
} ;
static struct jc42_chips jc42_chips [ ] = {
{ ADT_MANID , ADT7408_DEVID , ADT7408_DEVID_MASK } ,
2012-03-05 23:13:52 +04:00
{ ATMEL_MANID , AT30TS00_DEVID , AT30TS00_DEVID_MASK } ,
2014-04-16 09:07:30 +04:00
{ ATMEL_MANID2 , AT30TSE004_DEVID , AT30TSE004_DEVID_MASK } ,
2010-08-10 04:21:08 +04:00
{ IDT_MANID , TS3000B3_DEVID , TS3000B3_DEVID_MASK } ,
2012-03-05 23:13:52 +04:00
{ IDT_MANID , TS3000GB2_DEVID , TS3000GB2_DEVID_MASK } ,
2010-08-10 04:21:08 +04:00
{ MAX_MANID , MAX6604_DEVID , MAX6604_DEVID_MASK } ,
2012-03-05 23:13:52 +04:00
{ MCP_MANID , MCP9804_DEVID , MCP9804_DEVID_MASK } ,
2010-08-10 04:21:08 +04:00
{ MCP_MANID , MCP98242_DEVID , MCP98242_DEVID_MASK } ,
{ MCP_MANID , MCP98243_DEVID , MCP98243_DEVID_MASK } ,
2013-01-29 08:35:19 +04:00
{ MCP_MANID , MCP98244_DEVID , MCP98244_DEVID_MASK } ,
2010-08-10 04:21:08 +04:00
{ MCP_MANID , MCP9843_DEVID , MCP9843_DEVID_MASK } ,
{ NXP_MANID , SE97_DEVID , SE97_DEVID_MASK } ,
{ ONS_MANID , CAT6095_DEVID , CAT6095_DEVID_MASK } ,
{ NXP_MANID , SE98_DEVID , SE98_DEVID_MASK } ,
{ STM_MANID , STTS424_DEVID , STTS424_DEVID_MASK } ,
{ STM_MANID , STTS424E_DEVID , STTS424E_DEVID_MASK } ,
2012-03-05 17:32:00 +04:00
{ STM_MANID , STTS2002_DEVID , STTS2002_DEVID_MASK } ,
2014-04-16 09:07:30 +04:00
{ STM_MANID , STTS2004_DEVID , STTS2004_DEVID_MASK } ,
2012-03-05 17:32:00 +04:00
{ STM_MANID , STTS3000_DEVID , STTS3000_DEVID_MASK } ,
2010-08-10 04:21:08 +04:00
} ;
2014-04-16 08:44:20 +04:00
enum temp_index {
t_input = 0 ,
t_crit ,
t_min ,
t_max ,
t_num_temp
} ;
static const u8 temp_regs [ t_num_temp ] = {
[ t_input ] = JC42_REG_TEMP ,
[ t_crit ] = JC42_REG_TEMP_CRITICAL ,
[ t_min ] = JC42_REG_TEMP_LOWER ,
[ t_max ] = JC42_REG_TEMP_UPPER ,
} ;
2010-08-10 04:21:08 +04:00
/* Each client has this additional data */
struct jc42_data {
2013-09-05 20:02:34 +04:00
struct i2c_client * client ;
2010-08-10 04:21:08 +04:00
struct mutex update_lock ; /* protect register access */
bool extended ; /* true if extended range supported */
bool valid ;
unsigned long last_updated ; /* In jiffies */
u16 orig_config ; /* original configuration */
u16 config ; /* current configuration */
2014-04-16 08:44:20 +04:00
u16 temp [ t_num_temp ] ; /* Temperatures */
2010-08-10 04:21:08 +04:00
} ;
# define JC42_TEMP_MIN_EXTENDED (-40000)
# define JC42_TEMP_MIN 0
# define JC42_TEMP_MAX 125000
2015-01-19 04:29:32 +03:00
static u16 jc42_temp_to_reg ( long temp , bool extended )
2010-08-10 04:21:08 +04:00
{
2013-01-09 20:09:34 +04:00
int ntemp = clamp_val ( temp ,
extended ? JC42_TEMP_MIN_EXTENDED :
JC42_TEMP_MIN , JC42_TEMP_MAX ) ;
2010-08-10 04:21:08 +04:00
/* convert from 0.001 to 0.0625 resolution */
return ( ntemp * 2 / 125 ) & 0x1fff ;
}
static int jc42_temp_from_reg ( s16 reg )
{
2015-01-19 04:27:55 +03:00
reg = sign_extend32 ( reg , 12 ) ;
2010-08-10 04:21:08 +04:00
/* convert from 0.0625 to 0.001 resolution */
return reg * 125 / 2 ;
}
2014-04-16 08:28:15 +04:00
static struct jc42_data * jc42_update_device ( struct device * dev )
{
struct jc42_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
struct jc42_data * ret = data ;
2014-04-16 08:44:20 +04:00
int i , val ;
2014-04-16 08:28:15 +04:00
mutex_lock ( & data - > update_lock ) ;
if ( time_after ( jiffies , data - > last_updated + HZ ) | | ! data - > valid ) {
2014-04-16 08:44:20 +04:00
for ( i = 0 ; i < t_num_temp ; i + + ) {
val = i2c_smbus_read_word_swapped ( client , temp_regs [ i ] ) ;
if ( val < 0 ) {
ret = ERR_PTR ( val ) ;
goto abort ;
}
data - > temp [ i ] = val ;
2014-04-16 08:28:15 +04:00
}
data - > last_updated = jiffies ;
data - > valid = true ;
}
abort :
mutex_unlock ( & data - > update_lock ) ;
return ret ;
}
2014-04-16 08:44:20 +04:00
/* sysfs functions */
2010-08-10 04:21:08 +04:00
2014-04-16 08:44:20 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * devattr ,
char * buf )
2010-08-10 04:21:08 +04:00
{
2014-04-16 08:44:20 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2010-08-10 04:21:08 +04:00
struct jc42_data * data = jc42_update_device ( dev ) ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2014-04-16 08:44:20 +04:00
return sprintf ( buf , " %d \n " ,
jc42_temp_from_reg ( data - > temp [ attr - > index ] ) ) ;
2010-08-10 04:21:08 +04:00
}
2014-04-16 08:44:20 +04:00
static ssize_t show_temp_hyst ( struct device * dev ,
struct device_attribute * devattr , char * buf )
2010-08-10 04:21:08 +04:00
{
2014-04-16 08:44:20 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2010-08-10 04:21:08 +04:00
struct jc42_data * data = jc42_update_device ( dev ) ;
int temp , hyst ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2014-04-16 08:44:20 +04:00
temp = jc42_temp_from_reg ( data - > temp [ attr - > index ] ) ;
2012-07-27 00:13:47 +04:00
hyst = jc42_hysteresis [ ( data - > config & JC42_CFG_HYST_MASK )
> > JC42_CFG_HYST_SHIFT ] ;
2010-08-10 04:21:08 +04:00
return sprintf ( buf , " %d \n " , temp - hyst ) ;
}
2014-04-16 08:44:20 +04:00
static ssize_t set_temp ( struct device * dev , struct device_attribute * devattr ,
const char * buf , size_t count )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct jc42_data * data = dev_get_drvdata ( dev ) ;
int err , ret = count ;
int nr = attr - > index ;
long val ;
2010-08-10 04:21:08 +04:00
2014-04-16 08:44:20 +04:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
data - > temp [ nr ] = jc42_temp_to_reg ( val , data - > extended ) ;
err = i2c_smbus_write_word_swapped ( data - > client , temp_regs [ nr ] ,
data - > temp [ nr ] ) ;
if ( err < 0 )
ret = err ;
mutex_unlock ( & data - > update_lock ) ;
return ret ;
}
2010-08-10 04:21:08 +04:00
2012-01-19 23:02:19 +04:00
/*
* JC42 .4 compliant chips only support four hysteresis values .
* Pick best choice and go from there .
*/
2010-08-10 04:21:08 +04:00
static ssize_t set_temp_crit_hyst ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2013-09-05 20:02:34 +04:00
struct jc42_data * data = dev_get_drvdata ( dev ) ;
2015-01-23 12:27:03 +03:00
long val ;
2010-08-10 04:21:08 +04:00
int diff , hyst ;
int err ;
int ret = count ;
2015-01-23 12:27:03 +03:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
2010-08-10 04:21:08 +04:00
return - EINVAL ;
2015-01-23 12:27:03 +03:00
val = clamp_val ( val , ( data - > extended ? JC42_TEMP_MIN_EXTENDED :
JC42_TEMP_MIN ) - 6000 , JC42_TEMP_MAX ) ;
2014-04-16 08:44:20 +04:00
diff = jc42_temp_from_reg ( data - > temp [ t_crit ] ) - val ;
2015-01-19 20:16:53 +03:00
2010-08-10 04:21:08 +04:00
hyst = 0 ;
if ( diff > 0 ) {
if ( diff < 2250 )
hyst = 1 ; /* 1.5 degrees C */
else if ( diff < 4500 )
hyst = 2 ; /* 3.0 degrees C */
else
hyst = 3 ; /* 6.0 degrees C */
}
mutex_lock ( & data - > update_lock ) ;
2012-07-27 00:13:47 +04:00
data - > config = ( data - > config & ~ JC42_CFG_HYST_MASK )
2010-08-10 04:21:08 +04:00
| ( hyst < < JC42_CFG_HYST_SHIFT ) ;
2013-09-05 20:02:34 +04:00
err = i2c_smbus_write_word_swapped ( data - > client , JC42_REG_CONFIG ,
2011-11-04 15:00:47 +04:00
data - > config ) ;
2010-08-10 04:21:08 +04:00
if ( err < 0 )
ret = err ;
mutex_unlock ( & data - > update_lock ) ;
return ret ;
}
static ssize_t show_alarm ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
u16 bit = to_sensor_dev_attr ( attr ) - > index ;
struct jc42_data * data = jc42_update_device ( dev ) ;
u16 val ;
if ( IS_ERR ( data ) )
return PTR_ERR ( data ) ;
2014-04-16 08:44:20 +04:00
val = data - > temp [ t_input ] ;
2010-08-10 04:21:08 +04:00
if ( bit ! = JC42_ALARM_CRIT_BIT & & ( data - > config & JC42_CFG_CRIT_ONLY ) )
val = 0 ;
return sprintf ( buf , " %u \n " , ( val > > bit ) & 1 ) ;
}
2014-04-16 08:44:20 +04:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL , t_input ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit , S_IRUGO , show_temp , set_temp , t_crit ) ;
static SENSOR_DEVICE_ATTR ( temp1_min , S_IRUGO , show_temp , set_temp , t_min ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IRUGO , show_temp , set_temp , t_max ) ;
2010-08-10 04:21:08 +04:00
2014-04-16 08:44:20 +04:00
static SENSOR_DEVICE_ATTR ( temp1_crit_hyst , S_IRUGO , show_temp_hyst ,
set_temp_crit_hyst , t_crit ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_hyst , S_IRUGO , show_temp_hyst , NULL , t_max ) ;
2010-08-10 04:21:08 +04:00
static SENSOR_DEVICE_ATTR ( temp1_crit_alarm , S_IRUGO , show_alarm , NULL ,
JC42_ALARM_CRIT_BIT ) ;
static SENSOR_DEVICE_ATTR ( temp1_min_alarm , S_IRUGO , show_alarm , NULL ,
JC42_ALARM_MIN_BIT ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_alarm , S_IRUGO , show_alarm , NULL ,
JC42_ALARM_MAX_BIT ) ;
static struct attribute * jc42_attributes [ ] = {
2014-04-16 08:44:20 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_crit . dev_attr . attr ,
& sensor_dev_attr_temp1_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_hyst . dev_attr . attr ,
& sensor_dev_attr_temp1_max_hyst . dev_attr . attr ,
2010-08-10 04:21:08 +04:00
& sensor_dev_attr_temp1_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
NULL
} ;
2011-07-24 07:11:19 +04:00
static umode_t jc42_attribute_mode ( struct kobject * kobj ,
2011-02-16 16:02:38 +03:00
struct attribute * attr , int index )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
2013-09-05 20:02:34 +04:00
struct jc42_data * data = dev_get_drvdata ( dev ) ;
2011-02-16 16:02:38 +03:00
unsigned int config = data - > config ;
bool readonly ;
2014-04-16 08:44:20 +04:00
if ( attr = = & sensor_dev_attr_temp1_crit . dev_attr . attr )
2011-02-16 16:02:38 +03:00
readonly = config & JC42_CFG_TCRIT_LOCK ;
2014-04-16 08:44:20 +04:00
else if ( attr = = & sensor_dev_attr_temp1_min . dev_attr . attr | |
attr = = & sensor_dev_attr_temp1_max . dev_attr . attr )
2011-02-16 16:02:38 +03:00
readonly = config & JC42_CFG_EVENT_LOCK ;
2014-04-16 08:44:20 +04:00
else if ( attr = = & sensor_dev_attr_temp1_crit_hyst . dev_attr . attr )
2011-02-16 16:02:38 +03:00
readonly = config & ( JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK ) ;
else
readonly = true ;
return S_IRUGO | ( readonly ? 0 : S_IWUSR ) ;
}
2010-08-10 04:21:08 +04:00
static const struct attribute_group jc42_group = {
. attrs = jc42_attributes ,
2011-02-16 16:02:38 +03:00
. is_visible = jc42_attribute_mode ,
2010-08-10 04:21:08 +04:00
} ;
2013-09-05 20:02:34 +04:00
__ATTRIBUTE_GROUPS ( jc42 ) ;
2010-08-10 04:21:08 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2012-02-22 20:56:47 +04:00
static int jc42_detect ( struct i2c_client * client , struct i2c_board_info * info )
2010-08-10 04:21:08 +04:00
{
2012-02-22 20:56:47 +04:00
struct i2c_adapter * adapter = client - > adapter ;
2010-08-10 04:21:08 +04:00
int i , config , cap , manid , devid ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA ) )
return - ENODEV ;
2012-02-22 20:56:47 +04:00
cap = i2c_smbus_read_word_swapped ( client , JC42_REG_CAP ) ;
config = i2c_smbus_read_word_swapped ( client , JC42_REG_CONFIG ) ;
manid = i2c_smbus_read_word_swapped ( client , JC42_REG_MANID ) ;
devid = i2c_smbus_read_word_swapped ( client , JC42_REG_DEVICEID ) ;
2010-08-10 04:21:08 +04:00
if ( cap < 0 | | config < 0 | | manid < 0 | | devid < 0 )
return - ENODEV ;
if ( ( cap & 0xff00 ) | | ( config & 0xf800 ) )
return - ENODEV ;
for ( i = 0 ; i < ARRAY_SIZE ( jc42_chips ) ; i + + ) {
struct jc42_chips * chip = & jc42_chips [ i ] ;
if ( manid = = chip - > manid & &
( devid & chip - > devid_mask ) = = chip - > devid ) {
strlcpy ( info - > type , " jc42 " , I2C_NAME_SIZE ) ;
return 0 ;
}
}
return - ENODEV ;
}
2012-02-22 20:56:47 +04:00
static int jc42_probe ( struct i2c_client * client , const struct i2c_device_id * id )
2010-08-10 04:21:08 +04:00
{
2012-02-22 20:56:47 +04:00
struct device * dev = & client - > dev ;
2013-09-05 20:02:34 +04:00
struct device * hwmon_dev ;
struct jc42_data * data ;
int config , cap ;
2010-08-10 04:21:08 +04:00
2012-02-22 20:56:47 +04:00
data = devm_kzalloc ( dev , sizeof ( struct jc42_data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2010-08-10 04:21:08 +04:00
2013-09-05 20:02:34 +04:00
data - > client = client ;
2012-02-22 20:56:47 +04:00
i2c_set_clientdata ( client , data ) ;
2010-08-10 04:21:08 +04:00
mutex_init ( & data - > update_lock ) ;
2012-02-22 20:56:47 +04:00
cap = i2c_smbus_read_word_swapped ( client , JC42_REG_CAP ) ;
if ( cap < 0 )
return cap ;
2010-08-10 04:21:08 +04:00
data - > extended = ! ! ( cap & JC42_CAP_RANGE ) ;
2012-02-22 20:56:47 +04:00
config = i2c_smbus_read_word_swapped ( client , JC42_REG_CONFIG ) ;
if ( config < 0 )
return config ;
2010-08-10 04:21:08 +04:00
data - > orig_config = config ;
if ( config & JC42_CFG_SHUTDOWN ) {
config & = ~ JC42_CFG_SHUTDOWN ;
2012-02-22 20:56:47 +04:00
i2c_smbus_write_word_swapped ( client , JC42_REG_CONFIG , config ) ;
2010-08-10 04:21:08 +04:00
}
data - > config = config ;
2013-09-05 20:02:34 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data ,
jc42_groups ) ;
2013-09-18 07:54:24 +04:00
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2010-08-10 04:21:08 +04:00
}
static int jc42_remove ( struct i2c_client * client )
{
struct jc42_data * data = i2c_get_clientdata ( client ) ;
2012-07-27 00:18:43 +04:00
/* Restore original configuration except hysteresis */
if ( ( data - > config & ~ JC42_CFG_HYST_MASK ) ! =
( data - > orig_config & ~ JC42_CFG_HYST_MASK ) ) {
int config ;
config = ( data - > orig_config & ~ JC42_CFG_HYST_MASK )
| ( data - > config & JC42_CFG_HYST_MASK ) ;
i2c_smbus_write_word_swapped ( client , JC42_REG_CONFIG , config ) ;
}
2010-08-10 04:21:08 +04:00
return 0 ;
}
2014-04-16 08:28:15 +04:00
# ifdef CONFIG_PM
static int jc42_suspend ( struct device * dev )
2010-08-10 04:21:08 +04:00
{
2013-09-05 20:02:34 +04:00
struct jc42_data * data = dev_get_drvdata ( dev ) ;
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
data - > config | = JC42_CFG_SHUTDOWN ;
i2c_smbus_write_word_swapped ( data - > client , JC42_REG_CONFIG ,
data - > config ) ;
return 0 ;
}
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
static int jc42_resume ( struct device * dev )
{
struct jc42_data * data = dev_get_drvdata ( dev ) ;
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
data - > config & = ~ JC42_CFG_SHUTDOWN ;
i2c_smbus_write_word_swapped ( data - > client , JC42_REG_CONFIG ,
data - > config ) ;
return 0 ;
}
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
static const struct dev_pm_ops jc42_dev_pm_ops = {
. suspend = jc42_suspend ,
. resume = jc42_resume ,
} ;
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
# define JC42_DEV_PM_OPS (&jc42_dev_pm_ops)
# else
# define JC42_DEV_PM_OPS NULL
# endif /* CONFIG_PM */
2010-08-10 04:21:08 +04:00
2014-04-16 08:28:15 +04:00
static const struct i2c_device_id jc42_id [ ] = {
{ " jc42 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , jc42_id ) ;
static struct i2c_driver jc42_driver = {
. class = I2C_CLASS_SPD ,
. driver = {
. name = " jc42 " ,
. pm = JC42_DEV_PM_OPS ,
} ,
. probe = jc42_probe ,
. remove = jc42_remove ,
. id_table = jc42_id ,
. detect = jc42_detect ,
. address_list = normal_i2c ,
} ;
2010-08-10 04:21:08 +04:00
2012-01-20 11:38:18 +04:00
module_i2c_driver ( jc42_driver ) ;
2010-08-10 04:21:08 +04:00
2012-06-22 23:07:25 +04:00
MODULE_AUTHOR ( " Guenter Roeck <linux@roeck-us.net> " ) ;
2010-08-10 04:21:08 +04:00
MODULE_DESCRIPTION ( " JC42 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;