From 6570d4be4f56aa4b1b8999f49df44c5446e64325 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 8 Jul 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-07-08 --- pc/devlm78.c | 796 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 796 insertions(+) create mode 100644 pc/devlm78.c diff --git a/pc/devlm78.c b/pc/devlm78.c new file mode 100644 index 0000000000000000000000000000000000000000..7126b7a8062f27bb639dd8c1ad7813eac3c729b4 --- /dev/null +++ b/pc/devlm78.c @@ -0,0 +1,796 @@ + +/* + * 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; +} + +enum +{ + IntelVendID= 0x8086, + PiixBridgeID= 0x122E, + Piix3BridgeID= 0x7000, + Piix4BridgeID= 0x7110, + + PCSC= 0x78, /* programmable chip select control register */ + PCSC8bytes= 0x01, +}; + +int +readpcilm78(void) +{ + int pcs; + Pcidev *p; + + p = nil; + while((p = pcimatch(p, IntelVendID, 0)) != nil){ + switch(p->did){ + case PiixBridgeID: + case Piix3BridgeID: + case Piix4BridgeID: + break; + default: + continue; + } + break; + } + if(p == nil) + return -1; + + pcs = pcicfgr16(p, PCSC); + if(pcs & 3) { + /* already enabled */ + miBASE = pcs & ~3; + return 0; + } + + /* enable the chip, use default address 0x50 */ + pcicfgw16(p, PCSC, 0x50|PCSC8bytes); + pcs = pcicfgr16(p, PCSC); + 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, +};