@@ 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 <isa.h>
+#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,
+};