2005-04-17 02:20:36 +04:00
/*
* lm63 . c - driver for the National Semiconductor LM63 temperature sensor
* with integrated fan control
2014-01-29 23:40:08 +04:00
* Copyright ( C ) 2004 - 2008 Jean Delvare < jdelvare @ suse . de >
2005-04-17 02:20:36 +04:00
* Based on the lm90 driver .
*
* The LM63 is a sensor chip made by National Semiconductor . It measures
* two temperatures ( its own and one external one ) and the speed of one
* fan , those speed it can additionally control . Complete datasheet can be
* obtained from National ' s website at :
* http : //www.national.com/pf/LM/LM63.html
*
* The LM63 is basically an LM86 with fan speed monitoring and control
* capabilities added . It misses some of the LM86 features though :
* - No low limit for local temperature .
* - No critical limit for local temperature .
* - Critical limit for remote temperature can be changed only once . We
* will consider that the critical limit is read - only .
*
* The datasheet isn ' t very clear about what the tachometer reading is .
* I had a explanation from National Semiconductor though . The two lower
* bits of the read value have to be masked out . The value is still 16 bit
* in width .
*
* 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>
2005-06-06 21:34:45 +04:00
# include <linux/hwmon-sysfs.h>
2005-07-16 05:39:18 +04:00
# include <linux/hwmon.h>
# include <linux/err.h>
2006-01-19 01:19:26 +03:00
# include <linux/mutex.h>
2006-09-24 23:16:40 +04:00
# include <linux/sysfs.h>
2012-01-17 01:51:45 +04:00
# include <linux/types.h>
2005-04-17 02:20:36 +04:00
/*
* Addresses to scan
2012-01-17 01:51:47 +04:00
* Address is fully defined internally and cannot be changed except for
* LM64 which has one pin dedicated to address selection .
* LM63 and LM96163 have address 0x4c .
* LM64 can have address 0x18 or 0x4e .
2005-04-17 02:20:36 +04:00
*/
2010-05-27 21:58:38 +04:00
static const unsigned short normal_i2c [ ] = { 0x18 , 0x4c , 0x4e , I2C_CLIENT_END } ;
2005-04-17 02:20:36 +04:00
/*
* The LM63 registers
*/
# define LM63_REG_CONFIG1 0x03
2012-01-17 01:51:46 +04:00
# define LM63_REG_CONVRATE 0x04
2005-04-17 02:20:36 +04:00
# define LM63_REG_CONFIG2 0xBF
# define LM63_REG_CONFIG_FAN 0x4A
# define LM63_REG_TACH_COUNT_MSB 0x47
# define LM63_REG_TACH_COUNT_LSB 0x46
# define LM63_REG_TACH_LIMIT_MSB 0x49
# define LM63_REG_TACH_LIMIT_LSB 0x48
# define LM63_REG_PWM_VALUE 0x4C
# define LM63_REG_PWM_FREQ 0x4D
2012-01-17 01:51:47 +04:00
# define LM63_REG_LUT_TEMP_HYST 0x4F
# define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr))
# define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr))
2005-04-17 02:20:36 +04:00
# define LM63_REG_LOCAL_TEMP 0x00
# define LM63_REG_LOCAL_HIGH 0x05
# define LM63_REG_REMOTE_TEMP_MSB 0x01
# define LM63_REG_REMOTE_TEMP_LSB 0x10
# define LM63_REG_REMOTE_OFFSET_MSB 0x11
# define LM63_REG_REMOTE_OFFSET_LSB 0x12
# define LM63_REG_REMOTE_HIGH_MSB 0x07
# define LM63_REG_REMOTE_HIGH_LSB 0x13
# define LM63_REG_REMOTE_LOW_MSB 0x08
# define LM63_REG_REMOTE_LOW_LSB 0x14
# define LM63_REG_REMOTE_TCRIT 0x19
# define LM63_REG_REMOTE_TCRIT_HYST 0x21
# define LM63_REG_ALERT_STATUS 0x02
# define LM63_REG_ALERT_MASK 0x16
# define LM63_REG_MAN_ID 0xFE
# define LM63_REG_CHIP_ID 0xFF
2012-01-17 01:51:46 +04:00
# define LM96163_REG_TRUTHERM 0x30
2012-01-17 01:51:46 +04:00
# define LM96163_REG_REMOTE_TEMP_U_MSB 0x31
# define LM96163_REG_REMOTE_TEMP_U_LSB 0x32
2012-01-17 01:51:45 +04:00
# define LM96163_REG_CONFIG_ENHANCED 0x45
2012-01-17 01:51:46 +04:00
# define LM63_MAX_CONVRATE 9
# define LM63_MAX_CONVRATE_HZ 32
# define LM96163_MAX_CONVRATE_HZ 26
2005-04-17 02:20:36 +04:00
/*
* Conversions and various macros
* For tachometer counts , the LM63 uses 16 - bit values .
* For local temperature and high limit , remote critical limit and hysteresis
2005-05-04 04:21:25 +04:00
* value , it uses signed 8 - bit values with LSB = 1 degree Celsius .
2005-04-17 02:20:36 +04:00
* For remote temperature , low and high limits , it uses signed 11 - bit values
2005-05-04 04:21:25 +04:00
* with LSB = 0.125 degree Celsius , left - justified in 16 - bit registers .
2011-02-09 12:51:34 +03:00
* For LM64 the actual remote diode temperature is 16 degree Celsius higher
* than the register reading . Remote temperature setpoints have to be
* adapted accordingly .
2005-04-17 02:20:36 +04:00
*/
# define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \
5400000 / ( reg ) )
# define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \
( 5400000 / ( val ) ) & 0xFFFC )
# define TEMP8_FROM_REG(reg) ((reg) * 1000)
2014-06-25 19:08:35 +04:00
# define TEMP8_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
127000 ) , 1000 )
# define TEMP8U_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, \
255000 ) , 1000 )
2005-04-17 02:20:36 +04:00
# define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
2014-06-25 19:08:35 +04:00
# define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
127875 ) , 125 ) * 32 )
# define TEMP11U_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), 0, \
255875 ) , 125 ) * 32 )
# define HYST_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, 127000), \
1000 )
2005-04-17 02:20:36 +04:00
2012-01-17 01:51:46 +04:00
# define UPDATE_INTERVAL(max, rate) \
( ( 1000 < < ( LM63_MAX_CONVRATE - ( rate ) ) ) / ( max ) )
2012-01-17 01:51:45 +04:00
enum chips { lm63 , lm64 , lm96163 } ;
2010-05-27 21:58:38 +04:00
2005-04-17 02:20:36 +04:00
/*
* Client data ( each client gets its own )
*/
struct lm63_data {
2014-04-04 20:01:33 +04:00
struct i2c_client * client ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2014-04-04 20:01:33 +04:00
const struct attribute_group * groups [ 5 ] ;
2005-04-17 02:20:36 +04:00
char valid ; /* zero until following fields are valid */
2012-01-17 01:51:47 +04:00
char lut_valid ; /* zero until lut fields are valid */
2005-04-17 02:20:36 +04:00
unsigned long last_updated ; /* in jiffies */
2012-01-17 01:51:47 +04:00
unsigned long lut_last_updated ; /* in jiffies */
2012-01-17 01:51:46 +04:00
enum chips kind ;
2011-02-09 12:51:34 +03:00
int temp2_offset ;
2005-04-17 02:20:36 +04:00
2012-01-17 01:51:46 +04:00
int update_interval ; /* in milliseconds */
int max_convrate_hz ;
2012-01-17 01:51:47 +04:00
int lut_size ; /* 8 or 12 */
2012-01-17 01:51:46 +04:00
2005-04-17 02:20:36 +04:00
/* registers values */
u8 config , config_fan ;
2005-06-05 22:32:27 +04:00
u16 fan [ 2 ] ; /* 0: input
1 : low limit */
2005-04-17 02:20:36 +04:00
u8 pwm1_freq ;
2012-01-17 01:51:47 +04:00
u8 pwm1 [ 13 ] ; /* 0: current output
1 - 12 : lookup table */
s8 temp8 [ 15 ] ; /* 0: local input
2005-06-05 22:32:27 +04:00
1 : local high limit
2012-01-17 01:51:47 +04:00
2 : remote critical limit
2012-01-17 01:51:47 +04:00
3 - 14 : lookup table */
2012-01-17 01:51:45 +04:00
s16 temp11 [ 4 ] ; /* 0: remote input
2005-06-05 22:32:27 +04:00
1 : remote low limit
2012-01-17 01:51:45 +04:00
2 : remote high limit
3 : remote offset */
2012-01-17 01:51:46 +04:00
u16 temp11u ; /* remote input (unsigned) */
2005-04-17 02:20:36 +04:00
u8 temp2_crit_hyst ;
2012-01-17 01:51:47 +04:00
u8 lut_temp_hyst ;
2005-04-17 02:20:36 +04:00
u8 alarms ;
2012-01-17 01:51:45 +04:00
bool pwm_highres ;
2012-01-17 01:51:47 +04:00
bool lut_temp_highres ;
2012-01-17 01:51:46 +04:00
bool remote_unsigned ; /* true if unsigned remote upper limits */
2012-01-17 01:51:46 +04:00
bool trutherm ;
2005-04-17 02:20:36 +04:00
} ;
2012-01-17 01:51:46 +04:00
static inline int temp8_from_reg ( struct lm63_data * data , int nr )
{
if ( data - > remote_unsigned )
return TEMP8_FROM_REG ( ( u8 ) data - > temp8 [ nr ] ) ;
return TEMP8_FROM_REG ( data - > temp8 [ nr ] ) ;
}
2012-01-17 01:51:47 +04:00
static inline int lut_temp_from_reg ( struct lm63_data * data , int nr )
{
return data - > temp8 [ nr ] * ( data - > lut_temp_highres ? 500 : 1000 ) ;
}
2012-03-23 13:02:19 +04:00
static inline int lut_temp_to_reg ( struct lm63_data * data , long val )
{
val - = data - > temp2_offset ;
if ( data - > lut_temp_highres )
2013-01-09 20:09:34 +04:00
return DIV_ROUND_CLOSEST ( clamp_val ( val , 0 , 127500 ) , 500 ) ;
2012-03-23 13:02:19 +04:00
else
2013-01-09 20:09:34 +04:00
return DIV_ROUND_CLOSEST ( clamp_val ( val , 0 , 127000 ) , 1000 ) ;
2012-03-23 13:02:19 +04:00
}
2012-03-23 13:02:19 +04:00
/*
* Update the lookup table register cache .
* client - > update_lock must be held when calling this function .
*/
2014-04-04 20:01:33 +04:00
static void lm63_update_lut ( struct lm63_data * data )
2012-03-23 13:02:19 +04:00
{
2014-04-04 20:01:33 +04:00
struct i2c_client * client = data - > client ;
2012-03-23 13:02:19 +04:00
int i ;
if ( time_after ( jiffies , data - > lut_last_updated + 5 * HZ ) | |
! data - > lut_valid ) {
for ( i = 0 ; i < data - > lut_size ; i + + ) {
data - > pwm1 [ 1 + i ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_LUT_PWM ( i ) ) ;
data - > temp8 [ 3 + i ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_LUT_TEMP ( i ) ) ;
}
data - > lut_temp_hyst = i2c_smbus_read_byte_data ( client ,
LM63_REG_LUT_TEMP_HYST ) ;
data - > lut_last_updated = jiffies ;
data - > lut_valid = 1 ;
}
}
2012-03-23 13:02:18 +04:00
static struct lm63_data * lm63_update_device ( struct device * dev )
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-03-23 13:02:18 +04:00
unsigned long next_update ;
mutex_lock ( & data - > update_lock ) ;
2013-07-08 16:18:24 +04:00
next_update = data - > last_updated +
msecs_to_jiffies ( data - > update_interval ) ;
2012-03-23 13:02:18 +04:00
if ( time_after ( jiffies , next_update ) | | ! data - > valid ) {
if ( data - > config & 0x04 ) { /* tachometer enabled */
/* order matters for fan1_input */
data - > fan [ 0 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_TACH_COUNT_LSB ) & 0xFC ;
data - > fan [ 0 ] | = i2c_smbus_read_byte_data ( client ,
LM63_REG_TACH_COUNT_MSB ) < < 8 ;
data - > fan [ 1 ] = ( i2c_smbus_read_byte_data ( client ,
LM63_REG_TACH_LIMIT_LSB ) & 0xFC )
| ( i2c_smbus_read_byte_data ( client ,
LM63_REG_TACH_LIMIT_MSB ) < < 8 ) ;
}
data - > pwm1_freq = i2c_smbus_read_byte_data ( client ,
LM63_REG_PWM_FREQ ) ;
if ( data - > pwm1_freq = = 0 )
data - > pwm1_freq = 1 ;
data - > pwm1 [ 0 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_PWM_VALUE ) ;
data - > temp8 [ 0 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_LOCAL_TEMP ) ;
data - > temp8 [ 1 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_LOCAL_HIGH ) ;
/* order matters for temp2_input */
data - > temp11 [ 0 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_TEMP_MSB ) < < 8 ;
data - > temp11 [ 0 ] | = i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_TEMP_LSB ) ;
data - > temp11 [ 1 ] = ( i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_LOW_MSB ) < < 8 )
| i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_LOW_LSB ) ;
data - > temp11 [ 2 ] = ( i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_HIGH_MSB ) < < 8 )
| i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_HIGH_LSB ) ;
data - > temp11 [ 3 ] = ( i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_OFFSET_MSB ) < < 8 )
| i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_OFFSET_LSB ) ;
if ( data - > kind = = lm96163 )
data - > temp11u = ( i2c_smbus_read_byte_data ( client ,
LM96163_REG_REMOTE_TEMP_U_MSB ) < < 8 )
| i2c_smbus_read_byte_data ( client ,
LM96163_REG_REMOTE_TEMP_U_LSB ) ;
data - > temp8 [ 2 ] = i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_TCRIT ) ;
data - > temp2_crit_hyst = i2c_smbus_read_byte_data ( client ,
LM63_REG_REMOTE_TCRIT_HYST ) ;
data - > alarms = i2c_smbus_read_byte_data ( client ,
LM63_REG_ALERT_STATUS ) & 0x7F ;
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
2014-04-04 20:01:33 +04:00
lm63_update_lut ( data ) ;
2012-03-23 13:02:18 +04:00
mutex_unlock ( & data - > update_lock ) ;
return data ;
}
2012-03-23 13:02:19 +04:00
/*
* Trip points in the lookup table should be in ascending order for both
* temperatures and PWM output values .
*/
2014-04-04 20:01:33 +04:00
static int lm63_lut_looks_bad ( struct device * dev , struct lm63_data * data )
2012-03-23 13:02:19 +04:00
{
int i ;
mutex_lock ( & data - > update_lock ) ;
2014-04-04 20:01:33 +04:00
lm63_update_lut ( data ) ;
2012-03-23 13:02:19 +04:00
for ( i = 1 ; i < data - > lut_size ; i + + ) {
if ( data - > pwm1 [ 1 + i - 1 ] > data - > pwm1 [ 1 + i ]
| | data - > temp8 [ 3 + i - 1 ] > data - > temp8 [ 3 + i ] ) {
2014-04-04 20:01:33 +04:00
dev_warn ( dev ,
2012-03-23 13:02:19 +04:00
" Lookup table doesn't look sane (check entries %d and %d) \n " ,
i , i + 1 ) ;
break ;
}
}
mutex_unlock ( & data - > update_lock ) ;
return i = = data - > lut_size ? 0 : 1 ;
}
2005-04-17 02:20:36 +04:00
/*
* Sysfs callback functions and files
*/
2005-06-05 22:32:27 +04:00
static ssize_t show_fan ( struct device * dev , struct device_attribute * devattr ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-05 22:32:27 +04:00
static ssize_t set_fan ( struct device * dev , struct device_attribute * dummy ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-17 01:51:45 +04:00
unsigned long val ;
int err ;
err = kstrtoul ( buf , 10 , & val ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-06-05 22:32:27 +04:00
data - > fan [ 1 ] = FAN_TO_REG ( val ) ;
2005-04-17 02:20:36 +04:00
i2c_smbus_write_byte_data ( client , LM63_REG_TACH_LIMIT_LSB ,
2005-06-05 22:32:27 +04:00
data - > fan [ 1 ] & 0xFF ) ;
2005-04-17 02:20:36 +04:00
i2c_smbus_write_byte_data ( client , LM63_REG_TACH_LIMIT_MSB ,
2005-06-05 22:32:27 +04:00
data - > fan [ 1 ] > > 8 ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2012-01-17 01:51:47 +04:00
static ssize_t show_pwm1 ( struct device * dev , struct device_attribute * devattr ,
2005-06-05 22:32:27 +04:00
char * buf )
2005-04-17 02:20:36 +04:00
{
2012-01-17 01:51:47 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2005-04-17 02:20:36 +04:00
struct lm63_data * data = lm63_update_device ( dev ) ;
2012-01-17 01:51:47 +04:00
int nr = attr - > index ;
2012-01-17 01:51:45 +04:00
int pwm ;
if ( data - > pwm_highres )
2012-01-17 01:51:47 +04:00
pwm = data - > pwm1 [ nr ] ;
2012-01-17 01:51:45 +04:00
else
2012-01-17 01:51:47 +04:00
pwm = data - > pwm1 [ nr ] > = 2 * data - > pwm1_freq ?
255 : ( data - > pwm1 [ nr ] * 255 + data - > pwm1_freq ) /
2012-01-17 01:51:45 +04:00
( 2 * data - > pwm1_freq ) ;
return sprintf ( buf , " %d \n " , pwm ) ;
2005-04-17 02:20:36 +04:00
}
2012-03-23 13:02:19 +04:00
static ssize_t set_pwm1 ( struct device * dev , struct device_attribute * devattr ,
2005-06-05 22:32:27 +04:00
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2012-03-23 13:02:19 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-03-23 13:02:19 +04:00
int nr = attr - > index ;
2005-04-17 02:20:36 +04:00
unsigned long val ;
2012-01-17 01:51:45 +04:00
int err ;
2012-03-23 13:02:19 +04:00
u8 reg ;
2012-01-17 01:51:45 +04:00
2005-04-17 02:20:36 +04:00
if ( ! ( data - > config_fan & 0x20 ) ) /* register is read-only */
return - EPERM ;
2012-01-17 01:51:45 +04:00
err = kstrtoul ( buf , 10 , & val ) ;
if ( err )
return err ;
2012-03-23 13:02:19 +04:00
reg = nr ? LM63_REG_LUT_PWM ( nr - 1 ) : LM63_REG_PWM_VALUE ;
2013-01-09 20:09:34 +04:00
val = clamp_val ( val , 0 , 255 ) ;
2012-03-23 13:02:19 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2012-03-23 13:02:19 +04:00
data - > pwm1 [ nr ] = data - > pwm_highres ? val :
2012-01-17 01:51:47 +04:00
( val * data - > pwm1_freq * 2 + 127 ) / 255 ;
2012-03-23 13:02:19 +04:00
i2c_smbus_write_byte_data ( client , reg , data - > pwm1 [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2012-01-17 01:51:45 +04:00
static ssize_t show_pwm1_enable ( struct device * dev ,
struct device_attribute * dummy , char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , data - > config_fan & 0x20 ? 1 : 2 ) ;
}
2012-03-23 13:02:19 +04:00
static ssize_t set_pwm1_enable ( struct device * dev ,
struct device_attribute * dummy ,
const char * buf , size_t count )
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-03-23 13:02:19 +04:00
unsigned long val ;
int err ;
err = kstrtoul ( buf , 10 , & val ) ;
if ( err )
return err ;
if ( val < 1 | | val > 2 )
return - EINVAL ;
/*
* Only let the user switch to automatic mode if the lookup table
* looks sane .
*/
2014-04-04 20:01:33 +04:00
if ( val = = 2 & & lm63_lut_looks_bad ( dev , data ) )
2012-03-23 13:02:19 +04:00
return - EPERM ;
mutex_lock ( & data - > update_lock ) ;
data - > config_fan = i2c_smbus_read_byte_data ( client ,
LM63_REG_CONFIG_FAN ) ;
if ( val = = 1 )
data - > config_fan | = 0x20 ;
else
data - > config_fan & = ~ 0x20 ;
i2c_smbus_write_byte_data ( client , LM63_REG_CONFIG_FAN ,
2014-04-04 20:01:33 +04:00
data - > config_fan ) ;
2012-03-23 13:02:19 +04:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2011-02-09 12:51:34 +03:00
/*
* There are 8 bit registers for both local ( temp1 ) and remote ( temp2 ) sensor .
* For remote sensor registers temp2_offset has to be considered ,
* for local sensor it must not .
* So we need separate 8 bit accessors for local and remote sensor .
*/
static ssize_t show_local_temp8 ( struct device * dev ,
struct device_attribute * devattr ,
char * buf )
2005-06-05 22:32:27 +04:00
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP8_FROM_REG ( data - > temp8 [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-05 22:32:27 +04:00
2011-02-09 12:51:34 +03:00
static ssize_t show_remote_temp8 ( struct device * dev ,
struct device_attribute * devattr ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
2012-01-17 01:51:46 +04:00
return sprintf ( buf , " %d \n " , temp8_from_reg ( data , attr - > index )
2011-02-09 12:51:34 +03:00
+ data - > temp2_offset ) ;
}
2012-01-17 01:51:47 +04:00
static ssize_t show_lut_temp ( struct device * dev ,
struct device_attribute * devattr ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , lut_temp_from_reg ( data , attr - > index )
+ data - > temp2_offset ) ;
}
2012-01-17 01:51:46 +04:00
static ssize_t set_temp8 ( struct device * dev , struct device_attribute * devattr ,
const char * buf , size_t count )
2005-06-05 22:32:27 +04:00
{
2012-01-17 01:51:46 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-17 01:51:46 +04:00
int nr = attr - > index ;
2012-01-17 01:51:45 +04:00
long val ;
int err ;
2012-01-17 01:51:46 +04:00
int temp ;
2012-03-23 13:02:19 +04:00
u8 reg ;
2012-01-17 01:51:45 +04:00
err = kstrtol ( buf , 10 , & val ) ;
if ( err )
return err ;
2005-06-05 22:32:27 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2012-03-23 13:02:19 +04:00
switch ( nr ) {
case 2 :
reg = LM63_REG_REMOTE_TCRIT ;
2012-01-17 01:51:46 +04:00
if ( data - > remote_unsigned )
temp = TEMP8U_TO_REG ( val - data - > temp2_offset ) ;
else
temp = TEMP8_TO_REG ( val - data - > temp2_offset ) ;
2012-03-23 13:02:19 +04:00
break ;
case 1 :
reg = LM63_REG_LOCAL_HIGH ;
2012-01-17 01:51:46 +04:00
temp = TEMP8_TO_REG ( val ) ;
2012-03-23 13:02:19 +04:00
break ;
default : /* lookup table */
reg = LM63_REG_LUT_TEMP ( nr - 3 ) ;
temp = lut_temp_to_reg ( data , val ) ;
2012-01-17 01:51:46 +04:00
}
data - > temp8 [ nr ] = temp ;
i2c_smbus_write_byte_data ( client , reg , temp ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-06-05 22:32:27 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
2005-06-05 22:32:27 +04:00
static ssize_t show_temp11 ( struct device * dev , struct device_attribute * devattr ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
2012-01-17 01:51:46 +04:00
int nr = attr - > index ;
int temp ;
if ( ! nr ) {
/*
* Use unsigned temperature unless its value is zero .
* If it is zero , use signed temperature .
*/
if ( data - > temp11u )
temp = TEMP11_FROM_REG ( data - > temp11u ) ;
else
temp = TEMP11_FROM_REG ( data - > temp11 [ nr ] ) ;
} else {
if ( data - > remote_unsigned & & nr = = 2 )
temp = TEMP11_FROM_REG ( ( u16 ) data - > temp11 [ nr ] ) ;
else
temp = TEMP11_FROM_REG ( data - > temp11 [ nr ] ) ;
}
return sprintf ( buf , " %d \n " , temp + data - > temp2_offset ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-05 22:32:27 +04:00
static ssize_t set_temp11 ( struct device * dev , struct device_attribute * devattr ,
const char * buf , size_t count )
{
2012-01-17 01:51:45 +04:00
static const u8 reg [ 6 ] = {
2005-06-05 22:32:27 +04:00
LM63_REG_REMOTE_LOW_MSB ,
LM63_REG_REMOTE_LOW_LSB ,
LM63_REG_REMOTE_HIGH_MSB ,
LM63_REG_REMOTE_HIGH_LSB ,
2012-01-17 01:51:45 +04:00
LM63_REG_REMOTE_OFFSET_MSB ,
LM63_REG_REMOTE_OFFSET_LSB ,
2005-06-05 22:32:27 +04:00
} ;
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-17 01:51:45 +04:00
long val ;
int err ;
2005-06-05 22:32:27 +04:00
int nr = attr - > index ;
2012-01-17 01:51:45 +04:00
err = kstrtol ( buf , 10 , & val ) ;
if ( err )
return err ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2012-01-17 01:51:46 +04:00
if ( data - > remote_unsigned & & nr = = 2 )
data - > temp11 [ nr ] = TEMP11U_TO_REG ( val - data - > temp2_offset ) ;
else
data - > temp11 [ nr ] = TEMP11_TO_REG ( val - data - > temp2_offset ) ;
2005-06-05 22:32:27 +04:00
i2c_smbus_write_byte_data ( client , reg [ ( nr - 1 ) * 2 ] ,
data - > temp11 [ nr ] > > 8 ) ;
i2c_smbus_write_byte_data ( client , reg [ ( nr - 1 ) * 2 + 1 ] ,
data - > temp11 [ nr ] & 0xff ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-06-05 22:32:27 +04:00
return count ;
2005-04-17 02:20:36 +04:00
}
2012-01-17 01:51:45 +04:00
/*
* Hysteresis register holds a relative value , while we want to present
* an absolute to user - space
*/
static ssize_t show_temp2_crit_hyst ( struct device * dev ,
struct device_attribute * dummy , char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm63_data * data = lm63_update_device ( dev ) ;
2012-01-17 01:51:46 +04:00
return sprintf ( buf , " %d \n " , temp8_from_reg ( data , 2 )
2011-02-09 12:51:34 +03:00
+ data - > temp2_offset
2005-04-17 02:20:36 +04:00
- TEMP8_FROM_REG ( data - > temp2_crit_hyst ) ) ;
}
2012-01-17 01:51:47 +04:00
static ssize_t show_lut_temp_hyst ( struct device * dev ,
struct device_attribute * devattr , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , lut_temp_from_reg ( data , attr - > index )
+ data - > temp2_offset
- TEMP8_FROM_REG ( data - > lut_temp_hyst ) ) ;
}
2012-01-17 01:51:45 +04:00
/*
* And now the other way around , user - space provides an absolute
* hysteresis value and we have to store a relative one
*/
static ssize_t set_temp2_crit_hyst ( struct device * dev ,
struct device_attribute * dummy ,
2005-06-05 22:32:27 +04:00
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-17 01:51:45 +04:00
long val ;
int err ;
2005-04-17 02:20:36 +04:00
long hyst ;
2012-01-17 01:51:45 +04:00
err = kstrtol ( buf , 10 , & val ) ;
if ( err )
return err ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2012-01-17 01:51:46 +04:00
hyst = temp8_from_reg ( data , 2 ) + data - > temp2_offset - val ;
2005-04-17 02:20:36 +04:00
i2c_smbus_write_byte_data ( client , LM63_REG_REMOTE_TCRIT_HYST ,
HYST_TO_REG ( hyst ) ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2012-01-17 01:51:46 +04:00
/*
* Set conversion rate .
* client - > update_lock must be held when calling this function .
*/
2014-04-04 20:01:33 +04:00
static void lm63_set_convrate ( struct lm63_data * data , unsigned int interval )
2012-01-17 01:51:46 +04:00
{
2014-04-04 20:01:33 +04:00
struct i2c_client * client = data - > client ;
2012-01-17 01:51:46 +04:00
unsigned int update_interval ;
2014-04-04 20:01:33 +04:00
int i ;
2012-01-17 01:51:46 +04:00
/* Shift calculations to avoid rounding errors */
interval < < = 6 ;
/* find the nearest update rate */
update_interval = ( 1 < < ( LM63_MAX_CONVRATE + 6 ) ) * 1000
/ data - > max_convrate_hz ;
for ( i = 0 ; i < LM63_MAX_CONVRATE ; i + + , update_interval > > = 1 )
if ( interval > = update_interval * 3 / 4 )
break ;
i2c_smbus_write_byte_data ( client , LM63_REG_CONVRATE , i ) ;
data - > update_interval = UPDATE_INTERVAL ( data - > max_convrate_hz , i ) ;
}
static ssize_t show_update_interval ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct lm63_data * data = dev_get_drvdata ( dev ) ;
return sprintf ( buf , " %u \n " , data - > update_interval ) ;
}
static ssize_t set_update_interval ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
2012-01-17 01:51:46 +04:00
unsigned long val ;
int err ;
err = kstrtoul ( buf , 10 , & val ) ;
if ( err )
return err ;
mutex_lock ( & data - > update_lock ) ;
2014-04-04 20:01:33 +04:00
lm63_set_convrate ( data , clamp_val ( val , 0 , 100000 ) ) ;
2012-01-17 01:51:46 +04:00
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2012-01-17 01:51:46 +04:00
static ssize_t show_type ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
2012-01-17 01:51:46 +04:00
return sprintf ( buf , data - > trutherm ? " 1 \n " : " 2 \n " ) ;
}
static ssize_t set_type ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-01-17 01:51:46 +04:00
unsigned long val ;
int ret ;
u8 reg ;
ret = kstrtoul ( buf , 10 , & val ) ;
if ( ret < 0 )
return ret ;
if ( val ! = 1 & & val ! = 2 )
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
data - > trutherm = val = = 1 ;
reg = i2c_smbus_read_byte_data ( client , LM96163_REG_TRUTHERM ) & ~ 0x02 ;
i2c_smbus_write_byte_data ( client , LM96163_REG_TRUTHERM ,
reg | ( data - > trutherm ? 0x02 : 0x00 ) ) ;
data - > valid = 0 ;
mutex_unlock ( & data - > update_lock ) ;
return count ;
}
2005-06-05 22:32:27 +04:00
static ssize_t show_alarms ( struct device * dev , struct device_attribute * dummy ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm63_data * data = lm63_update_device ( dev ) ;
return sprintf ( buf , " %u \n " , data - > alarms ) ;
}
2006-09-24 22:52:15 +04:00
static ssize_t show_alarm ( struct device * dev , struct device_attribute * devattr ,
char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( devattr ) ;
struct lm63_data * data = lm63_update_device ( dev ) ;
int bitnr = attr - > index ;
return sprintf ( buf , " %u \n " , ( data - > alarms > > bitnr ) & 1 ) ;
}
2005-06-05 22:32:27 +04:00
static SENSOR_DEVICE_ATTR ( fan1_input , S_IRUGO , show_fan , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan1_min , S_IWUSR | S_IRUGO , show_fan ,
set_fan , 1 ) ;
2005-04-17 02:20:36 +04:00
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1 , S_IWUSR | S_IRUGO , show_pwm1 , set_pwm1 , 0 ) ;
2012-03-23 13:02:19 +04:00
static DEVICE_ATTR ( pwm1_enable , S_IWUSR | S_IRUGO ,
show_pwm1_enable , set_pwm1_enable ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point1_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 1 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point1_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 3 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point1_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 3 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point2_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 2 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point2_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 4 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point2_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 4 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point3_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 3 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point3_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 5 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point3_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 5 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point4_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 4 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point4_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 6 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point4_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 6 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point5_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 5 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point5_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 7 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point5_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 7 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point6_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 6 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point6_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 8 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point6_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 8 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point7_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 7 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point7_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 9 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point7_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 9 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point8_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 8 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point8_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 10 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point8_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 10 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point9_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 9 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point9_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 11 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point9_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 11 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point10_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 10 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point10_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 12 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point10_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 12 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point11_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 11 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point11_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 13 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point11_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 13 ) ;
2012-03-23 13:02:19 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point12_pwm , S_IWUSR | S_IRUGO ,
show_pwm1 , set_pwm1 , 12 ) ;
static SENSOR_DEVICE_ATTR ( pwm1_auto_point12_temp , S_IWUSR | S_IRUGO ,
show_lut_temp , set_temp8 , 14 ) ;
2012-01-17 01:51:47 +04:00
static SENSOR_DEVICE_ATTR ( pwm1_auto_point12_temp_hyst , S_IRUGO ,
show_lut_temp_hyst , NULL , 14 ) ;
2005-04-17 02:20:36 +04:00
2011-02-09 12:51:34 +03:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , show_local_temp8 , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IWUSR | S_IRUGO , show_local_temp8 ,
2012-01-17 01:51:46 +04:00
set_temp8 , 1 ) ;
2005-04-17 02:20:36 +04:00
2005-06-05 22:32:27 +04:00
static SENSOR_DEVICE_ATTR ( temp2_input , S_IRUGO , show_temp11 , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_min , S_IWUSR | S_IRUGO , show_temp11 ,
set_temp11 , 1 ) ;
static SENSOR_DEVICE_ATTR ( temp2_max , S_IWUSR | S_IRUGO , show_temp11 ,
set_temp11 , 2 ) ;
2012-01-17 01:51:45 +04:00
static SENSOR_DEVICE_ATTR ( temp2_offset , S_IWUSR | S_IRUGO , show_temp11 ,
set_temp11 , 3 ) ;
2011-02-09 12:51:34 +03:00
static SENSOR_DEVICE_ATTR ( temp2_crit , S_IRUGO , show_remote_temp8 ,
2012-01-17 01:51:46 +04:00
set_temp8 , 2 ) ;
2005-04-17 02:20:36 +04:00
static DEVICE_ATTR ( temp2_crit_hyst , S_IWUSR | S_IRUGO , show_temp2_crit_hyst ,
set_temp2_crit_hyst ) ;
2012-01-17 01:51:46 +04:00
static DEVICE_ATTR ( temp2_type , S_IWUSR | S_IRUGO , show_type , set_type ) ;
2006-09-24 22:52:15 +04:00
/* Individual alarm files */
static SENSOR_DEVICE_ATTR ( fan1_min_alarm , S_IRUGO , show_alarm , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_crit_alarm , S_IRUGO , show_alarm , NULL , 1 ) ;
2007-06-09 18:11:16 +04:00
static SENSOR_DEVICE_ATTR ( temp2_fault , S_IRUGO , show_alarm , NULL , 2 ) ;
2006-09-24 22:52:15 +04:00
static SENSOR_DEVICE_ATTR ( temp2_min_alarm , S_IRUGO , show_alarm , NULL , 3 ) ;
static SENSOR_DEVICE_ATTR ( temp2_max_alarm , S_IRUGO , show_alarm , NULL , 4 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_alarm , S_IRUGO , show_alarm , NULL , 6 ) ;
/* Raw alarm file for compatibility */
2005-04-17 02:20:36 +04:00
static DEVICE_ATTR ( alarms , S_IRUGO , show_alarms , NULL ) ;
2012-01-17 01:51:46 +04:00
static DEVICE_ATTR ( update_interval , S_IRUGO | S_IWUSR , show_update_interval ,
set_update_interval ) ;
2006-09-24 23:16:40 +04:00
static struct attribute * lm63_attributes [ ] = {
2012-01-17 01:51:47 +04:00
& sensor_dev_attr_pwm1 . dev_attr . attr ,
2006-09-24 23:16:40 +04:00
& dev_attr_pwm1_enable . attr ,
2012-01-17 01:51:47 +04:00
& sensor_dev_attr_pwm1_auto_point1_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point1_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point1_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point2_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point2_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point2_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point3_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point3_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point3_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point4_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point4_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point4_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point5_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point5_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point5_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point6_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point6_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point6_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point7_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point7_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point7_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point8_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point8_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point8_temp_hyst . dev_attr . attr ,
2006-09-24 23:16:40 +04:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp2_min . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp2_max . dev_attr . attr ,
2012-01-17 01:51:45 +04:00
& sensor_dev_attr_temp2_offset . dev_attr . attr ,
2006-09-24 23:16:40 +04:00
& sensor_dev_attr_temp2_crit . dev_attr . attr ,
& dev_attr_temp2_crit_hyst . attr ,
& sensor_dev_attr_temp2_crit_alarm . dev_attr . attr ,
2007-06-09 18:11:16 +04:00
& sensor_dev_attr_temp2_fault . dev_attr . attr ,
2006-09-24 23:16:40 +04:00
& sensor_dev_attr_temp2_min_alarm . dev_attr . attr ,
& sensor_dev_attr_temp2_max_alarm . dev_attr . attr ,
& sensor_dev_attr_temp1_max_alarm . dev_attr . attr ,
& dev_attr_alarms . attr ,
2012-01-17 01:51:46 +04:00
& dev_attr_update_interval . attr ,
2006-09-24 23:16:40 +04:00
NULL
} ;
2014-04-04 20:01:32 +04:00
static struct attribute * lm63_attributes_temp2_type [ ] = {
& dev_attr_temp2_type . attr ,
NULL
} ;
static const struct attribute_group lm63_group_temp2_type = {
. attrs = lm63_attributes_temp2_type ,
} ;
2012-01-17 01:51:47 +04:00
static struct attribute * lm63_attributes_extra_lut [ ] = {
& sensor_dev_attr_pwm1_auto_point9_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point9_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point9_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point10_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point10_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point10_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point11_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point11_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point11_temp_hyst . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point12_pwm . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point12_temp . dev_attr . attr ,
& sensor_dev_attr_pwm1_auto_point12_temp_hyst . dev_attr . attr ,
NULL
} ;
static const struct attribute_group lm63_group_extra_lut = {
. attrs = lm63_attributes_extra_lut ,
} ;
2012-01-17 01:51:46 +04:00
/*
* On LM63 , temp2_crit can be set only once , which should be job
* of the bootloader .
* On LM64 , temp2_crit can always be set .
* On LM96163 , temp2_crit can be set if bit 1 of the configuration
* register is true .
*/
static umode_t lm63_attribute_mode ( struct kobject * kobj ,
struct attribute * attr , int index )
{
struct device * dev = container_of ( kobj , struct device , kobj ) ;
2014-04-04 20:01:33 +04:00
struct lm63_data * data = dev_get_drvdata ( dev ) ;
2012-01-17 01:51:46 +04:00
if ( attr = = & sensor_dev_attr_temp2_crit . dev_attr . attr
& & ( data - > kind = = lm64 | |
( data - > kind = = lm96163 & & ( data - > config & 0x02 ) ) ) )
return attr - > mode | S_IWUSR ;
return attr - > mode ;
}
2006-09-24 23:16:40 +04:00
static const struct attribute_group lm63_group = {
2012-01-17 01:51:46 +04:00
. is_visible = lm63_attribute_mode ,
2006-09-24 23:16:40 +04:00
. attrs = lm63_attributes ,
} ;
static struct attribute * lm63_attributes_fan1 [ ] = {
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_fan1_min . dev_attr . attr ,
& sensor_dev_attr_fan1_min_alarm . dev_attr . attr ,
NULL
} ;
static const struct attribute_group lm63_group_fan1 = {
. attrs = lm63_attributes_fan1 ,
} ;
2005-04-17 02:20:36 +04:00
/*
* Real code
*/
2008-07-16 21:30:13 +04:00
/* Return 0 if detection is successful, -ENODEV otherwise */
2012-03-23 13:02:18 +04:00
static int lm63_detect ( struct i2c_client * client ,
2008-07-16 21:30:13 +04:00
struct i2c_board_info * info )
2005-04-17 02:20:36 +04:00
{
2012-03-23 13:02:18 +04:00
struct i2c_adapter * adapter = client - > adapter ;
2009-12-09 22:35:57 +03:00
u8 man_id , chip_id , reg_config1 , reg_config2 ;
u8 reg_alert_status , reg_alert_mask ;
2012-03-23 13:02:18 +04:00
int address = client - > addr ;
2005-04-17 02:20:36 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
2008-07-16 21:30:13 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2012-03-23 13:02:18 +04:00
man_id = i2c_smbus_read_byte_data ( client , LM63_REG_MAN_ID ) ;
chip_id = i2c_smbus_read_byte_data ( client , LM63_REG_CHIP_ID ) ;
2009-12-09 22:35:57 +03:00
2012-03-23 13:02:18 +04:00
reg_config1 = i2c_smbus_read_byte_data ( client , LM63_REG_CONFIG1 ) ;
reg_config2 = i2c_smbus_read_byte_data ( client , LM63_REG_CONFIG2 ) ;
reg_alert_status = i2c_smbus_read_byte_data ( client ,
2009-12-09 22:35:57 +03:00
LM63_REG_ALERT_STATUS ) ;
2012-03-23 13:02:18 +04:00
reg_alert_mask = i2c_smbus_read_byte_data ( client , LM63_REG_ALERT_MASK ) ;
2009-12-09 22:35:57 +03:00
if ( man_id ! = 0x01 /* National Semiconductor */
| | ( reg_config1 & 0x18 ) ! = 0x00
| | ( reg_config2 & 0xF8 ) ! = 0x00
| | ( reg_alert_status & 0x20 ) ! = 0x00
| | ( reg_alert_mask & 0xA4 ) ! = 0xA4 ) {
dev_dbg ( & adapter - > dev ,
" Unsupported chip (man_id=0x%02X, chip_id=0x%02X) \n " ,
man_id , chip_id ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2010-05-27 21:58:38 +04:00
if ( chip_id = = 0x41 & & address = = 0x4c )
strlcpy ( info - > type , " lm63 " , I2C_NAME_SIZE ) ;
else if ( chip_id = = 0x51 & & ( address = = 0x18 | | address = = 0x4e ) )
strlcpy ( info - > type , " lm64 " , I2C_NAME_SIZE ) ;
2012-01-17 01:51:45 +04:00
else if ( chip_id = = 0x49 & & address = = 0x4c )
strlcpy ( info - > type , " lm96163 " , I2C_NAME_SIZE ) ;
2010-05-27 21:58:38 +04:00
else
return - ENODEV ;
2008-07-16 21:30:13 +04:00
return 0 ;
}
2012-01-17 01:51:45 +04:00
/*
* Ideally we shouldn ' t have to initialize anything , since the BIOS
* should have taken care of everything
*/
2014-04-04 20:01:33 +04:00
static void lm63_init_client ( struct lm63_data * data )
2005-04-17 02:20:36 +04:00
{
2014-04-04 20:01:33 +04:00
struct i2c_client * client = data - > client ;
2014-04-04 20:01:33 +04:00
struct device * dev = & client - > dev ;
2012-01-17 01:51:46 +04:00
u8 convrate ;
2005-04-17 02:20:36 +04:00
data - > config = i2c_smbus_read_byte_data ( client , LM63_REG_CONFIG1 ) ;
data - > config_fan = i2c_smbus_read_byte_data ( client ,
LM63_REG_CONFIG_FAN ) ;
/* Start converting if needed */
if ( data - > config & 0x40 ) { /* standby */
2014-04-04 20:01:33 +04:00
dev_dbg ( dev , " Switching to operational mode \n " ) ;
2005-04-17 02:20:36 +04:00
data - > config & = 0xA7 ;
i2c_smbus_write_byte_data ( client , LM63_REG_CONFIG1 ,
data - > config ) ;
}
2012-01-17 01:51:46 +04:00
/* Tachometer is always enabled on LM64 */
if ( data - > kind = = lm64 )
data - > config | = 0x04 ;
2005-04-17 02:20:36 +04:00
/* We may need pwm1_freq before ever updating the client data */
data - > pwm1_freq = i2c_smbus_read_byte_data ( client , LM63_REG_PWM_FREQ ) ;
if ( data - > pwm1_freq = = 0 )
data - > pwm1_freq = 1 ;
2012-01-17 01:51:46 +04:00
switch ( data - > kind ) {
case lm63 :
case lm64 :
data - > max_convrate_hz = LM63_MAX_CONVRATE_HZ ;
2012-01-17 01:51:47 +04:00
data - > lut_size = 8 ;
2012-01-17 01:51:46 +04:00
break ;
case lm96163 :
data - > max_convrate_hz = LM96163_MAX_CONVRATE_HZ ;
2012-01-17 01:51:47 +04:00
data - > lut_size = 12 ;
2012-01-17 01:51:46 +04:00
data - > trutherm
= i2c_smbus_read_byte_data ( client ,
LM96163_REG_TRUTHERM ) & 0x02 ;
2012-01-17 01:51:46 +04:00
break ;
}
convrate = i2c_smbus_read_byte_data ( client , LM63_REG_CONVRATE ) ;
if ( unlikely ( convrate > LM63_MAX_CONVRATE ) )
convrate = LM63_MAX_CONVRATE ;
data - > update_interval = UPDATE_INTERVAL ( data - > max_convrate_hz ,
convrate ) ;
2012-01-17 01:51:45 +04:00
/*
2012-01-17 01:51:46 +04:00
* For LM96163 , check if high resolution PWM
* and unsigned temperature format is enabled .
2012-01-17 01:51:45 +04:00
*/
if ( data - > kind = = lm96163 ) {
u8 config_enhanced
= i2c_smbus_read_byte_data ( client ,
LM96163_REG_CONFIG_ENHANCED ) ;
2012-01-17 01:51:47 +04:00
if ( config_enhanced & 0x20 )
data - > lut_temp_highres = true ;
2012-01-17 01:51:45 +04:00
if ( ( config_enhanced & 0x10 )
& & ! ( data - > config_fan & 0x08 ) & & data - > pwm1_freq = = 8 )
data - > pwm_highres = true ;
if ( config_enhanced & 0x08 )
2012-01-17 01:51:46 +04:00
data - > remote_unsigned = true ;
2012-01-17 01:51:45 +04:00
}
2005-04-17 02:20:36 +04:00
/* Show some debug info about the LM63 configuration */
2012-01-17 01:51:46 +04:00
if ( data - > kind = = lm63 )
2014-04-04 20:01:33 +04:00
dev_dbg ( dev , " Alert/tach pin configured for %s \n " ,
2012-01-17 01:51:46 +04:00
( data - > config & 0x04 ) ? " tachometer input " :
" alert output " ) ;
2014-04-04 20:01:33 +04:00
dev_dbg ( dev , " PWM clock %s kHz, output frequency %u Hz \n " ,
2005-04-17 02:20:36 +04:00
( data - > config_fan & 0x08 ) ? " 1.4 " : " 360 " ,
( ( data - > config_fan & 0x08 ) ? 700 : 180000 ) / data - > pwm1_freq ) ;
2014-04-04 20:01:33 +04:00
dev_dbg ( dev , " PWM output active %s, %s mode \n " ,
2005-04-17 02:20:36 +04:00
( data - > config_fan & 0x10 ) ? " low " : " high " ,
( data - > config_fan & 0x20 ) ? " manual " : " auto " ) ;
}
2012-03-23 13:02:18 +04:00
static int lm63_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-04-17 02:20:36 +04:00
{
2014-04-04 20:01:33 +04:00
struct device * dev = & client - > dev ;
2014-04-04 20:01:33 +04:00
struct device * hwmon_dev ;
2012-03-23 13:02:18 +04:00
struct lm63_data * data ;
2014-04-04 20:01:33 +04:00
int groups = 0 ;
2005-04-17 02:20:36 +04:00
2014-04-04 20:01:33 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm63_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:08 +04:00
if ( ! data )
return - ENOMEM ;
2012-03-23 13:02:18 +04:00
2014-04-04 20:01:33 +04:00
data - > client = client ;
2012-03-23 13:02:18 +04:00
mutex_init ( & data - > update_lock ) ;
/* Set the device type */
data - > kind = id - > driver_data ;
if ( data - > kind = = lm64 )
data - > temp2_offset = 16000 ;
/* Initialize chip */
2014-04-04 20:01:33 +04:00
lm63_init_client ( data ) ;
2012-03-23 13:02:18 +04:00
/* Register sysfs hooks */
2014-04-04 20:01:33 +04:00
data - > groups [ groups + + ] = & lm63_group ;
if ( data - > config & 0x04 ) /* tachometer enabled */
data - > groups [ groups + + ] = & lm63_group_fan1 ;
2012-03-23 13:02:18 +04:00
2014-04-04 20:01:33 +04:00
if ( data - > kind = = lm96163 ) {
data - > groups [ groups + + ] = & lm63_group_temp2_type ;
data - > groups [ groups + + ] = & lm63_group_extra_lut ;
2012-03-23 13:02:18 +04:00
}
2014-04-04 20:01:33 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data , data - > groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2012-03-23 13:02:18 +04:00
}
2012-01-17 01:51:47 +04:00
2012-03-23 13:02:18 +04:00
/*
* Driver data ( common to all clients )
*/
2012-01-17 01:51:47 +04:00
2012-03-23 13:02:18 +04:00
static const struct i2c_device_id lm63_id [ ] = {
{ " lm63 " , lm63 } ,
{ " lm64 " , lm64 } ,
{ " lm96163 " , lm96163 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm63_id ) ;
2005-04-17 02:20:36 +04:00
2012-03-23 13:02:18 +04:00
static struct i2c_driver lm63_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " lm63 " ,
} ,
. probe = lm63_probe ,
. id_table = lm63_id ,
. detect = lm63_detect ,
. address_list = normal_i2c ,
} ;
2005-04-17 02:20:36 +04:00
2012-01-20 11:38:18 +04:00
module_i2c_driver ( lm63_driver ) ;
2005-04-17 02:20:36 +04:00
2014-01-29 23:40:08 +04:00
MODULE_AUTHOR ( " Jean Delvare <jdelvare@suse.de> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " LM63 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;