2005-04-17 02:20:36 +04:00
/*
2012-01-15 01:26:08 +04:00
* ds1621 . c - Part of lm_sensors , Linux kernel modules for hardware
* monitoring
* Christian W . Zuckschwerdt < zany @ triq . net > 2000 - 11 - 23
* based on lm75 . c by Frodo Looijaard < frodol @ dds . nl >
* Ported to Linux 2.6 by Aurelien Jarno < aurelien @ aurel32 . net > with
* the help of Jean Delvare < khali @ linux - fr . org >
*
2013-05-09 09:45:53 +04:00
* The DS1621 device is a digital temperature / thermometer with 9 - bit
* resolution , a thermal alarm output ( Tout ) , and user - defined minimum
* and maximum temperature thresholds ( TH and TL ) .
*
2013-06-11 05:46:02 +04:00
* The DS1625 , DS1631 , DS1721 , and DS1731 are pin compatible with the DS1621
* and similar in operation , with slight variations as noted in the device
2013-05-09 09:45:53 +04:00
* datasheets ( please refer to www . maximintegrated . com for specific
* device information ) .
*
* Since the DS1621 was the first chipset supported by this driver ,
* most comments will refer to this chipset , but are actually general
* and concern all supported chipsets , unless mentioned otherwise .
*
2012-01-15 01:26:08 +04:00
* 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 .
*/
2005-04-17 02:20:36 +04:00
# include <linux/module.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>
2007-06-09 18:11:15 +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>
2006-09-24 23:24:46 +04:00
# include <linux/sysfs.h>
2013-05-09 09:45:53 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
2013-05-09 09:45:53 +04:00
/* Supported devices */
2013-06-11 05:46:02 +04:00
enum chips { ds1621 , ds1625 , ds1631 , ds1721 , ds1731 } ;
2013-05-09 09:45:53 +04:00
2005-04-17 02:20:36 +04:00
/* Insmod parameters */
static int polarity = - 1 ;
module_param ( polarity , int , 0 ) ;
MODULE_PARM_DESC ( polarity , " Output's polarity: 0 = active high, 1 = active low " ) ;
2013-05-09 09:45:53 +04:00
/*
* The Configuration / Status register
*
* - DS1621 :
* 7 6 5 4 3 2 1 0
* | Done | THF | TLF | NVB | X | X | POL | 1 SHOT |
*
* - DS1625 :
* 7 6 5 4 3 2 1 0
* | Done | THF | TLF | NVB | 1 | 0 | POL | 1 SHOT |
*
2013-06-11 05:46:02 +04:00
* - DS1631 , DS1731 :
2013-05-17 02:10:41 +04:00
* 7 6 5 4 3 2 1 0
* | Done | THF | TLF | NVB | R1 | R0 | POL | 1 SHOT |
*
2013-05-09 09:45:53 +04:00
* - DS1721 :
* 7 6 5 4 3 2 1 0
* | Done | X | X | U | R1 | R0 | POL | 1 SHOT |
*
* Where :
* - ' X ' is Reserved
* - ' U ' is Undefined
*/
2005-04-17 02:20:36 +04:00
# define DS1621_REG_CONFIG_NVB 0x10
2013-05-09 09:45:53 +04:00
# define DS1621_REG_CONFIG_RESOL 0x0C
2005-04-17 02:20:36 +04:00
# define DS1621_REG_CONFIG_POLARITY 0x02
# define DS1621_REG_CONFIG_1SHOT 0x01
# define DS1621_REG_CONFIG_DONE 0x80
2013-05-09 09:45:53 +04:00
# define DS1621_REG_CONFIG_RESOL_SHIFT 2
/* ds1721 conversion rates: {C/LSB, time(ms), resolution bit setting} */
static const unsigned short ds1721_convrates [ ] = {
94 , /* 9-bits (0.5, 93.75, RES[0..1] = 0 */
188 , /* 10-bits (0.25, 187.5, RES[0..1] = 1 */
375 , /* 11-bits (0.125, 375, RES[0..1] = 2 */
750 , /* 12-bits (0.0625, 750, RES[0..1] = 3 */
} ;
# define DS1621_CONVERSION_MAX 750
# define DS1625_CONVERSION_MAX 500
# define DS1621_TEMP_MAX 125000
# define DS1621_TEMP_MIN (-55000)
/* The DS1621 temperature registers */
2007-06-09 18:11:15 +04:00
static const u8 DS1621_REG_TEMP [ 3 ] = {
0xAA , /* input, word, RO */
0xA2 , /* min, word, RW */
0xA1 , /* max, word, RW */
} ;
2005-04-17 02:20:36 +04:00
# define DS1621_REG_CONF 0xAC /* byte, RW */
# define DS1621_COM_START 0xEE /* no data */
2013-05-09 09:45:53 +04:00
# define DS1721_COM_START 0x51 /* no data */
2005-04-17 02:20:36 +04:00
# define DS1621_COM_STOP 0x22 /* no data */
/* The DS1621 configuration register */
# define DS1621_ALARM_TEMP_HIGH 0x40
# define DS1621_ALARM_TEMP_LOW 0x20
2007-06-09 18:11:15 +04:00
/* Conversions */
2005-04-17 02:20:36 +04:00
# define ALARMS_FROM_REG(val) ((val) & \
2012-01-15 01:26:08 +04:00
( DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW ) )
2005-04-17 02:20:36 +04:00
/* Each client has this additional data */
struct ds1621_data {
2013-07-06 09:30:39 +04:00
struct i2c_client * client ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2005-04-17 02:20:36 +04:00
char valid ; /* !=0 if following fields are valid */
unsigned long last_updated ; /* In jiffies */
2013-05-09 09:45:53 +04:00
enum chips kind ; /* device type */
2005-04-17 02:20:36 +04:00
2007-06-09 18:11:15 +04:00
u16 temp [ 3 ] ; /* Register values, word */
2005-04-17 02:20:36 +04:00
u8 conf ; /* Register encoding, combined */
2013-06-27 21:07:19 +04:00
u8 zbits ; /* Resolution encoded as number of
* zero bits */
2013-05-09 09:45:53 +04:00
u16 update_interval ; /* Conversion rate in milliseconds */
2005-04-17 02:20:36 +04:00
} ;
2013-05-09 09:45:53 +04:00
static inline int DS1621_TEMP_FROM_REG ( u16 reg )
{
return DIV_ROUND_CLOSEST ( ( ( s16 ) reg / 16 ) * 625 , 10 ) ;
}
/*
* TEMP : 0.001 C / bit ( - 55 C to + 125 C )
* REG :
2013-06-27 21:07:19 +04:00
* - 1621 , 1625 : 0.5 C / bit , 7 zero - bits
* - 1631 , 1721 , 1731 : 0.0625 C / bit , 4 zero - bits
2013-05-09 09:45:53 +04:00
*/
2013-06-27 21:07:19 +04:00
static inline u16 DS1621_TEMP_TO_REG ( long temp , u8 zbits )
2013-05-09 09:45:53 +04:00
{
2013-06-27 21:07:19 +04:00
temp = clamp_val ( temp , DS1621_TEMP_MIN , DS1621_TEMP_MAX ) ;
temp = DIV_ROUND_CLOSEST ( temp * ( 1 < < ( 8 - zbits ) ) , 1000 ) < < zbits ;
return temp ;
2013-05-09 09:45:53 +04:00
}
2013-07-06 09:30:39 +04:00
static void ds1621_init_client ( struct ds1621_data * data ,
struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2013-05-09 09:45:53 +04:00
u8 conf , new_conf , sreg , resol ;
2009-03-30 23:46:40 +04:00
new_conf = conf = i2c_smbus_read_byte_data ( client , DS1621_REG_CONF ) ;
2005-05-04 04:21:25 +04:00
/* switch to continuous conversion mode */
2009-03-30 23:46:40 +04:00
new_conf & = ~ DS1621_REG_CONFIG_1SHOT ;
2005-04-17 02:20:36 +04:00
/* setup output polarity */
if ( polarity = = 0 )
2009-03-30 23:46:40 +04:00
new_conf & = ~ DS1621_REG_CONFIG_POLARITY ;
2005-04-17 02:20:36 +04:00
else if ( polarity = = 1 )
2009-03-30 23:46:40 +04:00
new_conf | = DS1621_REG_CONFIG_POLARITY ;
2012-01-15 01:26:08 +04:00
2009-03-30 23:46:40 +04:00
if ( conf ! = new_conf )
i2c_smbus_write_byte_data ( client , DS1621_REG_CONF , new_conf ) ;
2012-01-15 01:26:08 +04:00
2013-05-09 09:45:53 +04:00
switch ( data - > kind ) {
case ds1625 :
data - > update_interval = DS1625_CONVERSION_MAX ;
2013-06-27 21:07:19 +04:00
data - > zbits = 7 ;
2013-05-09 09:45:53 +04:00
sreg = DS1621_COM_START ;
break ;
2013-05-17 02:10:41 +04:00
case ds1631 :
2013-05-09 09:45:53 +04:00
case ds1721 :
2013-06-11 05:46:02 +04:00
case ds1731 :
2013-05-09 09:45:53 +04:00
resol = ( new_conf & DS1621_REG_CONFIG_RESOL ) > >
DS1621_REG_CONFIG_RESOL_SHIFT ;
data - > update_interval = ds1721_convrates [ resol ] ;
2013-06-27 21:07:19 +04:00
data - > zbits = 7 - resol ;
2013-05-09 09:45:53 +04:00
sreg = DS1721_COM_START ;
break ;
default :
data - > update_interval = DS1621_CONVERSION_MAX ;
2013-06-27 21:07:19 +04:00
data - > zbits = 7 ;
2013-05-09 09:45:53 +04:00
sreg = DS1621_COM_START ;
break ;
}
2005-04-17 02:20:36 +04:00
/* start conversion */
2013-05-09 09:45:53 +04:00
i2c_smbus_write_byte ( client , sreg ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-30 23:46:40 +04:00
static struct ds1621_data * ds1621_update_client ( struct device * dev )
{
2013-07-06 09:30:39 +04:00
struct ds1621_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2009-03-30 23:46:40 +04:00
u8 new_conf ;
mutex_lock ( & data - > update_lock ) ;
2013-05-09 09:45:53 +04:00
if ( time_after ( jiffies , data - > last_updated + data - > update_interval ) | |
! data - > valid ) {
2009-03-30 23:46:40 +04:00
int i ;
dev_dbg ( & client - > dev , " Starting ds1621 update \n " ) ;
2009-03-30 23:46:40 +04:00
data - > conf = i2c_smbus_read_byte_data ( client , DS1621_REG_CONF ) ;
2009-03-30 23:46:40 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( data - > temp ) ; i + + )
2011-11-04 15:00:47 +04:00
data - > temp [ i ] = i2c_smbus_read_word_swapped ( client ,
2009-03-30 23:46:40 +04:00
DS1621_REG_TEMP [ i ] ) ;
2009-03-30 23:46:40 +04:00
/* reset alarms if necessary */
new_conf = data - > conf ;
if ( data - > temp [ 0 ] > data - > temp [ 1 ] ) /* input > min */
new_conf & = ~ DS1621_ALARM_TEMP_LOW ;
if ( data - > temp [ 0 ] < data - > temp [ 2 ] ) /* input < max */
new_conf & = ~ DS1621_ALARM_TEMP_HIGH ;
if ( data - > conf ! = new_conf )
2009-03-30 23:46:40 +04:00
i2c_smbus_write_byte_data ( client , DS1621_REG_CONF ,
new_conf ) ;
2009-03-30 23:46:40 +04:00
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
mutex_unlock ( & data - > update_lock ) ;
return data ;
}
2007-06-09 18:11:15 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * da ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
struct ds1621_data * data = ds1621_update_client ( dev ) ;
return sprintf ( buf , " %d \n " ,
2013-05-09 09:45:53 +04:00
DS1621_TEMP_FROM_REG ( data - > temp [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-06-09 18:11:15 +04:00
static ssize_t set_temp ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2013-07-06 09:30:39 +04:00
struct ds1621_data * data = dev_get_drvdata ( dev ) ;
2012-01-15 01:26:08 +04:00
long val ;
int err ;
err = kstrtol ( buf , 10 , & val ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2007-06-09 18:11:15 +04:00
mutex_lock ( & data - > update_lock ) ;
2013-06-27 21:07:19 +04:00
data - > temp [ attr - > index ] = DS1621_TEMP_TO_REG ( val , data - > zbits ) ;
2013-07-06 09:30:39 +04:00
i2c_smbus_write_word_swapped ( data - > client , DS1621_REG_TEMP [ attr - > index ] ,
2011-11-04 15:00:47 +04:00
data - > temp [ attr - > index ] ) ;
2007-06-09 18:11:15 +04:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
2007-06-09 18:11:15 +04:00
static ssize_t show_alarms ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct ds1621_data * data = ds1621_update_client ( dev ) ;
return sprintf ( buf , " %d \n " , ALARMS_FROM_REG ( data - > conf ) ) ;
}
2007-06-09 18:11:15 +04:00
static ssize_t show_alarm ( struct device * dev , struct device_attribute * da ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
struct ds1621_data * data = ds1621_update_client ( dev ) ;
return sprintf ( buf , " %d \n " , ! ! ( data - > conf & attr - > index ) ) ;
}
2013-05-09 09:45:54 +04:00
static ssize_t show_convrate ( struct device * dev , struct device_attribute * da ,
char * buf )
{
2013-07-06 09:30:39 +04:00
struct ds1621_data * data = dev_get_drvdata ( dev ) ;
2013-05-09 09:45:54 +04:00
return scnprintf ( buf , PAGE_SIZE , " %hu \n " , data - > update_interval ) ;
}
static ssize_t set_convrate ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
{
2013-07-06 09:30:39 +04:00
struct ds1621_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2013-05-09 09:45:54 +04:00
unsigned long convrate ;
s32 err ;
int resol = 0 ;
err = kstrtoul ( buf , 10 , & convrate ) ;
if ( err )
return err ;
/* Convert rate into resolution bits */
while ( resol < ( ARRAY_SIZE ( ds1721_convrates ) - 1 ) & &
convrate > ds1721_convrates [ resol ] )
resol + + ;
mutex_lock ( & data - > update_lock ) ;
data - > conf = i2c_smbus_read_byte_data ( client , DS1621_REG_CONF ) ;
data - > conf & = ~ DS1621_REG_CONFIG_RESOL ;
data - > conf | = ( resol < < DS1621_REG_CONFIG_RESOL_SHIFT ) ;
i2c_smbus_write_byte_data ( client , DS1621_REG_CONF , data - > conf ) ;
data - > update_interval = ds1721_convrates [ resol ] ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
static DEVICE_ATTR ( alarms , S_IRUGO , show_alarms , NULL ) ;
2013-05-09 09:45:54 +04:00
static DEVICE_ATTR ( update_interval , S_IWUSR | S_IRUGO , show_convrate ,
set_convrate ) ;
2007-06-09 18:11:15 +04:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_min , S_IWUSR | S_IRUGO , show_temp , set_temp , 1 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IWUSR | S_IRUGO , show_temp , set_temp , 2 ) ;
2007-06-09 18:11:15 +04:00
static SENSOR_DEVICE_ATTR ( temp1_min_alarm , S_IRUGO , show_alarm , NULL ,
DS1621_ALARM_TEMP_LOW ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_alarm , S_IRUGO , show_alarm , NULL ,
DS1621_ALARM_TEMP_HIGH ) ;
2005-04-17 02:20:36 +04:00
2006-09-24 23:24:46 +04:00
static struct attribute * ds1621_attributes [ ] = {
2007-06-09 18:11:15 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
2007-06-09 18:11:15 +04:00
& sensor_dev_attr_temp1_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
2006-09-24 23:24:46 +04:00
& dev_attr_alarms . attr ,
2013-05-09 09:45:54 +04:00
& dev_attr_update_interval . attr ,
2006-09-24 23:24:46 +04:00
NULL
} ;
2013-05-09 09:45:54 +04:00
static umode_t ds1621_attribute_visible ( struct kobject * kobj ,
struct attribute * attr , int index )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
2013-07-06 09:30:39 +04:00
struct ds1621_data * data = dev_get_drvdata ( dev ) ;
2013-05-09 09:45:54 +04:00
if ( attr = = & dev_attr_update_interval . attr )
2013-05-17 02:10:41 +04:00
if ( data - > kind = = ds1621 | | data - > kind = = ds1625 )
2013-05-09 09:45:54 +04:00
/* shhh, we're hiding update_interval */
return 0 ;
return attr - > mode ;
}
2006-09-24 23:24:46 +04:00
static const struct attribute_group ds1621_group = {
. attrs = ds1621_attributes ,
2013-05-09 09:45:54 +04:00
. is_visible = ds1621_attribute_visible
2006-09-24 23:24:46 +04:00
} ;
2013-07-06 09:30:39 +04:00
static const struct attribute_group * ds1621_groups [ ] = {
& ds1621_group ,
NULL
} ;
2008-07-16 21:30:11 +04:00
static int ds1621_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct ds1621_data * data ;
2013-07-06 09:30:39 +04:00
struct device * hwmon_dev ;
2008-07-16 21:30:11 +04:00
2012-06-02 20:58:03 +04:00
data = devm_kzalloc ( & client - > dev , sizeof ( struct ds1621_data ) ,
GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2008-07-16 21:30:11 +04:00
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
2013-05-09 09:45:53 +04:00
data - > kind = id - > driver_data ;
2013-07-06 09:30:39 +04:00
data - > client = client ;
2013-05-09 09:45:53 +04:00
2005-04-17 02:20:36 +04:00
/* Initialize the DS1621 chip */
2013-07-06 09:30:39 +04:00
ds1621_init_client ( data , client ) ;
2005-04-17 02:20:36 +04:00
2013-07-06 09:30:39 +04:00
hwmon_dev = hwmon_device_register_with_groups ( & client - > dev ,
client - > name , data ,
ds1621_groups ) ;
if ( IS_ERR ( hwmon_dev ) )
return PTR_ERR ( hwmon_dev ) ;
2006-09-24 23:24:46 +04:00
2013-07-06 09:30:39 +04:00
i2c_set_clientdata ( client , hwmon_dev ) ;
2005-07-16 05:39:18 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-16 21:30:11 +04:00
static int ds1621_remove ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2013-07-06 09:30:39 +04:00
struct device * hwmon_dev = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
2013-07-06 09:30:39 +04:00
hwmon_device_unregister ( hwmon_dev ) ;
2005-07-16 05:39:18 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-03-30 23:46:40 +04:00
static const struct i2c_device_id ds1621_id [ ] = {
2013-05-09 09:45:53 +04:00
{ " ds1621 " , ds1621 } ,
{ " ds1625 " , ds1625 } ,
2013-05-17 02:10:41 +04:00
{ " ds1631 " , ds1631 } ,
2013-05-09 09:45:53 +04:00
{ " ds1721 " , ds1721 } ,
2013-06-11 05:46:02 +04:00
{ " ds1731 " , ds1731 } ,
2009-03-30 23:46:40 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ds1621_id ) ;
2005-04-17 02:20:36 +04:00
2009-03-30 23:46:40 +04:00
/* This is the driver that will be inserted */
static struct i2c_driver ds1621_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " ds1621 " ,
} ,
. probe = ds1621_probe ,
. remove = ds1621_remove ,
. id_table = ds1621_id ,
} ;
2005-04-17 02:20:36 +04:00
2012-01-20 11:38:18 +04:00
module_i2c_driver ( ds1621_driver ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Christian W. Zuckschwerdt <zany@triq.net> " ) ;
MODULE_DESCRIPTION ( " DS1621 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;