2005-04-17 02:20:36 +04:00
/*
2012-01-15 08:39:24 +04:00
* lm78 . c - Part of lm_sensors , Linux kernel modules for hardware
* monitoring
* Copyright ( c ) 1998 , 1999 Frodo Looijaard < frodol @ dds . nl >
* Copyright ( c ) 2007 , 2011 Jean Delvare < khali @ linux - fr . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2005-04-17 02:20:36 +04:00
2011-01-12 23:55:10 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/jiffies.h>
# include <linux/i2c.h>
2005-07-16 05:39:18 +04:00
# include <linux/hwmon.h>
2005-08-01 00:12:09 +04:00
# include <linux/hwmon-vid.h>
2007-05-08 19:22:01 +04:00
# include <linux/hwmon-sysfs.h>
2005-07-16 05:39:18 +04:00
# include <linux/err.h>
2006-01-19 01:19:26 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
2011-07-25 23:46:11 +04:00
# ifdef CONFIG_ISA
# include <linux/platform_device.h>
# include <linux/ioport.h>
# include <linux/io.h>
# endif
2007-05-08 19:22:00 +04:00
2005-04-17 02:20:36 +04:00
/* Addresses to scan */
2008-02-18 06:28:03 +03:00
static const unsigned short normal_i2c [ ] = { 0x28 , 0x29 , 0x2a , 0x2b , 0x2c , 0x2d ,
0x2e , 0x2f , I2C_CLIENT_END } ;
2009-12-14 23:17:27 +03:00
enum chips { lm78 , lm79 } ;
2005-04-17 02:20:36 +04:00
/* Many LM78 constants specified below */
/* Length of ISA address segment */
# define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
# define LM78_ADDR_REG_OFFSET 5
# define LM78_DATA_REG_OFFSET 6
/* The LM78 registers */
# define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
# define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
# define LM78_REG_IN(nr) (0x20 + (nr))
# define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
# define LM78_REG_FAN(nr) (0x28 + (nr))
# define LM78_REG_TEMP 0x27
# define LM78_REG_TEMP_OVER 0x39
# define LM78_REG_TEMP_HYST 0x3a
# define LM78_REG_ALARM1 0x41
# define LM78_REG_ALARM2 0x42
# define LM78_REG_VID_FANDIV 0x47
# define LM78_REG_CONFIG 0x40
# define LM78_REG_CHIPID 0x49
# define LM78_REG_I2C_ADDR 0x48
2012-01-15 08:39:24 +04:00
/*
* Conversions . Rounding and limit checking is only done on the TO_REG
* variants .
*/
2005-04-17 02:20:36 +04:00
2012-01-15 08:39:24 +04:00
/*
* IN : mV ( 0 V to 4.08 V )
* REG : 16 mV / bit
*/
2005-04-17 02:20:36 +04:00
static inline u8 IN_TO_REG ( unsigned long val )
{
unsigned long nval = SENSORS_LIMIT ( val , 0 , 4080 ) ;
return ( nval + 8 ) / 16 ;
}
# define IN_FROM_REG(val) ((val) * 16)
static inline u8 FAN_TO_REG ( long rpm , int div )
{
if ( rpm < = 0 )
return 255 ;
return SENSORS_LIMIT ( ( 1350000 + rpm * div / 2 ) / ( rpm * div ) , 1 , 254 ) ;
}
static inline int FAN_FROM_REG ( u8 val , int div )
{
2012-01-15 08:39:24 +04:00
return val = = 0 ? - 1 : val = = 255 ? 0 : 1350000 / ( val * div ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-15 08:39:24 +04:00
/*
* TEMP : mC ( - 128 C to + 127 C )
* REG : 1 C / bit , two ' s complement
*/
2005-04-17 02:20:36 +04:00
static inline s8 TEMP_TO_REG ( int val )
{
int nval = SENSORS_LIMIT ( val , - 128000 , 127000 ) ;
2012-01-15 08:39:24 +04:00
return nval < 0 ? ( nval - 500 ) / 1000 : ( nval + 500 ) / 1000 ;
2005-04-17 02:20:36 +04:00
}
static inline int TEMP_FROM_REG ( s8 val )
{
return val * 1000 ;
}
# define DIV_FROM_REG(val) (1 << (val))
struct lm78_data {
2008-10-17 19:51:16 +04:00
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 lock ;
2005-04-17 02:20:36 +04:00
enum chips type ;
2008-10-17 19:51:15 +04:00
/* For ISA device only */
const char * name ;
int isa_addr ;
2006-01-19 01:19:26 +03:00
struct mutex update_lock ;
2005-04-17 02:20:36 +04:00
char valid ; /* !=0 if following fields are valid */
unsigned long last_updated ; /* In jiffies */
u8 in [ 7 ] ; /* Register value */
u8 in_max [ 7 ] ; /* Register value */
u8 in_min [ 7 ] ; /* Register value */
u8 fan [ 3 ] ; /* Register value */
u8 fan_min [ 3 ] ; /* Register value */
s8 temp ; /* Register value */
s8 temp_over ; /* Register value */
s8 temp_hyst ; /* Register value */
u8 fan_div [ 3 ] ; /* Register encoding, shifted right */
u8 vid ; /* Register encoding, combined */
u16 alarms ; /* Register encoding, combined */
} ;
2007-05-08 19:22:01 +04:00
static int lm78_read_value ( struct lm78_data * data , u8 reg ) ;
static int lm78_write_value ( struct lm78_data * data , u8 reg , u8 value ) ;
2005-04-17 02:20:36 +04:00
static struct lm78_data * lm78_update_device ( struct device * dev ) ;
2007-05-08 19:22:01 +04:00
static void lm78_init_device ( struct lm78_data * data ) ;
2005-04-17 02:20:36 +04:00
/* 7 Voltages */
2007-05-08 19:22:01 +04:00
static ssize_t show_in ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
return sprintf ( buf , " %d \n " , IN_FROM_REG ( data - > in [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
static ssize_t show_in_min ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
return sprintf ( buf , " %d \n " , IN_FROM_REG ( data - > in_min [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
static ssize_t show_in_max ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
return sprintf ( buf , " %d \n " , IN_FROM_REG ( data - > in_max [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
static ssize_t set_in_min ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2012-01-15 08:39:24 +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-04-17 02:20:36 +04:00
data - > in_min [ nr ] = IN_TO_REG ( val ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_IN_MIN ( nr ) , data - > in_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-05-08 19:22:01 +04:00
static ssize_t set_in_max ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2012-01-15 08:39:24 +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-04-17 02:20:36 +04:00
data - > in_max [ nr ] = IN_TO_REG ( val ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_IN_MAX ( nr ) , data - > in_max [ 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-15 08:39:24 +04:00
2005-04-17 02:20:36 +04:00
# define show_in_offset(offset) \
2007-05-08 19:22:01 +04:00
static SENSOR_DEVICE_ATTR ( in # # offset # # _input , S_IRUGO , \
show_in , NULL , offset ) ; \
static SENSOR_DEVICE_ATTR ( in # # offset # # _min , S_IRUGO | S_IWUSR , \
show_in_min , set_in_min , offset ) ; \
static SENSOR_DEVICE_ATTR ( in # # offset # # _max , S_IRUGO | S_IWUSR , \
show_in_max , set_in_max , offset ) ;
2005-04-17 02:20:36 +04:00
show_in_offset ( 0 ) ;
show_in_offset ( 1 ) ;
show_in_offset ( 2 ) ;
show_in_offset ( 3 ) ;
show_in_offset ( 4 ) ;
show_in_offset ( 5 ) ;
show_in_offset ( 6 ) ;
/* Temperature */
2007-05-08 19:22:01 +04:00
static ssize_t show_temp ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm78_data * data = lm78_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp ) ) ;
}
2007-05-08 19:22:01 +04:00
static ssize_t show_temp_over ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm78_data * data = lm78_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_over ) ) ;
}
2007-05-08 19:22:01 +04:00
static ssize_t set_temp_over ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2012-01-15 08:39:24 +04:00
long val ;
int err ;
err = kstrtol ( 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-04-17 02:20:36 +04:00
data - > temp_over = TEMP_TO_REG ( val ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_TEMP_OVER , data - > temp_over ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-05-08 19:22:01 +04:00
static ssize_t show_temp_hyst ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm78_data * data = lm78_update_device ( dev ) ;
return sprintf ( buf , " %d \n " , TEMP_FROM_REG ( data - > temp_hyst ) ) ;
}
2007-05-08 19:22:01 +04:00
static ssize_t set_temp_hyst ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2012-01-15 08:39:24 +04:00
long val ;
int err ;
err = kstrtol ( 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-04-17 02:20:36 +04:00
data - > temp_hyst = TEMP_TO_REG ( val ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_TEMP_HYST , data - > temp_hyst ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
static DEVICE_ATTR ( temp1_input , S_IRUGO , show_temp , NULL ) ;
static DEVICE_ATTR ( temp1_max , S_IRUGO | S_IWUSR ,
show_temp_over , set_temp_over ) ;
static DEVICE_ATTR ( temp1_max_hyst , S_IRUGO | S_IWUSR ,
show_temp_hyst , set_temp_hyst ) ;
/* 3 Fans */
2007-05-08 19:22:01 +04:00
static ssize_t show_fan ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan [ nr ] ,
2012-01-15 08:39:24 +04:00
DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
static ssize_t show_fan_min ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2012-01-15 08:39:24 +04:00
return sprintf ( buf , " %d \n " , FAN_FROM_REG ( data - > fan_min [ nr ] ,
DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
static ssize_t set_fan_min ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2012-01-15 08:39:24 +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-04-17 02:20:36 +04:00
data - > fan_min [ nr ] = FAN_TO_REG ( val , DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_FAN_MIN ( nr ) , data - > fan_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-05-08 19:22:01 +04:00
static ssize_t show_fan_div ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2005-04-17 02:20:36 +04:00
struct lm78_data * data = lm78_update_device ( dev ) ;
2007-05-08 19:22:01 +04:00
return sprintf ( buf , " %d \n " , DIV_FROM_REG ( data - > fan_div [ attr - > index ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-15 08:39:24 +04:00
/*
* Note : we save and restore the fan minimum here , because its value is
* determined in part by the fan divisor . This follows the principle of
* least surprise ; the user doesn ' t expect the fan minimum to change just
* because the divisor changed .
*/
2007-05-08 19:22:01 +04:00
static ssize_t set_fan_div ( struct device * dev , struct device_attribute * da ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:01 +04:00
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2007-05-08 19:22:01 +04:00
int nr = attr - > index ;
2005-04-17 02:20:36 +04:00
unsigned long min ;
u8 reg ;
2012-01-15 08:39:24 +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-04-17 02:20:36 +04:00
min = FAN_FROM_REG ( data - > fan_min [ nr ] ,
DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
switch ( val ) {
2012-01-15 08:39:24 +04:00
case 1 :
data - > fan_div [ nr ] = 0 ;
break ;
case 2 :
data - > fan_div [ nr ] = 1 ;
break ;
case 4 :
data - > fan_div [ nr ] = 2 ;
break ;
case 8 :
data - > fan_div [ nr ] = 3 ;
break ;
2005-04-17 02:20:36 +04:00
default :
2007-05-08 19:22:00 +04:00
dev_err ( dev , " fan_div value %ld not "
2005-04-17 02:20:36 +04:00
" supported. Choose one of 1, 2, 4 or 8! \n " , val ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2007-05-08 19:22:01 +04:00
reg = lm78_read_value ( data , LM78_REG_VID_FANDIV ) ;
2005-04-17 02:20:36 +04:00
switch ( nr ) {
case 0 :
reg = ( reg & 0xcf ) | ( data - > fan_div [ nr ] < < 4 ) ;
break ;
case 1 :
reg = ( reg & 0x3f ) | ( data - > fan_div [ nr ] < < 6 ) ;
break ;
}
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_VID_FANDIV , reg ) ;
2005-04-17 02:20:36 +04:00
data - > fan_min [ nr ] =
FAN_TO_REG ( min , DIV_FROM_REG ( data - > fan_div [ nr ] ) ) ;
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_FAN_MIN ( nr ) , data - > fan_min [ nr ] ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return count ;
}
2007-05-08 19:22:01 +04:00
# define show_fan_offset(offset) \
static SENSOR_DEVICE_ATTR ( fan # # offset # # _input , S_IRUGO , \
show_fan , NULL , offset - 1 ) ; \
static SENSOR_DEVICE_ATTR ( fan # # offset # # _min , S_IRUGO | S_IWUSR , \
show_fan_min , set_fan_min , offset - 1 ) ;
2005-04-17 02:20:36 +04:00
show_fan_offset ( 1 ) ;
show_fan_offset ( 2 ) ;
show_fan_offset ( 3 ) ;
/* Fan 3 divisor is locked in H/W */
2007-05-08 19:22:01 +04:00
static SENSOR_DEVICE_ATTR ( fan1_div , S_IRUGO | S_IWUSR ,
show_fan_div , set_fan_div , 0 ) ;
static SENSOR_DEVICE_ATTR ( fan2_div , S_IRUGO | S_IWUSR ,
show_fan_div , set_fan_div , 1 ) ;
static SENSOR_DEVICE_ATTR ( fan3_div , S_IRUGO , show_fan_div , NULL , 2 ) ;
2005-04-17 02:20:36 +04:00
/* VID */
2007-05-08 19:22:01 +04:00
static ssize_t show_vid ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm78_data * data = lm78_update_device ( dev ) ;
2005-11-24 02:44:26 +03:00
return sprintf ( buf , " %d \n " , vid_from_reg ( data - > vid , 82 ) ) ;
2005-04-17 02:20:36 +04:00
}
static DEVICE_ATTR ( cpu0_vid , S_IRUGO , show_vid , NULL ) ;
/* Alarms */
2007-05-08 19:22:01 +04:00
static ssize_t show_alarms ( struct device * dev , struct device_attribute * da ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct lm78_data * data = lm78_update_device ( dev ) ;
return sprintf ( buf , " %u \n " , data - > alarms ) ;
}
static DEVICE_ATTR ( alarms , S_IRUGO , show_alarms , NULL ) ;
2007-09-05 01:25:33 +04:00
static ssize_t show_alarm ( struct device * dev , struct device_attribute * da ,
char * buf )
{
struct lm78_data * data = lm78_update_device ( dev ) ;
int nr = to_sensor_dev_attr ( da ) - > index ;
return sprintf ( buf , " %u \n " , ( data - > alarms > > nr ) & 1 ) ;
}
static SENSOR_DEVICE_ATTR ( in0_alarm , S_IRUGO , show_alarm , NULL , 0 ) ;
static SENSOR_DEVICE_ATTR ( in1_alarm , S_IRUGO , show_alarm , NULL , 1 ) ;
static SENSOR_DEVICE_ATTR ( in2_alarm , S_IRUGO , show_alarm , NULL , 2 ) ;
static SENSOR_DEVICE_ATTR ( in3_alarm , S_IRUGO , show_alarm , NULL , 3 ) ;
static SENSOR_DEVICE_ATTR ( in4_alarm , S_IRUGO , show_alarm , NULL , 8 ) ;
static SENSOR_DEVICE_ATTR ( in5_alarm , S_IRUGO , show_alarm , NULL , 9 ) ;
static SENSOR_DEVICE_ATTR ( in6_alarm , S_IRUGO , show_alarm , NULL , 10 ) ;
static SENSOR_DEVICE_ATTR ( fan1_alarm , S_IRUGO , show_alarm , NULL , 6 ) ;
static SENSOR_DEVICE_ATTR ( fan2_alarm , S_IRUGO , show_alarm , NULL , 7 ) ;
static SENSOR_DEVICE_ATTR ( fan3_alarm , S_IRUGO , show_alarm , NULL , 11 ) ;
static SENSOR_DEVICE_ATTR ( temp1_alarm , S_IRUGO , show_alarm , NULL , 4 ) ;
2006-09-24 22:59:49 +04:00
static struct attribute * lm78_attributes [ ] = {
2007-05-08 19:22:01 +04: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 ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in0_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in1_min . dev_attr . attr ,
& sensor_dev_attr_in1_max . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in1_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in2_min . dev_attr . attr ,
& sensor_dev_attr_in2_max . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in2_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in3_min . dev_attr . attr ,
& sensor_dev_attr_in3_max . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in3_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04: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 ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in4_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_in5_input . dev_attr . attr ,
& sensor_dev_attr_in5_min . dev_attr . attr ,
& sensor_dev_attr_in5_max . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in5_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_in6_input . dev_attr . attr ,
& sensor_dev_attr_in6_min . dev_attr . attr ,
& sensor_dev_attr_in6_max . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_in6_alarm . dev_attr . attr ,
2006-09-24 22:59:49 +04:00
& dev_attr_temp1_input . attr ,
& dev_attr_temp1_max . attr ,
& dev_attr_temp1_max_hyst . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_temp1_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_fan1_input . dev_attr . attr ,
& sensor_dev_attr_fan1_min . dev_attr . attr ,
& sensor_dev_attr_fan1_div . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_fan1_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04: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 ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_fan2_alarm . dev_attr . attr ,
2007-05-08 19:22:01 +04:00
& sensor_dev_attr_fan3_input . dev_attr . attr ,
& sensor_dev_attr_fan3_min . dev_attr . attr ,
& sensor_dev_attr_fan3_div . dev_attr . attr ,
2007-09-05 01:25:33 +04:00
& sensor_dev_attr_fan3_alarm . dev_attr . attr ,
2006-09-24 22:59:49 +04:00
& dev_attr_alarms . attr ,
& dev_attr_cpu0_vid . attr ,
NULL
} ;
static const struct attribute_group lm78_group = {
. attrs = lm78_attributes ,
} ;
2011-07-25 23:46:11 +04:00
/*
* ISA related code
*/
# ifdef CONFIG_ISA
/* ISA device, if found */
static struct platform_device * pdev ;
static unsigned short isa_address = 0x290 ;
2012-01-15 08:39:24 +04:00
/*
* I2C devices get this name attribute automatically , but for ISA devices
* we must create it by ourselves .
*/
2007-05-08 19:22:00 +04:00
static ssize_t show_name ( struct device * dev , struct device_attribute
* devattr , char * buf )
{
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2008-10-17 19:51:15 +04:00
return sprintf ( buf , " %s \n " , data - > name ) ;
2007-05-08 19:22:00 +04:00
}
static DEVICE_ATTR ( name , S_IRUGO , show_name , NULL ) ;
2011-07-25 23:46:11 +04:00
static struct lm78_data * lm78_data_if_isa ( void )
{
return pdev ? platform_get_drvdata ( pdev ) : NULL ;
}
2008-10-17 19:51:15 +04:00
/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */
static int lm78_alias_detect ( struct i2c_client * client , u8 chipid )
{
2008-10-17 19:51:16 +04:00
struct lm78_data * isa ;
2008-10-17 19:51:15 +04:00
int i ;
if ( ! pdev ) /* No ISA chip */
return 0 ;
isa = platform_get_drvdata ( pdev ) ;
if ( lm78_read_value ( isa , LM78_REG_I2C_ADDR ) ! = client - > addr )
return 0 ; /* Address doesn't match */
if ( ( lm78_read_value ( isa , LM78_REG_CHIPID ) & 0xfe ) ! = ( chipid & 0xfe ) )
return 0 ; /* Chip type doesn't match */
2012-01-15 08:39:24 +04:00
/*
* We compare all the limit registers , the config register and the
* interrupt mask registers
*/
2008-10-17 19:51:15 +04:00
for ( i = 0x2b ; i < = 0x3d ; i + + ) {
2008-10-17 19:51:16 +04:00
if ( lm78_read_value ( isa , i ) ! =
i2c_smbus_read_byte_data ( client , i ) )
2008-10-17 19:51:15 +04:00
return 0 ;
}
if ( lm78_read_value ( isa , LM78_REG_CONFIG ) ! =
2008-10-17 19:51:16 +04:00
i2c_smbus_read_byte_data ( client , LM78_REG_CONFIG ) )
2008-10-17 19:51:15 +04:00
return 0 ;
for ( i = 0x43 ; i < = 0x46 ; i + + ) {
2008-10-17 19:51:16 +04:00
if ( lm78_read_value ( isa , i ) ! =
i2c_smbus_read_byte_data ( client , i ) )
2008-10-17 19:51:15 +04:00
return 0 ;
}
return 1 ;
}
2011-07-25 23:46:11 +04:00
# else /* !CONFIG_ISA */
static int lm78_alias_detect ( struct i2c_client * client , u8 chipid )
{
return 0 ;
}
static struct lm78_data * lm78_data_if_isa ( void )
{
return NULL ;
}
# endif /* CONFIG_ISA */
2008-10-17 19:51:15 +04:00
2009-12-14 23:17:23 +03:00
static int lm78_i2c_detect ( struct i2c_client * client ,
2008-10-17 19:51:16 +04:00
struct i2c_board_info * info )
2005-04-17 02:20:36 +04:00
{
2008-10-17 19:51:16 +04:00
int i ;
2011-07-25 23:46:11 +04:00
struct lm78_data * isa = lm78_data_if_isa ( ) ;
2008-10-17 19:51:16 +04:00
const char * client_name ;
struct i2c_adapter * adapter = client - > adapter ;
int address = client - > addr ;
2005-04-17 02:20:36 +04:00
2008-10-17 19:51:16 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2012-01-15 08:39:24 +04:00
/*
* We block updates of the ISA device to minimize the risk of
* concurrent access to the same LM78 chip through different
* interfaces .
*/
2008-10-17 19:51:16 +04:00
if ( isa )
mutex_lock ( & isa - > update_lock ) ;
2005-04-17 02:20:36 +04:00
2009-12-09 22:35:57 +03:00
if ( ( i2c_smbus_read_byte_data ( client , LM78_REG_CONFIG ) & 0x80 )
| | i2c_smbus_read_byte_data ( client , LM78_REG_I2C_ADDR ) ! = address )
goto err_nodev ;
/* Explicitly prevent the misdetection of Winbond chips */
i = i2c_smbus_read_byte_data ( client , 0x4f ) ;
if ( i = = 0xa3 | | i = = 0x5c )
goto err_nodev ;
2005-04-17 02:20:36 +04:00
/* Determine the chip type. */
2009-12-09 22:35:57 +03:00
i = i2c_smbus_read_byte_data ( client , LM78_REG_CHIPID ) ;
if ( i = = 0x00 | | i = = 0x20 /* LM78 */
| | i = = 0x40 ) /* LM78-J */
client_name = " lm78 " ;
else if ( ( i & 0xfe ) = = 0xc0 )
client_name = " lm79 " ;
else
goto err_nodev ;
if ( lm78_alias_detect ( client , i ) ) {
dev_dbg ( & adapter - > dev , " Device at 0x%02x appears to "
" be the same as ISA device \n " , address ) ;
goto err_nodev ;
2005-04-17 02:20:36 +04:00
}
2008-10-17 19:51:16 +04:00
if ( isa )
mutex_unlock ( & isa - > update_lock ) ;
strlcpy ( info - > type , client_name , I2C_NAME_SIZE ) ;
2005-04-17 02:20:36 +04:00
2008-10-17 19:51:16 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2008-10-17 19:51:16 +04:00
err_nodev :
if ( isa )
mutex_unlock ( & isa - > update_lock ) ;
return - ENODEV ;
}
static int lm78_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct lm78_data * data ;
int err ;
2012-06-02 20:58:09 +04:00
data = devm_kzalloc ( & client - > dev , sizeof ( struct lm78_data ) , GFP_KERNEL ) ;
2008-10-17 19:51:16 +04:00
if ( ! data )
return - ENOMEM ;
i2c_set_clientdata ( client , data ) ;
data - > client = client ;
data - > type = id - > driver_data ;
2005-04-17 02:20:36 +04:00
/* Initialize the LM78 chip */
2007-05-08 19:22:01 +04:00
lm78_init_device ( data ) ;
2005-04-17 02:20:36 +04:00
/* Register sysfs hooks */
2008-10-17 19:51:16 +04:00
err = sysfs_create_group ( & client - > dev . kobj , & lm78_group ) ;
if ( err )
2012-06-02 20:58:09 +04:00
return err ;
2006-09-24 22:59:49 +04:00
2008-10-17 19:51:16 +04: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 ) ;
2012-06-02 20:58:09 +04:00
goto error ;
2005-07-16 05:39:18 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
2012-06-02 20:58:09 +04:00
error :
2008-10-17 19:51:16 +04:00
sysfs_remove_group ( & client - > dev . kobj , & lm78_group ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2008-10-17 19:51:16 +04:00
static int lm78_i2c_remove ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2005-07-16 05:39:18 +04:00
struct lm78_data * data = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
2007-08-21 00:46:20 +04:00
hwmon_device_unregister ( data - > hwmon_dev ) ;
2006-09-24 22:59:49 +04:00
sysfs_remove_group ( & client - > dev . kobj , & lm78_group ) ;
2007-05-08 19:22:00 +04:00
return 0 ;
}
2011-07-25 23:46:11 +04:00
static const struct i2c_device_id lm78_i2c_id [ ] = {
{ " lm78 " , lm78 } ,
{ " lm79 " , lm79 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , lm78_i2c_id ) ;
2008-10-17 19:51:15 +04:00
2011-07-25 23:46:11 +04:00
static struct i2c_driver lm78_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " lm78 " ,
} ,
. probe = lm78_i2c_probe ,
. remove = lm78_i2c_remove ,
. id_table = lm78_i2c_id ,
. detect = lm78_i2c_detect ,
. address_list = normal_i2c ,
} ;
2005-04-17 02:20:36 +04:00
2012-01-15 08:39:24 +04:00
/*
* The SMBus locks itself , but ISA access must be locked explicitly !
* We don ' t want to lock the whole ISA bus , so we lock each client
* separately .
* We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks ,
* would slow down the LM78 access and should not be necessary .
*/
2007-05-08 19:22:01 +04:00
static int lm78_read_value ( struct lm78_data * data , u8 reg )
2005-04-17 02:20:36 +04:00
{
2008-10-17 19:51:16 +04:00
struct i2c_client * client = data - > client ;
2007-05-08 19:22:01 +04:00
2011-07-25 23:46:11 +04:00
# ifdef CONFIG_ISA
2008-10-17 19:51:16 +04:00
if ( ! client ) { /* ISA device */
2007-05-08 19:22:01 +04:00
int res ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > lock ) ;
2008-10-17 19:51:15 +04:00
outb_p ( reg , data - > isa_addr + LM78_ADDR_REG_OFFSET ) ;
res = inb_p ( data - > isa_addr + LM78_DATA_REG_OFFSET ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > lock ) ;
2005-04-17 02:20:36 +04:00
return res ;
} else
2011-07-25 23:46:11 +04:00
# endif
2005-04-17 02:20:36 +04:00
return i2c_smbus_read_byte_data ( client , reg ) ;
}
2007-05-08 19:22:01 +04:00
static int lm78_write_value ( struct lm78_data * data , u8 reg , u8 value )
2005-04-17 02:20:36 +04:00
{
2008-10-17 19:51:16 +04:00
struct i2c_client * client = data - > client ;
2007-05-08 19:22:01 +04:00
2011-07-25 23:46:11 +04:00
# ifdef CONFIG_ISA
2008-10-17 19:51:16 +04:00
if ( ! client ) { /* ISA device */
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > lock ) ;
2008-10-17 19:51:15 +04:00
outb_p ( reg , data - > isa_addr + LM78_ADDR_REG_OFFSET ) ;
outb_p ( value , data - > isa_addr + LM78_DATA_REG_OFFSET ) ;
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
} else
2011-07-25 23:46:11 +04:00
# endif
2005-04-17 02:20:36 +04:00
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
2007-05-08 19:22:01 +04:00
static void lm78_init_device ( struct lm78_data * data )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:00 +04:00
u8 config ;
int i ;
2005-04-17 02:20:36 +04:00
/* Start monitoring */
2007-05-08 19:22:01 +04:00
config = lm78_read_value ( data , LM78_REG_CONFIG ) ;
2007-05-08 19:22:00 +04:00
if ( ( config & 0x09 ) ! = 0x01 )
2007-05-08 19:22:01 +04:00
lm78_write_value ( data , LM78_REG_CONFIG ,
2005-04-17 02:20:36 +04:00
( config & 0xf7 ) | 0x01 ) ;
2007-05-08 19:22:00 +04:00
/* A few vars need to be filled upon startup */
for ( i = 0 ; i < 3 ; i + + ) {
2007-05-08 19:22:01 +04:00
data - > fan_min [ i ] = lm78_read_value ( data ,
2007-05-08 19:22:00 +04:00
LM78_REG_FAN_MIN ( i ) ) ;
}
mutex_init ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
}
static struct lm78_data * lm78_update_device ( struct device * dev )
{
2007-05-08 19:22:00 +04:00
struct lm78_data * data = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
int i ;
2006-01-19 01:19:26 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
if ( time_after ( jiffies , data - > last_updated + HZ + HZ / 2 )
| | ! data - > valid ) {
2007-05-08 19:22:00 +04:00
dev_dbg ( dev , " Starting lm78 update \n " ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < = 6 ; i + + ) {
data - > in [ i ] =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_IN ( i ) ) ;
2005-04-17 02:20:36 +04:00
data - > in_min [ i ] =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_IN_MIN ( i ) ) ;
2005-04-17 02:20:36 +04:00
data - > in_max [ i ] =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_IN_MAX ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
for ( i = 0 ; i < 3 ; i + + ) {
data - > fan [ i ] =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_FAN ( i ) ) ;
2005-04-17 02:20:36 +04:00
data - > fan_min [ i ] =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_FAN_MIN ( i ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-08 19:22:01 +04:00
data - > temp = lm78_read_value ( data , LM78_REG_TEMP ) ;
2005-04-17 02:20:36 +04:00
data - > temp_over =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_TEMP_OVER ) ;
2005-04-17 02:20:36 +04:00
data - > temp_hyst =
2007-05-08 19:22:01 +04:00
lm78_read_value ( data , LM78_REG_TEMP_HYST ) ;
i = lm78_read_value ( data , LM78_REG_VID_FANDIV ) ;
2005-04-17 02:20:36 +04:00
data - > vid = i & 0x0f ;
if ( data - > type = = lm79 )
data - > vid | =
2007-05-08 19:22:01 +04:00
( lm78_read_value ( data , LM78_REG_CHIPID ) &
2005-04-17 02:20:36 +04:00
0x01 ) < < 4 ;
else
data - > vid | = 0x10 ;
data - > fan_div [ 0 ] = ( i > > 4 ) & 0x03 ;
data - > fan_div [ 1 ] = i > > 6 ;
2007-05-08 19:22:01 +04:00
data - > alarms = lm78_read_value ( data , LM78_REG_ALARM1 ) +
( lm78_read_value ( data , LM78_REG_ALARM2 ) < < 8 ) ;
2005-04-17 02:20:36 +04:00
data - > last_updated = jiffies ;
data - > valid = 1 ;
data - > fan_div [ 2 ] = 1 ;
}
2006-01-19 01:19:26 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-04-17 02:20:36 +04:00
return data ;
}
2011-07-25 23:46:11 +04:00
# ifdef CONFIG_ISA
2012-11-19 22:22:35 +04:00
static int lm78_isa_probe ( struct platform_device * pdev )
2011-07-25 23:46:11 +04:00
{
int err ;
struct lm78_data * data ;
struct resource * res ;
/* Reserve the ISA region */
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
2012-06-02 20:58:09 +04:00
if ( ! devm_request_region ( & pdev - > dev , res - > start + LM78_ADDR_REG_OFFSET ,
2 , " lm78 " ) )
return - EBUSY ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( struct lm78_data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2011-07-25 23:46:11 +04:00
mutex_init ( & data - > lock ) ;
data - > isa_addr = res - > start ;
platform_set_drvdata ( pdev , data ) ;
if ( lm78_read_value ( data , LM78_REG_CHIPID ) & 0x80 ) {
data - > type = lm79 ;
data - > name = " lm79 " ;
} else {
data - > type = lm78 ;
data - > name = " lm78 " ;
}
/* Initialize the LM78 chip */
lm78_init_device ( data ) ;
/* Register sysfs hooks */
2012-01-15 08:39:24 +04:00
err = sysfs_create_group ( & pdev - > dev . kobj , & lm78_group ) ;
if ( err )
goto exit_remove_files ;
err = device_create_file ( & pdev - > dev , & dev_attr_name ) ;
if ( err )
2011-07-25 23:46:11 +04:00
goto exit_remove_files ;
data - > hwmon_dev = hwmon_device_register ( & pdev - > dev ) ;
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
goto exit_remove_files ;
}
return 0 ;
exit_remove_files :
sysfs_remove_group ( & pdev - > dev . kobj , & lm78_group ) ;
device_remove_file ( & pdev - > dev , & dev_attr_name ) ;
return err ;
}
2012-11-19 22:25:51 +04:00
static int lm78_isa_remove ( struct platform_device * pdev )
2011-07-25 23:46:11 +04:00
{
struct lm78_data * data = platform_get_drvdata ( pdev ) ;
hwmon_device_unregister ( data - > hwmon_dev ) ;
sysfs_remove_group ( & pdev - > dev . kobj , & lm78_group ) ;
device_remove_file ( & pdev - > dev , & dev_attr_name ) ;
return 0 ;
}
static struct platform_driver lm78_isa_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " lm78 " ,
} ,
. probe = lm78_isa_probe ,
2012-11-19 22:21:20 +04:00
. remove = lm78_isa_remove ,
2011-07-25 23:46:11 +04:00
} ;
2007-05-08 19:22:00 +04:00
/* return 1 if a supported chip is found, 0 otherwise */
static int __init lm78_isa_found ( unsigned short address )
{
int val , save , found = 0 ;
2010-02-05 21:58:36 +03:00
int port ;
2012-01-15 08:39:24 +04:00
/*
* Some boards declare base + 0 to base + 7 as a PNP device , some base + 4
2010-02-05 21:58:36 +03:00
* to base + 7 and some base + 5 to base + 6. So we better request each port
2012-01-15 08:39:24 +04:00
* individually for the probing phase .
*/
2010-02-05 21:58:36 +03:00
for ( port = address ; port < address + LM78_EXTENT ; port + + ) {
if ( ! request_region ( port , 1 , " lm78 " ) ) {
2011-01-12 23:55:10 +03:00
pr_debug ( " Failed to request port 0x%x \n " , port ) ;
2010-02-05 21:58:36 +03:00
goto release ;
}
2008-10-17 19:51:15 +04:00
}
2007-05-08 19:22:00 +04:00
# define REALLY_SLOW_IO
2012-01-15 08:39:24 +04:00
/*
* We need the timeouts for at least some LM78 - like
* chips . But only if we read ' undefined ' registers .
*/
2007-05-08 19:22:00 +04:00
val = inb_p ( address + 1 ) ;
if ( inb_p ( address + 2 ) ! = val
| | inb_p ( address + 3 ) ! = val
| | inb_p ( address + 7 ) ! = val )
goto release ;
# undef REALLY_SLOW_IO
2012-01-15 08:39:24 +04:00
/*
* We should be able to change the 7 LSB of the address port . The
* MSB ( busy flag ) should be clear initially , set after the write .
*/
2007-05-08 19:22:00 +04:00
save = inb_p ( address + LM78_ADDR_REG_OFFSET ) ;
if ( save & 0x80 )
goto release ;
val = ~ save & 0x7f ;
outb_p ( val , address + LM78_ADDR_REG_OFFSET ) ;
if ( inb_p ( address + LM78_ADDR_REG_OFFSET ) ! = ( val | 0x80 ) ) {
outb_p ( save , address + LM78_ADDR_REG_OFFSET ) ;
goto release ;
}
/* We found a device, now see if it could be an LM78 */
outb_p ( LM78_REG_CONFIG , address + LM78_ADDR_REG_OFFSET ) ;
val = inb_p ( address + LM78_DATA_REG_OFFSET ) ;
if ( val & 0x80 )
goto release ;
outb_p ( LM78_REG_I2C_ADDR , address + LM78_ADDR_REG_OFFSET ) ;
val = inb_p ( address + LM78_DATA_REG_OFFSET ) ;
if ( val < 0x03 | | val > 0x77 ) /* Not a valid I2C address */
goto release ;
/* The busy flag should be clear again */
if ( inb_p ( address + LM78_ADDR_REG_OFFSET ) & 0x80 )
goto release ;
/* Explicitly prevent the misdetection of Winbond chips */
outb_p ( 0x4f , address + LM78_ADDR_REG_OFFSET ) ;
val = inb_p ( address + LM78_DATA_REG_OFFSET ) ;
if ( val = = 0xa3 | | val = = 0x5c )
goto release ;
/* Explicitly prevent the misdetection of ITE chips */
outb_p ( 0x58 , address + LM78_ADDR_REG_OFFSET ) ;
val = inb_p ( address + LM78_DATA_REG_OFFSET ) ;
if ( val = = 0x90 )
goto release ;
/* Determine the chip type */
outb_p ( LM78_REG_CHIPID , address + LM78_ADDR_REG_OFFSET ) ;
val = inb_p ( address + LM78_DATA_REG_OFFSET ) ;
2007-07-25 01:36:00 +04:00
if ( val = = 0x00 | | val = = 0x20 /* LM78 */
2007-05-08 19:22:00 +04:00
| | val = = 0x40 /* LM78-J */
| | ( val & 0xfe ) = = 0xc0 ) /* LM79 */
found = 1 ;
if ( found )
2011-01-12 23:55:10 +03:00
pr_info ( " Found an %s chip at %#x \n " ,
2007-05-08 19:22:00 +04:00
val & 0x80 ? " LM79 " : " LM78 " , ( int ) address ) ;
release :
2010-02-05 21:58:36 +03:00
for ( port - - ; port > = address ; port - - )
release_region ( port , 1 ) ;
2007-05-08 19:22:00 +04:00
return found ;
}
static int __init lm78_isa_device_add ( unsigned short address )
{
struct resource res = {
. start = address ,
2007-08-29 12:39:57 +04:00
. end = address + LM78_EXTENT - 1 ,
2007-05-08 19:22:00 +04:00
. name = " lm78 " ,
. flags = IORESOURCE_IO ,
} ;
int err ;
pdev = platform_device_alloc ( " lm78 " , address ) ;
if ( ! pdev ) {
err = - ENOMEM ;
2011-01-12 23:55:10 +03:00
pr_err ( " Device allocation failed \n " ) ;
2007-05-08 19:22:00 +04:00
goto exit ;
}
err = platform_device_add_resources ( pdev , & res , 1 ) ;
if ( err ) {
2011-01-12 23:55:10 +03:00
pr_err ( " Device resource addition failed (%d) \n " , err ) ;
2007-05-08 19:22:00 +04:00
goto exit_device_put ;
}
err = platform_device_add ( pdev ) ;
if ( err ) {
2011-01-12 23:55:10 +03:00
pr_err ( " Device addition failed (%d) \n " , err ) ;
2007-05-08 19:22:00 +04:00
goto exit_device_put ;
}
return 0 ;
exit_device_put :
platform_device_put ( pdev ) ;
exit :
pdev = NULL ;
return err ;
}
2011-07-25 23:46:11 +04:00
static int __init lm78_isa_register ( void )
2005-04-17 02:20:36 +04:00
{
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 01:51:07 +04:00
int res ;
2007-05-08 19:22:00 +04:00
if ( lm78_isa_found ( isa_address ) ) {
res = platform_driver_register ( & lm78_isa_driver ) ;
if ( res )
2008-10-17 19:51:15 +04:00
goto exit ;
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 01:51:07 +04:00
2007-05-08 19:22:00 +04:00
/* Sets global pdev as a side effect */
res = lm78_isa_device_add ( isa_address ) ;
if ( res )
goto exit_unreg_isa_driver ;
}
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 01:51:07 +04:00
return 0 ;
2007-05-08 19:22:00 +04:00
exit_unreg_isa_driver :
platform_driver_unregister ( & lm78_isa_driver ) ;
exit :
return res ;
2005-04-17 02:20:36 +04:00
}
2011-07-25 23:46:11 +04:00
static void lm78_isa_unregister ( void )
2005-04-17 02:20:36 +04:00
{
2007-05-08 19:22:00 +04:00
if ( pdev ) {
platform_device_unregister ( pdev ) ;
platform_driver_unregister ( & lm78_isa_driver ) ;
}
2011-07-25 23:46:11 +04:00
}
# else /* !CONFIG_ISA */
static int __init lm78_isa_register ( void )
{
return 0 ;
}
static void lm78_isa_unregister ( void )
{
}
# endif /* CONFIG_ISA */
static int __init sm_lm78_init ( void )
{
int res ;
2012-01-15 08:39:24 +04:00
/*
* We register the ISA device first , so that we can skip the
* registration of an I2C interface to the same device .
*/
2011-07-25 23:46:11 +04:00
res = lm78_isa_register ( ) ;
if ( res )
goto exit ;
res = i2c_add_driver ( & lm78_driver ) ;
if ( res )
goto exit_unreg_isa_device ;
return 0 ;
exit_unreg_isa_device :
lm78_isa_unregister ( ) ;
exit :
return res ;
}
static void __exit sm_lm78_exit ( void )
{
lm78_isa_unregister ( ) ;
2005-04-17 02:20:36 +04:00
i2c_del_driver ( & lm78_driver ) ;
}
2011-07-25 23:46:11 +04:00
MODULE_AUTHOR ( " Frodo Looijaard, Jean Delvare <khali@linux-fr.org> " ) ;
2005-07-27 23:30:16 +04:00
MODULE_DESCRIPTION ( " LM78/LM79 driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_init ( sm_lm78_init ) ;
module_exit ( sm_lm78_exit ) ;