/* * 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 #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*/