/*
* Natinal Semiconductor LM80 hardware monitor chip
*
***************************************************************
* Before calling these routines, one must call method->Open() *
* After calling these routines, one must call method->Close() *
***************************************************************
*
National Semiconductor
Chip Temp Volt Fan SMBus IOport
lm80 1 7 2 yes no
*
* Copyright Shin-ichi Nagamura
*/
#include <stdio.h>
#include "sensors.h"
/* external (global) data */
extern int smb_slave;
extern LM_METHODS method_smb;
#undef DEBUG
#define LM80_ADDR_START 0x50 /*0x50-0x5E*/
#define LM80_ADDR_END 0x5E
#define LM80_CONFIG 0x00
#define LM80_ISR1 0x01
#define LM80_ISR2 0x02
#define LM80_FANDIV 0x05
#define LM80_RESOLUTION 0x06
#define LM80_IN0 0x20
#define LM80_TEMPERATURE 0x27
#define LM80_FANRPM1 0x28
#define LM80_FANRPM2 0x29
#define LM80_CONFIG_START 0x01
#define LM80_CONFIG_INTEN 0x02
#define LM80_CONFIG_INTOPEN 0x04
#define LM80_CONFIG_INTCLR 0x08
#define LM80_CONFIG_RESET 0x10
#define LM80_CONFIG_CHACLR 0x20
#define LM80_CONFIG_GPO 0x40
#define LM80_CONFIG_INIT 0x80
#define LM80_R06_OSACTHI 0x02
#define LM80_R06_TEMPRES 0x08
#define LM80_R06_TEMP11 0xF0
#define LM80_R06_TEMP8 0x80
static int lm80_probe(LM_METHODS *);
static float lm80_temp(LM_METHODS *, int);
static int lm80_fanrpm(LM_METHODS *, int);
static float lm80_volt(LM_METHODS *, int);
#if defined(DEBUG)
static void dumpreg( void );
#endif /*DEBUG*/
SENSOR lm80 = {
"Nat.Semi.Con. Chip LM80",
lm80_probe,
lm80_temp,
lm80_volt,
lm80_fanrpm
};
#define LM80_chkRegNum 20
/* Register checked for probing */
static int chkReg[] = {
0x00, 0x01, 0x02, 0x04,
0x05, 0x06, 0x20, 0x21,
0x22, 0x24, 0x25, 0x26,
0x27, 0x28, 0x29, 0x2B,
0x2D, 0x2F, 0x31, 0x33,
0x35, 0x37, 0x39, 0x3B,
0x3C, 0x3D,
-1 };
/*
* return 0 if not probed
*/
static int lm80_probe(LM_METHODS *method)
{
int n, dat, reg, save;
if( method != &method_smb )
return 0;
save = smb_slave;
for (n = LM80_ADDR_START; n <= LM80_ADDR_END;) {
if (!(smb_slave = get_smb_slave(n, LM80_ADDR_END)))
goto ret0;
else {
if (method->Read(LM80_ISR2) & 0xC0) {
n = smb_slave + 2;
continue;
}
for (reg = 0x2A; reg <= 0x3D; reg++) {
dat = method->Read(reg);
if (method->Read(reg + 0x40) != dat ||
method->Read(reg + 0x80) != dat ||
method->Read(reg + 0xC0) != dat)
break;
}
if (reg > 0x3D
&& chkReg_Probe(smb_slave, "Probing LM80 chip:\n",
chkReg, method) >= LM80_chkRegNum)
goto ret1;
else
n = smb_slave + 2;
}
}
ret0:
smb_slave = save;
return 0;
ret1:
if((method->Read(LM80_CONFIG ) & LM80_CONFIG_START) == 0) {
method->Write(LM80_CONFIG, LM80_CONFIG_RESET);
method->Write(LM80_CONFIG, LM80_CONFIG_START);
}
method->Write(LM80_FANDIV,
(method->Read(LM80_FANDIV) & 0x3C) | 0x40);
method->Write(LM80_RESOLUTION,
LM80_R06_TEMPRES | LM80_R06_OSACTHI);
#if defined(DEBUG)
dumpreg();
#endif /*DEBUG*/
kill_smb_slave(smb_slave);
return 1;
}
/*!
* \retval 0xFFFF no sensor
* \retval other temperature
* no = 0,1,2,...
*/
static float lm80_temp(LM_METHODS *method, int no)
{
int reg, val, sft;
float ret;
if (no != 0)
return 0xFFFF;
reg = method->Read(LM80_RESOLUTION);
val = method->Read(LM80_TEMPERATURE);
sft = ((( reg & LM80_R06_TEMPRES ) >> 3 ) * 3 ) + 1;
val <<= sft;
val |= reg >> (8 - sft);
ret = (float)val;
if (reg & LM80_R06_TEMPRES)
ret *= 0.0625;
else
ret *= 0.5;
return ret;
}
/*!
* \retval 0x0000FFFF no sensor
* no = 0,1,2,...
*/
static float lm80_volt(LM_METHODS *method, int no)
{
const float r1[7] = { 23.7, 23.7, 22.1, 24, 160, 27, 180 };
const float r2[7] = { 75, 75, 30, 14.7, 30.1, 3.4, 42.2 };
float vout, val;
if( no < 0 || 6 < no )
return 0xFFFF;
vout = method->Read(LM80_IN0 + no);
vout *= 0.01; /*LSB is 10mv*/
/*
*VOUT = ( VCC * R2 ) / ( R1 + R2 )
*VCC = ( VOUT * ( R1 + R2 )) / R2
*/
val = ( vout * ( r1[no] + r2[no] )) / r2[no];
if( no >= 5 )
val = ( val - 5 ) * -1;
/*
* ((( vout * ( r1[no] + r2[no] )) / r2[no] ) - 5 ) * -1;
*/
return val;
}
/*!
* \retval 0x0000FFFF no sensor
* \retval 0x00010000 unknown (maybe no sensor)
* no = 0,1,2,...
*
* Clock is 22.5kHz (22,500 x 60 = 1350000 counts/minute)
*/
static int lm80_fanrpm(LM_METHODS *method, int no)
{
int reg, val;
int div, sft;
long rpm;
#if defined(DEBUG)
printf("lm80_fanrpm(): no=%d\n", no);
#endif /*DEBUG*/
if (no < 0 || 1 < no)
return 0xFFFF;
reg = method->Read(LM80_FANDIV);
val = method->Read(LM80_FANRPM1 + no);
#if defined(DEBUG)
printf("lm80_fanrpm(): reg=0x%02X, val=0x%02X\n", reg, val);
#endif /*DEBUG*/
sft = (no + 1) * 2;
div = (reg >> sft) & 0x03;
#if defined(DEBUG)
printf( "lm80_fanrpm(): sft=%d, div=%d\n", sft, div );
#endif /*DEBUG*/
if (val == 0xFF) {
if (div < 3) {
reg += 1 << sft;
(*method->Write)(LM80_FANDIV, reg);
}
#if defined(DEBUG)
printf( "lm80_fanrpm(): write new reg 0x%02X\n", reg );
#endif /*DEBUG*/
return 0x10000;
}
#if defined(DEBUG)
printf( "lm80_fanrpm(): val=%d, div=%d\n", val, div );
#endif /*DEBUG*/
rpm = 1350000 / ( val * ( 1 << div ));
return rpm;
}
#if defined(DEBUG)
static void dumpreg(void)
{
int cmd;
printf( "SlaveAddress=0x%04X", smb_slave );
#if 0
printf( "Reg00 :" );
for( cmd=0; cmd<=6; cmd++ )
printf( " 0x%02X", (*method_smb.Read)(cmd));
#endif
for( cmd=0x00; cmd<=0x7F; cmd++ ) {
if(( cmd & 0x0F ) == 0 )
printf( "\nReg%02X :", cmd );
printf("%c%02X", ((cmd&0x0F)==8)?'-':' ',(*method_smb.Read)(cmd));
}
printf("\n");
}
#endif /*DEBUG*/
syntax highlighted by Code2HTML, v. 0.9.1