2010-05-27 19:58:56 +02:00
/*
* emc1403 . c - SMSC Thermal Driver
*
* Copyright ( C ) 2008 Intel Corp
*
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* 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 .
*
* 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 . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA .
* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
*
* TODO
* - cache alarm and critical limit registers
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/err.h>
# include <linux/sysfs.h>
# include <linux/mutex.h>
2012-10-10 15:25:56 +02:00
# include <linux/jiffies.h>
2010-05-27 19:58:56 +02:00
# define THERMAL_PID_REG 0xfd
# define THERMAL_SMSC_ID_REG 0xfe
# define THERMAL_REVISION_REG 0xff
struct thermal_data {
2013-09-29 11:49:53 -07:00
struct i2c_client * client ;
2013-09-29 13:20:53 -07:00
const struct attribute_group * groups [ 3 ] ;
2010-05-27 19:58:56 +02:00
struct mutex mutex ;
2012-01-19 11:02:17 -08:00
/*
* Cache the hyst value so we don ' t keep re - reading it . In theory
* we could cache it forever as nobody else should be writing it .
*/
2010-05-27 19:58:56 +02:00
u8 cached_hyst ;
unsigned long hyst_valid ;
} ;
static ssize_t show_temp ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
int retval ;
2010-05-27 19:58:56 +02:00
2013-09-29 11:49:53 -07:00
retval = i2c_smbus_read_byte_data ( data - > client , sda - > index ) ;
2010-05-27 19:58:56 +02:00
if ( retval < 0 )
return retval ;
return sprintf ( buf , " %d000 \n " , retval ) ;
}
static ssize_t show_bit ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sensor_device_attribute_2 * sda = to_sensor_dev_attr_2 ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
int retval ;
2010-05-27 19:58:56 +02:00
2013-09-29 11:49:53 -07:00
retval = i2c_smbus_read_byte_data ( data - > client , sda - > nr ) ;
2010-05-27 19:58:56 +02:00
if ( retval < 0 )
return retval ;
2013-09-29 11:49:53 -07:00
return sprintf ( buf , " %d \n " , ! ! ( retval & sda - > index ) ) ;
2010-05-27 19:58:56 +02:00
}
static ssize_t store_temp ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
2010-05-27 19:58:56 +02:00
unsigned long val ;
int retval ;
2012-01-04 20:58:52 +01:00
if ( kstrtoul ( buf , 10 , & val ) )
2010-05-27 19:58:56 +02:00
return - EINVAL ;
2013-09-29 11:49:53 -07:00
retval = i2c_smbus_write_byte_data ( data - > client , sda - > index ,
2010-05-27 19:58:56 +02:00
DIV_ROUND_CLOSEST ( val , 1000 ) ) ;
if ( retval < 0 )
return retval ;
return count ;
}
2010-08-14 21:08:49 +02:00
static ssize_t store_bit ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct sensor_device_attribute_2 * sda = to_sensor_dev_attr_2 ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2010-08-14 21:08:49 +02:00
unsigned long val ;
int retval ;
2012-01-04 20:58:52 +01:00
if ( kstrtoul ( buf , 10 , & val ) )
2010-08-14 21:08:49 +02:00
return - EINVAL ;
mutex_lock ( & data - > mutex ) ;
retval = i2c_smbus_read_byte_data ( client , sda - > nr ) ;
if ( retval < 0 )
goto fail ;
retval & = ~ sda - > index ;
if ( val )
retval | = sda - > index ;
retval = i2c_smbus_write_byte_data ( client , sda - > index , retval ) ;
if ( retval = = 0 )
retval = count ;
fail :
mutex_unlock ( & data - > mutex ) ;
return retval ;
}
2010-05-27 19:58:56 +02:00
static ssize_t show_hyst ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2010-05-27 19:58:56 +02:00
int retval ;
int hyst ;
retval = i2c_smbus_read_byte_data ( client , sda - > index ) ;
if ( retval < 0 )
return retval ;
if ( time_after ( jiffies , data - > hyst_valid ) ) {
hyst = i2c_smbus_read_byte_data ( client , 0x21 ) ;
if ( hyst < 0 )
return retval ;
data - > cached_hyst = hyst ;
data - > hyst_valid = jiffies + HZ ;
}
return sprintf ( buf , " %d000 \n " , retval - data - > cached_hyst ) ;
}
static ssize_t store_hyst ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( attr ) ;
2013-09-29 11:49:53 -07:00
struct thermal_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2010-05-27 19:58:56 +02:00
int retval ;
int hyst ;
unsigned long val ;
2012-01-04 20:58:52 +01:00
if ( kstrtoul ( buf , 10 , & val ) )
2010-05-27 19:58:56 +02:00
return - EINVAL ;
mutex_lock ( & data - > mutex ) ;
retval = i2c_smbus_read_byte_data ( client , sda - > index ) ;
if ( retval < 0 )
goto fail ;
hyst = val - retval * 1000 ;
hyst = DIV_ROUND_CLOSEST ( hyst , 1000 ) ;
if ( hyst < 0 | | hyst > 255 ) {
retval = - ERANGE ;
goto fail ;
}
retval = i2c_smbus_write_byte_data ( client , 0x21 , hyst ) ;
if ( retval = = 0 ) {
retval = count ;
data - > cached_hyst = hyst ;
data - > hyst_valid = jiffies + HZ ;
}
fail :
mutex_unlock ( & data - > mutex ) ;
return retval ;
}
/*
* Sensors . We pass the actual i2c register to the methods .
*/
static SENSOR_DEVICE_ATTR ( temp1_min , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x06 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x05 ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x20 ) ;
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL , 0x00 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp1_min_alarm , S_IRUGO ,
show_bit , NULL , 0x36 , 0x01 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp1_max_alarm , S_IRUGO ,
show_bit , NULL , 0x35 , 0x01 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp1_crit_alarm , S_IRUGO ,
show_bit , NULL , 0x37 , 0x01 ) ;
static SENSOR_DEVICE_ATTR ( temp1_crit_hyst , S_IRUGO | S_IWUSR ,
show_hyst , store_hyst , 0x20 ) ;
static SENSOR_DEVICE_ATTR ( temp2_min , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x08 ) ;
static SENSOR_DEVICE_ATTR ( temp2_max , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x07 ) ;
static SENSOR_DEVICE_ATTR ( temp2_crit , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x19 ) ;
static SENSOR_DEVICE_ATTR ( temp2_input , S_IRUGO , show_temp , NULL , 0x01 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp2_min_alarm , S_IRUGO ,
show_bit , NULL , 0x36 , 0x02 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp2_max_alarm , S_IRUGO ,
show_bit , NULL , 0x35 , 0x02 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp2_crit_alarm , S_IRUGO ,
show_bit , NULL , 0x37 , 0x02 ) ;
static SENSOR_DEVICE_ATTR ( temp2_crit_hyst , S_IRUGO | S_IWUSR ,
show_hyst , store_hyst , 0x19 ) ;
static SENSOR_DEVICE_ATTR ( temp3_min , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x16 ) ;
static SENSOR_DEVICE_ATTR ( temp3_max , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x15 ) ;
static SENSOR_DEVICE_ATTR ( temp3_crit , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x1A ) ;
static SENSOR_DEVICE_ATTR ( temp3_input , S_IRUGO , show_temp , NULL , 0x23 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp3_min_alarm , S_IRUGO ,
show_bit , NULL , 0x36 , 0x04 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp3_max_alarm , S_IRUGO ,
show_bit , NULL , 0x35 , 0x04 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp3_crit_alarm , S_IRUGO ,
show_bit , NULL , 0x37 , 0x04 ) ;
static SENSOR_DEVICE_ATTR ( temp3_crit_hyst , S_IRUGO | S_IWUSR ,
show_hyst , store_hyst , 0x1A ) ;
2013-09-29 13:20:53 -07:00
static SENSOR_DEVICE_ATTR ( temp4_min , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x2D ) ;
static SENSOR_DEVICE_ATTR ( temp4_max , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x2C ) ;
static SENSOR_DEVICE_ATTR ( temp4_crit , S_IRUGO | S_IWUSR ,
show_temp , store_temp , 0x30 ) ;
static SENSOR_DEVICE_ATTR ( temp4_input , S_IRUGO , show_temp , NULL , 0x2A ) ;
static SENSOR_DEVICE_ATTR_2 ( temp4_min_alarm , S_IRUGO ,
show_bit , NULL , 0x36 , 0x08 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp4_max_alarm , S_IRUGO ,
show_bit , NULL , 0x35 , 0x08 ) ;
static SENSOR_DEVICE_ATTR_2 ( temp4_crit_alarm , S_IRUGO ,
show_bit , NULL , 0x37 , 0x08 ) ;
static SENSOR_DEVICE_ATTR ( temp4_crit_hyst , S_IRUGO | S_IWUSR ,
show_hyst , store_hyst , 0x30 ) ;
2010-08-14 21:08:49 +02:00
static SENSOR_DEVICE_ATTR_2 ( power_state , S_IRUGO | S_IWUSR ,
show_bit , store_bit , 0x03 , 0x40 ) ;
2013-09-29 11:49:53 -07:00
static struct attribute * emc1403_attrs [ ] = {
2010-05-27 19:58:56 +02:00
& sensor_dev_attr_temp1_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_crit . dev_attr . attr ,
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_crit_hyst . dev_attr . attr ,
& sensor_dev_attr_temp2_min . dev_attr . attr ,
& sensor_dev_attr_temp2_max . dev_attr . attr ,
& sensor_dev_attr_temp2_crit . dev_attr . attr ,
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp2_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_crit_hyst . dev_attr . attr ,
& sensor_dev_attr_temp3_min . dev_attr . attr ,
& sensor_dev_attr_temp3_max . dev_attr . attr ,
& sensor_dev_attr_temp3_crit . dev_attr . attr ,
& sensor_dev_attr_temp3_input . dev_attr . attr ,
& sensor_dev_attr_temp3_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp3_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp3_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp3_crit_hyst . dev_attr . attr ,
2010-08-14 21:08:49 +02:00
& sensor_dev_attr_power_state . dev_attr . attr ,
2010-05-27 19:58:56 +02:00
NULL
} ;
2013-09-29 13:20:53 -07:00
static const struct attribute_group emc1403_group = {
. attrs = emc1403_attrs ,
} ;
static struct attribute * emc1404_attrs [ ] = {
& sensor_dev_attr_temp4_min . dev_attr . attr ,
& sensor_dev_attr_temp4_max . dev_attr . attr ,
& sensor_dev_attr_temp4_crit . dev_attr . attr ,
& sensor_dev_attr_temp4_input . dev_attr . attr ,
& sensor_dev_attr_temp4_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp4_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp4_crit_alarm . dev_attr . attr ,
& sensor_dev_attr_temp4_crit_hyst . dev_attr . attr ,
NULL
} ;
static const struct attribute_group emc1404_group = {
. attrs = emc1404_attrs ,
} ;
2010-05-27 19:58:56 +02:00
static int emc1403_detect ( struct i2c_client * client ,
struct i2c_board_info * info )
{
int id ;
2011-01-12 21:55:12 +01:00
/* Check if thermal chip is SMSC and EMC1403 or EMC1423 */
2010-05-27 19:58:56 +02:00
id = i2c_smbus_read_byte_data ( client , THERMAL_SMSC_ID_REG ) ;
if ( id ! = 0x5d )
return - ENODEV ;
2011-01-12 21:55:12 +01:00
id = i2c_smbus_read_byte_data ( client , THERMAL_PID_REG ) ;
switch ( id ) {
case 0x21 :
strlcpy ( info - > type , " emc1403 " , I2C_NAME_SIZE ) ;
break ;
case 0x23 :
strlcpy ( info - > type , " emc1423 " , I2C_NAME_SIZE ) ;
break ;
2013-09-29 13:20:53 -07:00
case 0x25 :
strlcpy ( info - > type , " emc1404 " , I2C_NAME_SIZE ) ;
break ;
case 0x27 :
strlcpy ( info - > type , " emc1424 " , I2C_NAME_SIZE ) ;
break ;
2011-01-12 21:55:12 +01:00
default :
2010-05-27 19:58:56 +02:00
return - ENODEV ;
2011-01-12 21:55:12 +01:00
}
2010-05-27 19:58:56 +02:00
id = i2c_smbus_read_byte_data ( client , THERMAL_REVISION_REG ) ;
if ( id ! = 0x01 )
return - ENODEV ;
return 0 ;
}
static int emc1403_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct thermal_data * data ;
2013-09-29 11:49:53 -07:00
struct device * hwmon_dev ;
2010-05-27 19:58:56 +02:00
2012-06-02 09:58:04 -07:00
data = devm_kzalloc ( & client - > dev , sizeof ( struct thermal_data ) ,
GFP_KERNEL ) ;
if ( data = = NULL )
2010-05-27 19:58:56 +02:00
return - ENOMEM ;
2013-09-29 11:49:53 -07:00
data - > client = client ;
2010-05-27 19:58:56 +02:00
mutex_init ( & data - > mutex ) ;
data - > hyst_valid = jiffies - 1 ; /* Expired */
2013-09-29 13:20:53 -07:00
data - > groups [ 0 ] = & emc1403_group ;
if ( id - > driver_data )
data - > groups [ 1 ] = & emc1404_group ;
2013-09-29 11:49:53 -07:00
hwmon_dev = hwmon_device_register_with_groups ( & client - > dev ,
client - > name , data ,
2013-09-29 13:20:53 -07:00
data - > groups ) ;
2013-09-29 11:49:53 -07:00
if ( IS_ERR ( hwmon_dev ) )
return PTR_ERR ( hwmon_dev ) ;
2010-05-27 19:58:56 +02:00
2013-09-29 13:20:53 -07:00
dev_info ( & client - > dev , " %s Thermal chip found \n " , id - > name ) ;
2010-05-27 19:58:56 +02:00
return 0 ;
}
static const unsigned short emc1403_address_list [ ] = {
2011-02-09 11:51:29 -08:00
0x18 , 0x29 , 0x4c , 0x4d , I2C_CLIENT_END
2010-05-27 19:58:56 +02:00
} ;
static const struct i2c_device_id emc1403_idtable [ ] = {
{ " emc1403 " , 0 } ,
2013-09-29 13:20:53 -07:00
{ " emc1404 " , 1 } ,
2011-01-12 21:55:12 +01:00
{ " emc1423 " , 0 } ,
2013-09-29 13:20:53 -07:00
{ " emc1424 " , 1 } ,
2010-05-27 19:58:56 +02:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , emc1403_idtable ) ;
static struct i2c_driver sensor_emc1403 = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " emc1403 " ,
} ,
. detect = emc1403_detect ,
. probe = emc1403_probe ,
. id_table = emc1403_idtable ,
. address_list = emc1403_address_list ,
} ;
2012-01-20 15:38:18 +08:00
module_i2c_driver ( sensor_emc1403 ) ;
2010-05-27 19:58:56 +02:00
MODULE_AUTHOR ( " Kalhan Trisal <kalhan.trisal@intel.com " ) ;
MODULE_DESCRIPTION ( " emc1403 Thermal Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;