2005-04-17 02:20:36 +04:00
/*
gl520sm . c - Part of lm_sensors , Linux kernel modules for hardware
monitoring
Copyright ( c ) 1998 , 1999 Frodo Looijaard < frodol @ dds . nl > ,
2007-10-20 01:21:04 +04:00
Kyösti Mälkki < kmalkki @ cc . hut . fi >
2005-04-17 02:20:36 +04:00
Copyright ( c ) 2005 Maarten Deprez < maartendeprez @ users . sourceforge . net >
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>
2005-07-29 23:15:12 +04:00
# include <linux/jiffies.h>
2005-04-17 02:20:36 +04:00
# include <linux/i2c.h>
2005-07-16 05:39:18 +04:00
# include <linux/hwmon.h>
2007-11-05 01:45:14 +03:00
# include <linux/hwmon-sysfs.h>
2005-07-31 23:52:01 +04:00
# include <linux/hwmon-vid.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:17:13 +04:00
# include <linux/sysfs.h>
2005-04-17 02:20:36 +04:00
/* Type of the extra sensor */
static unsigned short extra_sensor_type ;
module_param ( extra_sensor_type , ushort , 0 ) ;
MODULE_PARM_DESC ( extra_sensor_type , " Type of extra sensor (0=autodetect, 1=temperature, 2=voltage) " ) ;
/* Addresses to scan */
static unsigned short normal_i2c [ ] = { 0x2c , 0x2d , I2C_CLIENT_END } ;
/* Insmod parameters */
2005-07-31 23:49:03 +04:00
I2C_CLIENT_INSMOD_1 ( gl520sm ) ;
2005-04-17 02:20:36 +04:00
2007-11-05 01:44:23 +03:00
/* Many GL520 constants specified below
2005-04-17 02:20:36 +04:00
One of the inputs can be configured as either temp or voltage .
2007-11-05 01:44:23 +03:00
That ' s why _TEMP2 and _IN4 access the same register
2005-04-17 02:20:36 +04:00
*/
/* The GL520 registers */
# define GL520_REG_CHIP_ID 0x00
# define GL520_REG_REVISION 0x01
# define GL520_REG_CONF 0x03
# define GL520_REG_MASK 0x11
# define GL520_REG_VID_INPUT 0x02
2007-11-05 01:44:52 +03:00
static const u8 GL520_REG_IN_INPUT [ ] = { 0x15 , 0x14 , 0x13 , 0x0d , 0x0e } ;
static const u8 GL520_REG_IN_LIMIT [ ] = { 0x0c , 0x09 , 0x0a , 0x0b } ;
static const u8 GL520_REG_IN_MIN [ ] = { 0x0c , 0x09 , 0x0a , 0x0b , 0x18 } ;
static const u8 GL520_REG_IN_MAX [ ] = { 0x0c , 0x09 , 0x0a , 0x0b , 0x17 } ;
static const u8 GL520_REG_TEMP_INPUT [ ] = { 0x04 , 0x0e } ;
static const u8 GL520_REG_TEMP_MAX [ ] = { 0x05 , 0x17 } ;
static const u8 GL520_REG_TEMP_MAX_HYST [ ] = { 0x06 , 0x18 } ;
2005-04-17 02:20:36 +04:00
# define GL520_REG_FAN_INPUT 0x07
# define GL520_REG_FAN_MIN 0x08
# define GL520_REG_FAN_DIV 0x0f
# define GL520_REG_FAN_OFF GL520_REG_FAN_DIV
# define GL520_REG_ALARMS 0x12
# define GL520_REG_BEEP_MASK 0x10
# define GL520_REG_BEEP_ENABLE GL520_REG_CONF
/*
* Function declarations
*/
static int gl520_attach_adapter ( struct i2c_adapter * adapter ) ;
static int gl520_detect ( struct i2c_adapter * adapter , int address , int kind ) ;
static void gl520_init_client ( struct i2c_client * client ) ;
static int gl520_detach_client ( struct i2c_client * client ) ;
static int gl520_read_value ( struct i2c_client * client , u8 reg ) ;
static int gl520_write_value ( struct i2c_client * client , u8 reg , u16 value ) ;
static struct gl520_data * gl520_update_device ( struct device * dev ) ;
/* Driver data */
static struct i2c_driver gl520_driver = {
2005-11-26 22:37:41 +03:00
. driver = {
. name = " gl520sm " ,
} ,
2005-04-17 02:20:36 +04:00
. attach_adapter = gl520_attach_adapter ,
. detach_client = gl520_detach_client ,
} ;
/* Client data */
struct gl520_data {
struct i2c_client client ;
2007-08-21 00:46:20 +04:00
struct device * hwmon_dev ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2005-04-17 02:20:36 +04:00
char valid ; /* zero until the following fields are valid */
unsigned long last_updated ; /* in jiffies */
u8 vid ;
u8 vrm ;
u8 in_input [ 5 ] ; /* [0] = VVD */
u8 in_min [ 5 ] ; /* [0] = VDD */
u8 in_max [ 5 ] ; /* [0] = VDD */
u8 fan_input [ 2 ] ;
u8 fan_min [ 2 ] ;
u8 fan_div [ 2 ] ;
u8 fan_off ;
u8 temp_input [ 2 ] ;
u8 temp_max [ 2 ] ;
u8 temp_max_hyst [ 2 ] ;
u8 alarms ;
u8 beep_enable ;
u8 beep_mask ;
u8 alarm_mask ;
u8 two_temps ;
} ;
/*
* Sysfs stuff
*/
2007-11-05 01:45:14 +03:00
static ssize_t get_cpu_vid ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %u \n " , vid_from_reg ( data - > vid , data - > vrm ) ) ;
}
2007-11-05 01:45:14 +03:00
static DEVICE_ATTR ( cpu0_vid , S_IRUGO , get_cpu_vid , NULL ) ;
2005-04-17 02:20:36 +04:00
# define VDD_FROM_REG(val) (((val)*95+2) / 4)
# define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47) / 95),0,255))
# define IN_FROM_REG(val) ((val)*19)
# define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9) / 19),0,255))
2007-11-05 01:45:14 +03:00
static ssize_t get_in_input ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
u8 r = data - > in_input [ n ] ;
if ( n = = 0 )
return sprintf ( buf , " %d \n " , VDD_FROM_REG ( r ) ) ;
else
return sprintf ( buf , " %d \n " , IN_FROM_REG ( r ) ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t get_in_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
u8 r = data - > in_min [ n ] ;
if ( n = = 0 )
return sprintf ( buf , " %d \n " , VDD_FROM_REG ( r ) ) ;
else
return sprintf ( buf , " %d \n " , IN_FROM_REG ( r ) ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t get_in_max ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
u8 r = data - > in_max [ n ] ;
if ( n = = 0 )
return sprintf ( buf , " %d \n " , VDD_FROM_REG ( r ) ) ;
else
return sprintf ( buf , " %d \n " , IN_FROM_REG ( r ) ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_in_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
long v = simple_strtol ( buf , NULL , 10 ) ;
u8 r ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
if ( n = = 0 )
r = VDD_TO_REG ( v ) ;
else
r = IN_TO_REG ( v ) ;
data - > in_min [ n ] = r ;
if ( n < 4 )
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_IN_MIN [ n ] ,
( gl520_read_value ( client , GL520_REG_IN_MIN [ n ] )
& ~ 0xff ) | r ) ;
2005-04-17 02:20:36 +04:00
else
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_IN_MIN [ n ] , r ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_in_max ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
long v = simple_strtol ( buf , NULL , 10 ) ;
u8 r ;
if ( n = = 0 )
r = VDD_TO_REG ( v ) ;
else
r = IN_TO_REG ( v ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > in_max [ n ] = r ;
if ( n < 4 )
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_IN_MAX [ n ] ,
( gl520_read_value ( client , GL520_REG_IN_MAX [ n ] )
& ~ 0xff00 ) | ( r < < 8 ) ) ;
2005-04-17 02:20:36 +04:00
else
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_IN_MAX [ n ] , r ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static SENSOR_DEVICE_ATTR ( in0_input , S_IRUGO , get_in_input , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( in1_input , S_IRUGO , get_in_input , NULL , 1 ) ;
static SENSOR_DEVICE_ATTR ( in2_input , S_IRUGO , get_in_input , NULL , 2 ) ;
static SENSOR_DEVICE_ATTR ( in3_input , S_IRUGO , get_in_input , NULL , 3 ) ;
static SENSOR_DEVICE_ATTR ( in4_input , S_IRUGO , get_in_input , NULL , 4 ) ;
static SENSOR_DEVICE_ATTR ( in0_min , S_IRUGO | S_IWUSR ,
get_in_min , set_in_min , 0 ) ;
static SENSOR_DEVICE_ATTR ( in1_min , S_IRUGO | S_IWUSR ,
get_in_min , set_in_min , 1 ) ;
static SENSOR_DEVICE_ATTR ( in2_min , S_IRUGO | S_IWUSR ,
get_in_min , set_in_min , 2 ) ;
static SENSOR_DEVICE_ATTR ( in3_min , S_IRUGO | S_IWUSR ,
get_in_min , set_in_min , 3 ) ;
static SENSOR_DEVICE_ATTR ( in4_min , S_IRUGO | S_IWUSR ,
get_in_min , set_in_min , 4 ) ;
static SENSOR_DEVICE_ATTR ( in0_max , S_IRUGO | S_IWUSR ,
get_in_max , set_in_max , 0 ) ;
static SENSOR_DEVICE_ATTR ( in1_max , S_IRUGO | S_IWUSR ,
get_in_max , set_in_max , 1 ) ;
static SENSOR_DEVICE_ATTR ( in2_max , S_IRUGO | S_IWUSR ,
get_in_max , set_in_max , 2 ) ;
static SENSOR_DEVICE_ATTR ( in3_max , S_IRUGO | S_IWUSR ,
get_in_max , set_in_max , 3 ) ;
static SENSOR_DEVICE_ATTR ( in4_max , S_IRUGO | S_IWUSR ,
get_in_max , set_in_max , 4 ) ;
2005-04-17 02:20:36 +04:00
# define DIV_FROM_REG(val) (1 << (val))
# define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000 / ((val) << (div))))
# define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255));
2007-11-05 01:45:14 +03:00
static ssize_t get_fan_input ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan_input [ n ] ,
data - > fan_div [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t get_fan_min ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan_min [ n ] ,
data - > fan_div [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t get_fan_div ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , DIV_FROM_REG ( data - > fan_div [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t get_fan_off ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , data - > fan_off ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_fan_min ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
unsigned long v = simple_strtoul ( buf , NULL , 10 ) ;
u8 r ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2007-11-05 01:45:14 +03:00
r = FAN_TO_REG ( v , data - > fan_div [ n ] ) ;
data - > fan_min [ n ] = r ;
2005-04-17 02:20:36 +04:00
2007-11-05 01:45:14 +03:00
if ( n = = 0 )
gl520_write_value ( client , GL520_REG_FAN_MIN ,
( gl520_read_value ( client , GL520_REG_FAN_MIN )
& ~ 0xff00 ) | ( r < < 8 ) ) ;
2005-04-17 02:20:36 +04:00
else
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_FAN_MIN ,
( gl520_read_value ( client , GL520_REG_FAN_MIN )
& ~ 0xff ) | r ) ;
2005-04-17 02:20:36 +04:00
data - > beep_mask = gl520_read_value ( client , GL520_REG_BEEP_MASK ) ;
2007-11-05 01:45:14 +03:00
if ( data - > fan_min [ n ] = = 0 )
data - > alarm_mask & = ( n = = 0 ) ? ~ 0x20 : ~ 0x40 ;
2005-04-17 02:20:36 +04:00
else
2007-11-05 01:45:14 +03:00
data - > alarm_mask | = ( n = = 0 ) ? 0x20 : 0x40 ;
2005-04-17 02:20:36 +04:00
data - > beep_mask & = data - > alarm_mask ;
gl520_write_value ( client , GL520_REG_BEEP_MASK , data - > beep_mask ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_fan_div ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
unsigned long v = simple_strtoul ( buf , NULL , 10 ) ;
u8 r ;
switch ( v ) {
case 1 : r = 0 ; break ;
case 2 : r = 1 ; break ;
case 4 : r = 2 ; break ;
case 8 : r = 3 ; break ;
default :
dev_err ( & client - > dev , " fan_div value %ld not supported. Choose one of 1, 2, 4 or 8! \n " , v ) ;
return - EINVAL ;
}
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2007-11-05 01:45:14 +03:00
data - > fan_div [ n ] = r ;
2005-04-17 02:20:36 +04:00
2007-11-05 01:45:14 +03:00
if ( n = = 0 )
gl520_write_value ( client , GL520_REG_FAN_DIV ,
( gl520_read_value ( client , GL520_REG_FAN_DIV )
& ~ 0xc0 ) | ( r < < 6 ) ) ;
2005-04-17 02:20:36 +04:00
else
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_FAN_DIV ,
( gl520_read_value ( client , GL520_REG_FAN_DIV )
& ~ 0x30 ) | ( r < < 4 ) ) ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_fan_off ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
u8 r = simple_strtoul ( buf , NULL , 10 ) ? 1 : 0 ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > fan_off = r ;
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_FAN_OFF ,
( gl520_read_value ( client , GL520_REG_FAN_OFF )
& ~ 0x0c ) | ( r < < 2 ) ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static SENSOR_DEVICE_ATTR ( fan1_input , S_IRUGO , get_fan_input , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan2_input , S_IRUGO , get_fan_input , NULL , 1 ) ;
static SENSOR_DEVICE_ATTR ( fan1_min , S_IRUGO | S_IWUSR ,
get_fan_min , set_fan_min , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan2_min , S_IRUGO | S_IWUSR ,
get_fan_min , set_fan_min , 1 ) ;
static SENSOR_DEVICE_ATTR ( fan1_div , S_IRUGO | S_IWUSR ,
get_fan_div , set_fan_div , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan2_div , S_IRUGO | S_IWUSR ,
get_fan_div , set_fan_div , 1 ) ;
static DEVICE_ATTR ( fan1_off , S_IRUGO | S_IWUSR ,
get_fan_off , set_fan_off ) ;
2005-04-17 02:20:36 +04:00
# define TEMP_FROM_REG(val) (((val) - 130) * 1000)
# define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255))
2007-11-05 01:45:14 +03:00
static ssize_t get_temp_input ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_input [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t get_temp_max ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_max [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t get_temp_max_hyst ( struct device * dev , struct device_attribute
* attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
int n = to_sensor_dev_attr ( attr ) - > index ;
struct gl520_data * data = gl520_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_max_hyst [ n ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-05 01:45:14 +03:00
static ssize_t set_temp_max ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
long v = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2007-11-05 01:45:14 +03:00
data - > temp_max [ n ] = TEMP_TO_REG ( v ) ;
gl520_write_value ( client , GL520_REG_TEMP_MAX [ n ] , data - > temp_max [ n ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_temp_max_hyst ( struct device * dev , struct device_attribute
* attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
int n = to_sensor_dev_attr ( attr ) - > index ;
2005-04-17 02:20:36 +04:00
long v = simple_strtol ( buf , NULL , 10 ) ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2007-11-05 01:45:14 +03:00
data - > temp_max_hyst [ n ] = TEMP_TO_REG ( v ) ;
gl520_write_value ( client , GL520_REG_TEMP_MAX_HYST [ n ] ,
data - > temp_max_hyst [ n ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static SENSOR_DEVICE_ATTR ( temp1_input , S_IRUGO , get_temp_input , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_input , S_IRUGO , get_temp_input , NULL , 1 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max , S_IRUGO | S_IWUSR ,
get_temp_max , set_temp_max , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_max , S_IRUGO | S_IWUSR ,
get_temp_max , set_temp_max , 1 ) ;
static SENSOR_DEVICE_ATTR ( temp1_max_hyst , S_IRUGO | S_IWUSR ,
get_temp_max_hyst , set_temp_max_hyst , 0 ) ;
static SENSOR_DEVICE_ATTR ( temp2_max_hyst , S_IRUGO | S_IWUSR ,
get_temp_max_hyst , set_temp_max_hyst , 1 ) ;
static ssize_t get_alarms ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , data - > alarms ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t get_beep_enable ( struct device * dev , struct device_attribute
* attr , char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , data - > beep_enable ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t get_beep_mask ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct gl520_data * data = gl520_update_device ( dev ) ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , data - > beep_mask ) ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_beep_enable ( struct device * dev , struct device_attribute
* attr , const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
u8 r = simple_strtoul ( buf , NULL , 10 ) ? 0 : 1 ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
data - > beep_enable = ! r ;
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_BEEP_ENABLE ,
( gl520_read_value ( client , GL520_REG_BEEP_ENABLE )
& ~ 0x04 ) | ( r < < 2 ) ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static ssize_t set_beep_mask ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-11-05 01:45:14 +03:00
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
u8 r = simple_strtoul ( buf , NULL , 10 ) ;
2007-11-05 01:44:23 +03:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
r & = data - > alarm_mask ;
data - > beep_mask = r ;
2007-11-05 01:45:14 +03:00
gl520_write_value ( client , GL520_REG_BEEP_MASK , r ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-11-05 01:45:14 +03:00
static DEVICE_ATTR ( alarms , S_IRUGO , get_alarms , NULL ) ;
static DEVICE_ATTR ( beep_enable , S_IRUGO | S_IWUSR ,
get_beep_enable , set_beep_enable ) ;
static DEVICE_ATTR ( beep_mask , S_IRUGO | S_IWUSR ,
get_beep_mask , set_beep_mask ) ;
2006-09-24 23:17:13 +04:00
static struct attribute * gl520_attributes [ ] = {
& dev_attr_cpu0_vid . attr ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_in0_input . dev_attr . attr ,
& sensor_dev_attr_in0_min . dev_attr . attr ,
& sensor_dev_attr_in0_max . dev_attr . attr ,
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in1_min . dev_attr . attr ,
& sensor_dev_attr_in1_max . dev_attr . attr ,
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in2_min . dev_attr . attr ,
& sensor_dev_attr_in2_max . dev_attr . attr ,
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in3_min . dev_attr . attr ,
& sensor_dev_attr_in3_max . dev_attr . attr ,
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_fan1_min . dev_attr . attr ,
& sensor_dev_attr_fan1_div . dev_attr . attr ,
2006-09-24 23:17:13 +04:00
& dev_attr_fan1_off . attr ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_fan2_input . dev_attr . attr ,
& sensor_dev_attr_fan2_min . dev_attr . attr ,
& sensor_dev_attr_fan2_div . dev_attr . attr ,
2006-09-24 23:17:13 +04:00
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_temp1_input . dev_attr . attr ,
& sensor_dev_attr_temp1_max . dev_attr . attr ,
& sensor_dev_attr_temp1_max_hyst . dev_attr . attr ,
2006-09-24 23:17:13 +04:00
& dev_attr_alarms . attr ,
& dev_attr_beep_enable . attr ,
& dev_attr_beep_mask . attr ,
NULL
} ;
static const struct attribute_group gl520_group = {
. attrs = gl520_attributes ,
} ;
static struct attribute * gl520_attributes_opt [ ] = {
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_in4_input . dev_attr . attr ,
& sensor_dev_attr_in4_min . dev_attr . attr ,
& sensor_dev_attr_in4_max . dev_attr . attr ,
2006-09-24 23:17:13 +04:00
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_temp2_input . dev_attr . attr ,
& sensor_dev_attr_temp2_max . dev_attr . attr ,
& sensor_dev_attr_temp2_max_hyst . dev_attr . attr ,
2006-09-24 23:17:13 +04:00
NULL
} ;
static const struct attribute_group gl520_group_opt = {
. attrs = gl520_attributes_opt ,
} ;
2005-04-17 02:20:36 +04:00
/*
* Real code
*/
static int gl520_attach_adapter ( struct i2c_adapter * adapter )
{
if ( ! ( adapter - > class & I2C_CLASS_HWMON ) )
return 0 ;
2005-07-31 23:42:02 +04:00
return i2c_probe ( adapter , & addr_data , gl520_detect ) ;
2005-04-17 02:20:36 +04:00
}
static int gl520_detect ( struct i2c_adapter * adapter , int address , int kind )
{
2007-11-05 01:44:23 +03:00
struct i2c_client * client ;
2005-04-17 02:20:36 +04:00
struct gl520_data * data ;
int err = 0 ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA ) )
goto exit ;
/* OK. For now, we presume we have a valid client. We now create the
client structure , even though we cannot fill it completely yet .
But it allows us to access gl520_ { read , write } _value . */
2005-10-18 01:08:32 +04:00
if ( ! ( data = kzalloc ( sizeof ( struct gl520_data ) , GFP_KERNEL ) ) ) {
2005-04-17 02:20:36 +04:00
err = - ENOMEM ;
goto exit ;
}
2007-11-05 01:44:23 +03:00
client = & data - > client ;
i2c_set_clientdata ( client , data ) ;
client - > addr = address ;
client - > adapter = adapter ;
client - > driver = & gl520_driver ;
2005-04-17 02:20:36 +04:00
/* Determine the chip type. */
if ( kind < 0 ) {
2007-11-05 01:44:23 +03:00
if ( ( gl520_read_value ( client , GL520_REG_CHIP_ID ) ! = 0x20 ) | |
( ( gl520_read_value ( client , GL520_REG_REVISION ) & 0x7f ) ! = 0x00 ) | |
( ( gl520_read_value ( client , GL520_REG_CONF ) & 0x80 ) ! = 0x00 ) ) {
dev_dbg ( & client - > dev , " Unknown chip type, skipping \n " ) ;
2005-04-17 02:20:36 +04:00
goto exit_free ;
}
}
/* Fill in the remaining client fields */
2007-11-05 01:44:23 +03:00
strlcpy ( client - > name , " gl520sm " , I2C_NAME_SIZE ) ;
2006-01-19 01:19:26 +03:00
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
/* Tell the I2C layer a new client has arrived */
2007-11-05 01:44:23 +03:00
if ( ( err = i2c_attach_client ( client ) ) )
2005-04-17 02:20:36 +04:00
goto exit_free ;
/* Initialize the GL520SM chip */
2007-11-05 01:44:23 +03:00
gl520_init_client ( client ) ;
2005-04-17 02:20:36 +04:00
/* Register sysfs hooks */
2007-11-05 01:44:23 +03:00
if ( ( err = sysfs_create_group ( & client - > dev . kobj , & gl520_group ) ) )
2005-07-16 05:39:18 +04:00
goto exit_detach ;
2005-04-17 02:20:36 +04:00
2006-09-24 23:17:13 +04:00
if ( data - > two_temps ) {
2007-11-05 01:44:23 +03:00
if ( ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_temp2_input . dev_attr ) )
2007-11-05 01:44:23 +03:00
| | ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_temp2_max . dev_attr ) )
2007-11-05 01:44:23 +03:00
| | ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_temp2_max_hyst . dev_attr ) ) )
2006-09-24 23:17:13 +04:00
goto exit_remove_files ;
} else {
2007-11-05 01:44:23 +03:00
if ( ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_in4_input . dev_attr ) )
2007-11-05 01:44:23 +03:00
| | ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_in4_min . dev_attr ) )
2007-11-05 01:44:23 +03:00
| | ( err = device_create_file ( & client - > dev ,
2007-11-05 01:45:14 +03:00
& sensor_dev_attr_in4_max . dev_attr ) ) )
2006-09-24 23:17:13 +04:00
goto exit_remove_files ;
}
2005-04-17 02:20:36 +04:00
2007-11-05 01:44:23 +03:00
data - > hwmon_dev = hwmon_device_register ( & client - > dev ) ;
2007-08-21 00:46:20 +04:00
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
2006-09-24 23:17:13 +04:00
goto exit_remove_files ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
2006-09-24 23:17:13 +04:00
exit_remove_files :
2007-11-05 01:44:23 +03:00
sysfs_remove_group ( & client - > dev . kobj , & gl520_group ) ;
sysfs_remove_group ( & client - > dev . kobj , & gl520_group_opt ) ;
2005-07-16 05:39:18 +04:00
exit_detach :
2007-11-05 01:44:23 +03:00
i2c_detach_client ( client ) ;
2005-04-17 02:20:36 +04:00
exit_free :
kfree ( data ) ;
exit :
return err ;
}
/* Called when we have found a new GL520SM. */
static void gl520_init_client ( struct i2c_client * client )
{
struct gl520_data * data = i2c_get_clientdata ( client ) ;
u8 oldconf , conf ;
conf = oldconf = gl520_read_value ( client , GL520_REG_CONF ) ;
data - > alarm_mask = 0xff ;
2005-07-31 23:52:01 +04:00
data - > vrm = vid_which_vrm ( ) ;
2005-04-17 02:20:36 +04:00
if ( extra_sensor_type = = 1 )
conf & = ~ 0x10 ;
else if ( extra_sensor_type = = 2 )
conf | = 0x10 ;
data - > two_temps = ! ( conf & 0x10 ) ;
/* If IRQ# is disabled, we can safely force comparator mode */
if ( ! ( conf & 0x20 ) )
conf & = 0xf7 ;
/* Enable monitoring if needed */
conf | = 0x40 ;
if ( conf ! = oldconf )
gl520_write_value ( client , GL520_REG_CONF , conf ) ;
gl520_update_device ( & ( client - > dev ) ) ;
if ( data - > fan_min [ 0 ] = = 0 )
data - > alarm_mask & = ~ 0x20 ;
if ( data - > fan_min [ 1 ] = = 0 )
data - > alarm_mask & = ~ 0x40 ;
data - > beep_mask & = data - > alarm_mask ;
gl520_write_value ( client , GL520_REG_BEEP_MASK , data - > beep_mask ) ;
}
static int gl520_detach_client ( struct i2c_client * client )
{
2005-07-16 05:39:18 +04:00
struct gl520_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
int err ;
2007-08-21 00:46:20 +04:00
hwmon_device_unregister ( data - > hwmon_dev ) ;
2006-09-24 23:17:13 +04:00
sysfs_remove_group ( & client - > dev . kobj , & gl520_group ) ;
sysfs_remove_group ( & client - > dev . kobj , & gl520_group_opt ) ;
2005-07-16 05:39:18 +04:00
2005-07-28 00:14:49 +04:00
if ( ( err = i2c_detach_client ( client ) ) )
2005-04-17 02:20:36 +04:00
return err ;
2005-07-16 05:39:18 +04:00
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-11-05 01:44:23 +03:00
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
2005-04-17 02:20:36 +04:00
GL520 uses a high - byte first convention */
static int gl520_read_value ( struct i2c_client * client , u8 reg )
{
if ( ( reg > = 0x07 ) & & ( reg < = 0x0c ) )
return swab16 ( i2c_smbus_read_word_data ( client , reg ) ) ;
else
return i2c_smbus_read_byte_data ( client , reg ) ;
}
static int gl520_write_value ( struct i2c_client * client , u8 reg , u16 value )
{
if ( ( reg > = 0x07 ) & & ( reg < = 0x0c ) )
return i2c_smbus_write_word_data ( client , reg , swab16 ( value ) ) ;
else
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
static struct gl520_data * gl520_update_device ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct gl520_data * data = i2c_get_clientdata ( client ) ;
2007-11-05 01:44:52 +03:00
int val , i ;
2005-04-17 02:20:36 +04:00
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
2005-07-29 23:15:12 +04:00
if ( time_after ( jiffies , data - > last_updated + 2 * HZ ) | | ! data - > valid ) {
2005-04-17 02:20:36 +04:00
dev_dbg ( & client - > dev , " Starting gl520sm update \n " ) ;
data - > alarms = gl520_read_value ( client , GL520_REG_ALARMS ) ;
data - > beep_mask = gl520_read_value ( client , GL520_REG_BEEP_MASK ) ;
data - > vid = gl520_read_value ( client , GL520_REG_VID_INPUT ) & 0x1f ;
2007-11-05 01:44:52 +03:00
for ( i = 0 ; i < 4 ; i + + ) {
data - > in_input [ i ] = gl520_read_value ( client ,
GL520_REG_IN_INPUT [ i ] ) ;
val = gl520_read_value ( client , GL520_REG_IN_LIMIT [ i ] ) ;
data - > in_min [ i ] = val & 0xff ;
data - > in_max [ i ] = ( val > > 8 ) & 0xff ;
}
2005-04-17 02:20:36 +04:00
val = gl520_read_value ( client , GL520_REG_FAN_INPUT ) ;
data - > fan_input [ 0 ] = ( val > > 8 ) & 0xff ;
data - > fan_input [ 1 ] = val & 0xff ;
val = gl520_read_value ( client , GL520_REG_FAN_MIN ) ;
data - > fan_min [ 0 ] = ( val > > 8 ) & 0xff ;
data - > fan_min [ 1 ] = val & 0xff ;
2007-11-05 01:44:52 +03:00
data - > temp_input [ 0 ] = gl520_read_value ( client ,
GL520_REG_TEMP_INPUT [ 0 ] ) ;
data - > temp_max [ 0 ] = gl520_read_value ( client ,
GL520_REG_TEMP_MAX [ 0 ] ) ;
data - > temp_max_hyst [ 0 ] = gl520_read_value ( client ,
GL520_REG_TEMP_MAX_HYST [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
val = gl520_read_value ( client , GL520_REG_FAN_DIV ) ;
data - > fan_div [ 0 ] = ( val > > 6 ) & 0x03 ;
data - > fan_div [ 1 ] = ( val > > 4 ) & 0x03 ;
data - > fan_off = ( val > > 2 ) & 0x01 ;
data - > alarms & = data - > alarm_mask ;
val = gl520_read_value ( client , GL520_REG_CONF ) ;
data - > beep_enable = ! ( ( val > > 2 ) & 1 ) ;
/* Temp1 and Vin4 are the same input */
if ( data - > two_temps ) {
2007-11-05 01:44:52 +03:00
data - > temp_input [ 1 ] = gl520_read_value ( client ,
GL520_REG_TEMP_INPUT [ 1 ] ) ;
data - > temp_max [ 1 ] = gl520_read_value ( client ,
GL520_REG_TEMP_MAX [ 1 ] ) ;
data - > temp_max_hyst [ 1 ] = gl520_read_value ( client ,
GL520_REG_TEMP_MAX_HYST [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-11-05 01:44:52 +03:00
data - > in_input [ 4 ] = gl520_read_value ( client ,
GL520_REG_IN_INPUT [ 4 ] ) ;
data - > in_min [ 4 ] = gl520_read_value ( client ,
GL520_REG_IN_MIN [ 4 ] ) ;
data - > in_max [ 4 ] = gl520_read_value ( client ,
GL520_REG_IN_MAX [ 4 ] ) ;
2005-04-17 02:20:36 +04:00
}
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return data ;
}
static int __init sensors_gl520sm_init ( void )
{
return i2c_add_driver ( & gl520_driver ) ;
}
static void __exit sensors_gl520sm_exit ( void )
{
i2c_del_driver ( & gl520_driver ) ;
}
MODULE_AUTHOR ( " Frodo Looijaard <frodol@dds.nl>, "
2007-10-20 01:21:04 +04:00
" Kyösti Mälkki <kmalkki@cc.hut.fi>, "
2005-04-17 02:20:36 +04:00
" Maarten Deprez <maartendeprez@users.sourceforge.net> " ) ;
MODULE_DESCRIPTION ( " GL520SM driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( sensors_gl520sm_init ) ;
module_exit ( sensors_gl520sm_exit ) ;