2019-05-20 10:19:02 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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 >
2014-01-29 23:40:08 +04:00
* Copyright ( c ) 2007 , 2011 Jean Delvare < jdelvare @ suse . de >
2012-01-15 08:39:24 +04:00
*/
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 )
{
2013-01-09 20:09:34 +04:00
unsigned long nval = clamp_val ( val , 0 , 4080 ) ;
2005-04-17 02:20:36 +04:00
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 ;
2013-12-12 11:05:33 +04:00
if ( rpm > 1350000 )
return 1 ;
2013-01-09 20:09:34 +04:00
return clamp_val ( ( 1350000 + rpm * div / 2 ) / ( rpm * div ) , 1 , 254 ) ;
2005-04-17 02:20:36 +04:00
}
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
*/
2014-07-30 07:48:59 +04:00
static inline s8 TEMP_TO_REG ( long val )
2005-04-17 02:20:36 +04:00
{
2013-01-09 20:09:34 +04:00
int nval = clamp_val ( 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 ;
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 ;
2021-09-24 22:52:02 +03:00
bool valid ; /* true if following fields are valid */
2005-04-17 02:20:36 +04:00
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 */
2019-01-22 21:07:16 +03:00
static ssize_t in_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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
}
2019-01-22 21:07:16 +03:00
static ssize_t in_min_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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
}
2019-01-22 21:07:16 +03:00
static ssize_t in_max_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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
}
2019-01-22 21:07:16 +03:00
static ssize_t in_min_store ( 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 ;
}
2019-01-22 21:07:16 +03:00
static ssize_t in_max_store ( 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
2019-01-22 21:07:16 +03:00
static SENSOR_DEVICE_ATTR_RO ( in0_input , in , 0 ) ;
static SENSOR_DEVICE_ATTR_RW ( in0_min , in_min , 0 ) ;
static SENSOR_DEVICE_ATTR_RW ( in0_max , in_max , 0 ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_input , in , 1 ) ;
static SENSOR_DEVICE_ATTR_RW ( in1_min , in_min , 1 ) ;
static SENSOR_DEVICE_ATTR_RW ( in1_max , in_max , 1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in2_input , in , 2 ) ;
static SENSOR_DEVICE_ATTR_RW ( in2_min , in_min , 2 ) ;
static SENSOR_DEVICE_ATTR_RW ( in2_max , in_max , 2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in3_input , in , 3 ) ;
static SENSOR_DEVICE_ATTR_RW ( in3_min , in_min , 3 ) ;
static SENSOR_DEVICE_ATTR_RW ( in3_max , in_max , 3 ) ;
static SENSOR_DEVICE_ATTR_RO ( in4_input , in , 4 ) ;
static SENSOR_DEVICE_ATTR_RW ( in4_min , in_min , 4 ) ;
static SENSOR_DEVICE_ATTR_RW ( in4_max , in_max , 4 ) ;
static SENSOR_DEVICE_ATTR_RO ( in5_input , in , 5 ) ;
static SENSOR_DEVICE_ATTR_RW ( in5_min , in_min , 5 ) ;
static SENSOR_DEVICE_ATTR_RW ( in5_max , in_max , 5 ) ;
static SENSOR_DEVICE_ATTR_RO ( in6_input , in , 6 ) ;
static SENSOR_DEVICE_ATTR_RW ( in6_min , in_min , 6 ) ;
static SENSOR_DEVICE_ATTR_RW ( in6_max , in_max , 6 ) ;
2005-04-17 02:20:36 +04:00
/* Temperature */
2016-12-22 15:05:25 +03:00
static ssize_t temp1_input_show ( 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 ) ) ;
}
2016-12-22 15:05:25 +03:00
static ssize_t temp1_max_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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 ) ) ;
}
2016-12-22 15:05:25 +03:00
static ssize_t temp1_max_store ( 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 ;
}
2016-12-22 15:05:25 +03:00
static ssize_t temp1_max_hyst_show ( 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 ) ) ;
}
2016-12-22 15:05:25 +03:00
static ssize_t temp1_max_hyst_store ( 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 ;
}
2016-12-22 15:05:25 +03:00
static DEVICE_ATTR_RO ( temp1_input ) ;
static DEVICE_ATTR_RW ( temp1_max ) ;
static DEVICE_ATTR_RW ( temp1_max_hyst ) ;
2005-04-17 02:20:36 +04:00
/* 3 Fans */
2019-01-22 21:07:16 +03:00
static ssize_t fan_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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
}
2019-01-22 21:07:16 +03:00
static ssize_t fan_min_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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
}
2019-01-22 21:07:16 +03:00
static ssize_t fan_min_store ( 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 ;
}
2019-01-22 21:07:16 +03:00
static ssize_t fan_div_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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 .
*/
2019-01-22 21:07:16 +03:00
static ssize_t fan_div_store ( 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 :
2013-01-10 22:01:24 +04:00
dev_err ( dev ,
" fan_div value %ld not 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 ;
}
2019-01-22 21:07:16 +03:00
static SENSOR_DEVICE_ATTR_RO ( fan1_input , fan , 0 ) ;
static SENSOR_DEVICE_ATTR_RW ( fan1_min , fan_min , 0 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan2_input , fan , 1 ) ;
static SENSOR_DEVICE_ATTR_RW ( fan2_min , fan_min , 1 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan3_input , fan , 2 ) ;
static SENSOR_DEVICE_ATTR_RW ( fan3_min , fan_min , 2 ) ;
2005-04-17 02:20:36 +04:00
/* Fan 3 divisor is locked in H/W */
2019-01-22 21:07:16 +03:00
static SENSOR_DEVICE_ATTR_RW ( fan1_div , fan_div , 0 ) ;
static SENSOR_DEVICE_ATTR_RW ( fan2_div , fan_div , 1 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan3_div , fan_div , 2 ) ;
2005-04-17 02:20:36 +04:00
/* VID */
2016-12-22 15:05:25 +03:00
static ssize_t cpu0_vid_show ( 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
}
2016-12-22 15:05:25 +03:00
static DEVICE_ATTR_RO ( cpu0_vid ) ;
2005-04-17 02:20:36 +04:00
/* Alarms */
2016-12-22 15:05:25 +03:00
static ssize_t alarms_show ( struct device * dev , struct device_attribute * da ,
2007-05-08 19:22:01 +04:00
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 ) ;
}
2016-12-22 15:05:25 +03:00
static DEVICE_ATTR_RO ( alarms ) ;
2005-04-17 02:20:36 +04:00
2019-01-22 21:07:16 +03:00
static ssize_t alarm_show ( struct device * dev , struct device_attribute * da ,
2007-09-05 01:25:33 +04:00
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 ) ;
}
2019-01-22 21:07:16 +03:00
static SENSOR_DEVICE_ATTR_RO ( in0_alarm , alarm , 0 ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_alarm , alarm , 1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in2_alarm , alarm , 2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in3_alarm , alarm , 3 ) ;
static SENSOR_DEVICE_ATTR_RO ( in4_alarm , alarm , 8 ) ;
static SENSOR_DEVICE_ATTR_RO ( in5_alarm , alarm , 9 ) ;
static SENSOR_DEVICE_ATTR_RO ( in6_alarm , alarm , 10 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan1_alarm , alarm , 6 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan2_alarm , alarm , 7 ) ;
static SENSOR_DEVICE_ATTR_RO ( fan3_alarm , alarm , 11 ) ;
static SENSOR_DEVICE_ATTR_RO ( temp1_alarm , alarm , 4 ) ;
2007-09-05 01:25:33 +04:00
2014-07-23 08:39:21 +04:00
static struct attribute * lm78_attrs [ ] = {
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
} ;
2014-07-23 08:39:21 +04:00
ATTRIBUTE_GROUPS ( lm78 ) ;
2006-09-24 22:59:49 +04:00
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 ;
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 ) ) {
2013-01-10 22:01:24 +04:00
dev_dbg ( & adapter - > dev ,
" Device at 0x%02x appears to be the same as ISA device \n " ,
address ) ;
2009-12-09 22:35:57 +03:00
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 ) ;
2022-08-19 00:00:11 +03:00
strscpy ( 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 ;
}
2020-08-13 19:02:22 +03:00
static const struct i2c_device_id lm78_i2c_id [ ] ;
static int lm78_i2c_probe ( struct i2c_client * client )
2008-10-17 19:51:16 +04:00
{
2014-07-23 08:39:21 +04:00
struct device * dev = & client - > dev ;
struct device * hwmon_dev ;
2008-10-17 19:51:16 +04:00
struct lm78_data * data ;
2014-07-23 08:39:21 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm78_data ) , GFP_KERNEL ) ;
2008-10-17 19:51:16 +04:00
if ( ! data )
return - ENOMEM ;
data - > client = client ;
2020-08-13 19:02:22 +03:00
data - > type = i2c_match_id ( lm78_i2c_id , client ) - > 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
2014-07-23 08:39:21 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data , lm78_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2007-05-08 19:22:00 +04:00
}
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 " ,
} ,
2023-05-05 16:17:18 +03:00
. probe = lm78_i2c_probe ,
2011-07-25 23:46:11 +04:00
. 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 ;
2021-09-24 22:52:02 +03:00
data - > valid = true ;
2005-04-17 02:20:36 +04:00
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
{
2014-07-23 08:39:21 +04:00
struct device * dev = & pdev - > dev ;
struct device * hwmon_dev ;
2011-07-25 23:46:11 +04:00
struct lm78_data * data ;
struct resource * res ;
/* Reserve the ISA region */
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
2014-07-23 08:39:21 +04:00
if ( ! devm_request_region ( dev , res - > start + LM78_ADDR_REG_OFFSET ,
2012-06-02 20:58:09 +04:00
2 , " lm78 " ) )
return - EBUSY ;
2014-07-23 08:39:21 +04:00
data = devm_kzalloc ( dev , sizeof ( struct lm78_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:09 +04:00
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 ) ;
2014-07-23 08:39:21 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , data - > name ,
data , lm78_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2011-07-25 23:46:11 +04:00
}
static struct platform_driver lm78_isa_driver = {
. driver = {
. name = " lm78 " ,
} ,
. probe = lm78_isa_probe ,
} ;
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 ) ;
}
2014-01-29 23:40:08 +04:00
MODULE_AUTHOR ( " Frodo Looijaard, Jean Delvare <jdelvare@suse.de> " ) ;
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 ) ;