/* * Device Driver for National Semiconductor LM78 */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "interp.h" #include #include "runt.h" enum { /* Define the memory locations, registers, * and bit patterns for the nslm78 chip */ /* Define Address Register Port 0x05 */ ADDRESS_PORT = 0x05, /* Define Addresses of the various Registers in LM78 */ CONFIG_REG = 0x40, INTR_STATUS_REG1 = 0x41, INTR_STATUS_REG2 = 0x42, SMI_MASK_REG1 = 0x43, SMI_MASK_REG2 = 0x44, NMI_MASK_REG1 = 0x45, NMI_MASK_REG2 = 0x46, VID_FAN_REG = 0x47, SERIAL_BUS_ADDR_REG = 0x48, CHIP_RESET = 0x49, VALUE_RAM_BASE_1 = 0x20, VALUE_RAM_BASE_2 = 0x60, VALUE_RAM_LIMIT_2B = 0x6B, VALUE_RAM_LIMIT_2E = 0x7D, ARRAY_SIZE = (VALUE_RAM_LIMIT_2E - VALUE_RAM_LIMIT_2B) + 1, /* Define Data Register Port 0x06 */ DATA_PORT = 0x06, /* Define bit patterns for Configuration Register */ ONE_BIT = 0x01, START_MONITOR = ONE_BIT, SMI_ENABLE = (ONE_BIT<<1), NMI_IRQ_ENABLE = (ONE_BIT<<2), INT_CLEAR = (ONE_BIT<<3), RESET = (ONE_BIT<<4), NMI_IRQ_SELECT = (ONE_BIT<<5), INITIALIZE = (ONE_BIT<<7), /* For now, we are not interested in handling the interrupts. */ /* No bit patterns are defined for those interrupt registers */ /* Do not plan to use Serial Bus Address */ /* Define Chip Reset/ID Register for LM78 */ CHIP_RESET_REG = 0x49, /* Do not plan to use POST RAM (Power On Self Test) */ /* VALUE RAM Addresses for the Readings and Watchlog Limits */ IN0_RD = 0x60, IN1_RD = 0x61, IN2_RD = 0x62, IN3_RD = 0x63, IN4_RD = 0x64, IN5_RD = 0x65, IN6_RD = 0x66, TEMP_RD = 0x67, FAN1_RD = 0x68, FAN2_RD = 0x69, FAN3_RD = 0x6A, IN0_H = 0x6B, IN0_L = 0x6C, IN1_H = 0x6D, IN1_L = 0x6E, IN2_H = 0x6F, IN2_L = 0x70, IN3_H = 0x71, IN3_L = 0x72, IN4_H = 0x73, IN4_L = 0x74, IN5_H = 0x75, IN5_L = 0x76, IN6_H = 0x77, IN6_L = 0x78, TEMP_H = 0x79, TEMP_L = 0x7A, FAN1_CNT = 0x7B, FAN2_CNT = 0x7C, FAN3_CNT = 0x7D }; enum { Qdir, Qtemp, Qfan1, Qfan2, Qfan3, Qvolt1, Qvolt2, Qvolt3, Qvolt4, Qvolt5, Qvolt6, Qvolt7, Qalert, }; Dirtab LM78tab[]={ "temp", {Qtemp, 0}, 0, 0666, "fan1", {Qfan1, 0}, 0, 0666, "fan2", {Qfan2, 0}, 0, 0666, "fan3", {Qfan3, 0}, 0, 0666, "volt1", {Qvolt1, 0}, 0, 0666, "volt2", {Qvolt2, 0}, 0, 0666, "volt3", {Qvolt3, 0}, 0, 0666, "volt4", {Qvolt4, 0}, 0, 0666, "volt5", {Qvolt5, 0}, 0, 0666, "volt6", {Qvolt6, 0}, 0, 0666, "volt7", {Qvolt7, 0}, 0, 0666, "alert", {Qalert, 0}, 0, 0666, }; #define NLM78tab nelem(LM78tab) #define RAM_SIZE 30 #define NFIELD 2 #define NBUFSIZE 128 #define N_DOT_PLACE 3 ulong miBASE; /* LM78 base address */ static int array_RAM[RAM_SIZE]; static int FanPulsesPerRev[] = { 0, 2, 2, 2 }; static int VoltageScale[] = { 0, 16, 16, 27, 60, 60, 16, 27 }; /* per CPV5000 LM78 Access */ /* volt1 for 3.3 v */ /* volt2 for cpu v (2.7 v) */ /* volt3 for 5.0 v */ /* volt4 for 12 v */ /* volt5 for -12 v */ static int default_val[] = { 250, 187, 250, 125, 222, 148, 250, 167, 250, 167, 250, 187, 222, 148, 35, 40, 219, 218, 217 }; /* voltages and fan speeds are converted to scales */ void write_routine(int value, int ival); static void LM78reset(void) /* Reset the LM78 Chip */ { } static void LM78detach(void) { } static int power(int x, int n) { int i, p; p = 1; for (i = 1; i <= n; ++i) p = p * x; return p; } static void write_ram (void) { int i; outb(miBASE+ADDRESS_PORT, VALUE_RAM_LIMIT_2B); for (i=0; i < ARRAY_SIZE; i++) { outb(miBASE+DATA_PORT, default_val[i]); } return; } static void set_intr_mask_regs(void) { write_routine(0xe0, SMI_MASK_REG1); write_routine(0x7f, SMI_MASK_REG2); /* mask the interrupt status bits */ write_routine(0xff, NMI_MASK_REG1); write_routine(0x7f, NMI_MASK_REG2); return; } static void start_monitor(int conf_reg_content ) { conf_reg_content |= START_MONITOR; conf_reg_content &= ~INT_CLEAR ; conf_reg_content &= ~INITIALIZE ; outb( miBASE+ADDRESS_PORT, CONFIG_REG); outb( miBASE+DATA_PORT, conf_reg_content); return; } static int read_vid(void) { int i,x; x = splhi(); outb( miBASE+ADDRESS_PORT, 0x47); /* tell the chip, we want to */ /* read VID */ i = inb(miBASE+DATA_PORT); splx(x); return i; } int readpcilm78(void) { int pcs; Pcidev *p; p = pcimatch(0, 0x8086, 0x7000); if(p == nil) return -1; pcs = pcicfgr16 (p, 0x78); if(pcs & 1) { miBASE = pcs & ~3; return 0; } pcicfgw16(p, 0x78, 0x51); /* enable the chip, use default address 0x50 */ pcs = pcicfgr16(p, 0x78); miBASE = pcs & ~3; return 0; } static void LM78init(void) { int config_reg_content; if (readpcilm78() == -1) error("LM78 Not Initialized\n"); outb( miBASE+ADDRESS_PORT, CONFIG_REG); config_reg_content = inb(miBASE+DATA_PORT); /* Read in the config reg content */ config_reg_content |= INITIALIZE; /* After Power on the config_reg */ /* has value 0000 1000 */ /* For reset with all interrupt */ /* disabled, the config_reg is */ /* to 1000 1000 */ outb( miBASE+ADDRESS_PORT, CONFIG_REG); outb( miBASE+DATA_PORT, config_reg_content); write_ram(); /* write to WATCHDOG RAM the default values */ read_vid(); set_intr_mask_regs(); start_monitor(config_reg_content); } static Chan* LM78attach(char *spec) { return devattach('L', spec); } static Chan* LM78clone(Chan* c, Chan* nc) { return devclone(c, nc); } static int LM78walk(Chan* c, char* name) { return devwalk(c, name, LM78tab, NLM78tab, devgen); } static void LM78stat(Chan* c, char* db) { devstat(c, db, LM78tab, NLM78tab, devgen); } static Chan* LM78open(Chan* c, int omode) { return devopen(c, omode, LM78tab, NLM78tab, devgen); } static void LM78create(Chan* c, char* name, int omode, ulong perm) { USED(c, name, omode, perm); error(Eperm); } static void LM78remove(Chan* c) { USED(c); error(Eperm); } static void LM78wstat(Chan* c, char* dp) { USED(c, dp); error(Eperm); } static void LM78close(Chan* c) { USED(c); } static int value_f(int f, int n_digit) { int i, dvalue; dvalue = 0; for (i = n_digit; i >= 1; i--) { dvalue += (f%10) * power(10, N_DOT_PLACE-i); f /= 10; } return dvalue; } static int aatof(char * str_ptr) { int i, f; char *p; char *q; i = strtol(str_ptr, &p, 10); if (p == str_ptr) error ("no digits\n"); if (*p == 0) return (i * 1000); f = strtol(p+1, &q,10); /* do the part after the period */ if (q > p+N_DOT_PLACE+1) error("more than three digits after period\n"); if (q == p+1) return (i*1000); if (q == p+2) return (i*1000 + value_f( f, 1)); if (q == p+3) return (i*1000 + value_f( f, 2)); if (q == p+4) return (i*1000 + value_f( f, 3)); } void reverse(char s[]) { int c, i, j; for (i = 0, j = strlen(s) -1; i < j; i++, j--) { c = s[i]; s[i] = s[j]; s[j] = c; } } static void itoreal(int n, char s[], int dot_place) { int i, sign; if ((sign =n) < 0) n = -n; i = 0; do { if (i != dot_place) { s[i++] = n % 10 + '0' ; n /= 10; } else s[i++] = '.'; } while (n >0); if ( (sign > 0 ) && sign < power(10, dot_place)) { s[i++] = '.'; }; if ( (sign < 0 ) && (-sign < power(10, dot_place))) { s[i++] = '.'; s[i++] = '-'; }; if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); } static int convert_to_temp(int val) { if (val == 0xff) return 0; if (val >= 0xc9) return ( -((~val & 0177) + 1)); return val; } static int rpm_to_count(char* rpm, int index) { int n; n = 1350000/(FanPulsesPerRev[index] * aatof(rpm)/1000); if (n > 255) error(Ebadarg); return n; } static int convert_to_rpm(int val, int index ) { if ((val==255) || (val == 0)) return (0); else return(1350000/(FanPulsesPerRev[index] * val)); } static char* convert_to_volt(int val, int index) { int i; char *vstring = malloc(12); if (vstring == 0) error("no memory"); i = val * VoltageScale[index]; itoreal(i, vstring, N_DOT_PLACE); return vstring; } static int voltage_to_scale( char *volt, int index) { int n; n = aatof(volt) / VoltageScale[index]; if (n > 255) error(Ebadarg); else return n; } static int read_routine(int ivalue) { int x, iv; x = splhi(); /* turn off all maskable interrupts */ outb(miBASE+ADDRESS_PORT, ivalue); iv = inb(miBASE+DATA_PORT ); splx(x); return iv; } static long LM78read(Chan* c, void* a, long n, ulong offset) { int value, lo, hi, fannum, v_num; int f_value, f_hi, f_lo, intr1, intr2; char *v_str, *v_hi, *v_lo; char buf[NBUFSIZE]; USED(offset); v_str = nil; v_hi = nil; v_lo = nil; f_hi = 0; switch(c->qid.path & ~CHDIR){ case Qdir: return devdirread(c,a,n,LM78tab, NLM78tab,devgen); case Qtemp: /* read temperature from RAM and pass it back via *a */ f_value = convert_to_temp(read_routine(TEMP_RD)); f_hi = convert_to_temp(read_routine(TEMP_H)); f_lo = convert_to_temp(read_routine(TEMP_L)); break; case Qfan1: fannum = 1; value = read_routine(FAN1_RD); f_value = convert_to_rpm(value, fannum); lo = read_routine(FAN1_CNT); f_lo = convert_to_rpm(lo, fannum); break; case Qfan2: fannum = 2; value = read_routine(FAN2_RD); f_value = convert_to_rpm(value, fannum); lo = read_routine(FAN2_CNT); f_lo = convert_to_rpm(lo, fannum); break; case Qfan3: fannum = 3; value = read_routine(FAN3_RD); f_value = convert_to_rpm(value, fannum); lo = read_routine(FAN3_CNT); f_lo = convert_to_rpm(lo, fannum); break; default: switch(c->qid.path & ~CHDIR){ case Qvolt1: v_num = 1; value = read_routine(IN0_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN0_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN0_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt2: v_num = 2; value = read_routine(IN1_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN1_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN1_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt3: v_num = 3; value = read_routine(IN2_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN2_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN2_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt4: v_num = 4; value = read_routine(IN3_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN3_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN3_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt5: v_num = 5; value = read_routine(IN4_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN4_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN4_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt6: v_num =6; value = read_routine(IN5_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN5_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN5_L); v_lo = convert_to_volt(lo, v_num); break; case Qvolt7: v_num = 7; value = read_routine(IN6_RD); v_str = convert_to_volt(value, v_num); hi = read_routine(IN6_H); v_hi = convert_to_volt(hi, v_num); lo = read_routine(IN6_L); v_lo = convert_to_volt(lo, v_num); break; case Qalert: /* read interrupt status registers */ /* delay(15*100); */ intr1 = read_routine(INTR_STATUS_REG1); intr2 = read_routine(INTR_STATUS_REG2); intr1 &= 0x001F; intr2 &= 0x001F; n = sprint(buf, "%d", intr1); return readstr(offset, a, n, buf); /* deliver data to address a */ default: error(Ebadarg); } /* get ready to send to kernel */ sprint(buf, "%s %s %s", v_str, v_lo, v_hi); free(v_str); free(v_hi); free(v_lo); return readstr(offset, a, n, buf); } n = sprint(buf, "%d %d %d", f_value, f_lo, f_hi); return readstr(offset, a, n, buf); /* deliver data to address a */ } void write_routine(int value, int ival) { int x; x = splhi(); /* turn off all maskable interrupts */ /* take the input from *a convert to int */ /* and write it to the RAM */ outb( miBASE+ADDRESS_PORT, ival); outb( miBASE+DATA_PORT, value); splx(x); return; } int tempvalue( char *ptr) { int temp_digit; char *p; temp_digit = strtol(ptr, &p, 10); if (p == ptr) error(Ebadarg); if (temp_digit > 125) error(Ebadarg); return temp_digit; } static Block* LM78bread(Chan* c, long n, ulong offset) { return devbread(c, n, offset); } static long LM78write(Chan* c, char* a, long n, ulong offset) { int nf, fan_num, v_num; char *field[NFIELD], buf[NBUFSIZE]; USED(offset); if(n > sizeof(buf)-1) n = sizeof(buf)-1; memmove(buf, a, n); buf[n] = '\0'; nf = parsefields(buf, field, NFIELD, " \t"); USED(nf); switch(c->qid.path & ~CHDIR){ case Qtemp: write_routine(tempvalue(field[1]), TEMP_H); delay(1000); write_routine(tempvalue(field[0]), TEMP_L); break; case Qfan1: fan_num = 1; write_routine(rpm_to_count(field[0], fan_num), FAN1_CNT); break; case Qfan2: fan_num = 2; write_routine(rpm_to_count(field[0], fan_num), FAN2_CNT); break; case Qfan3: fan_num = 3; write_routine(rpm_to_count(field[0], fan_num), FAN3_CNT); break; case Qvolt1: v_num = 1; write_routine(voltage_to_scale(field[1], v_num), IN0_H); write_routine(voltage_to_scale(field[0], v_num), IN0_L); break; case Qvolt2: v_num = 2; write_routine(voltage_to_scale(field[1], v_num), IN1_H); write_routine(voltage_to_scale(field[0], v_num), IN1_L); break; case Qvolt3: v_num = 3; write_routine(voltage_to_scale(field[1], v_num), IN2_H); write_routine(voltage_to_scale(field[0], v_num), IN2_L); break; case Qvolt4: v_num = 4; write_routine(voltage_to_scale(field[1], v_num), IN3_H); write_routine(voltage_to_scale(field[0], v_num), IN3_L); break; case Qvolt5: v_num = 5; write_routine(voltage_to_scale(field[1], v_num), IN4_H); write_routine(voltage_to_scale(field[0], v_num), IN4_L); break; case Qvolt6: v_num = 6; write_routine(voltage_to_scale(field[1], v_num), IN5_H); write_routine(voltage_to_scale(field[0], v_num), IN5_L); break; case Qvolt7: v_num = 7; write_routine(voltage_to_scale(field[1], v_num), IN6_H); write_routine(voltage_to_scale(field[0], v_num), IN6_L); break; default: error(Ebadarg); } return n; } static long LM78bwrite(Chan* c, Block* bp, ulong offset) { return devbwrite(c, bp, offset); } Dev LM78devtab = { 'L', "LM78", LM78reset, LM78init, LM78attach, LM78detach, LM78clone, LM78walk, LM78stat, LM78open, LM78create, LM78close, LM78read, LM78bread, LM78write, LM78bwrite, LM78remove, LM78wstat, };