## diffname pc/devlm78.c 1999/0708
## diff -e /dev/null /n/emeliedump/1999/0708/sys/src/brazil/pc/devlm78.c
0a
/*
* 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,
};
.
## diffname pc/devlm78.c 1999/0709
## diff -e /n/emeliedump/1999/0708/sys/src/brazil/pc/devlm78.c /n/emeliedump/1999/0709/sys/src/brazil/pc/devlm78.c
269,274c
/* this bridge uses the SMbus */
case Piix4PMID:
return -1:
}
}
return -1;
.
262,267c
/* enable the chip, use default address 0x50 */
pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
pcs = pcicfgr16(p, PCSC);
miBASE = pcs & ~3;
return 0;
.
250,260c
/* these bridges have pretty easy access to the lm78 */
case PiixID:
case Piix3ID:
pcs = pcicfgr16(p, PCSC);
if(pcs & 3) {
/* already enabled */
miBASE = pcs & ~3;
return 0;
}
.
236a
Piix4PMID= 0x7113, /* PIIX4 power management function */
.
233,235c
PiixID= 0x122E,
Piix3ID= 0x7000,
.
## diffname pc/devlm78.c 1999/0715
## diff -e /n/emeliedump/1999/0709/sys/src/brazil/pc/devlm78.c /n/emeliedump/1999/0715/sys/src/brazil/pc/devlm78.c
791,793c
devbwrite,
devremove,
devwstat,
.
789c
devbread,
.
786c
devcreate,
.
781,782c
devclone,
.
778c
devreset,
.
776c
'T',
.
772c
error(Eperm);
return 0;
.
770c
lm78write(Chan *c, void *a, long n, vlong offset)
.
766c
return 0;
.
712,764c
error(Eperm);
.
561,710d
531,559c
return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
.
529a
.
517,528d
515c
lm78read(Chan *c, void *a, long n, vlong offset)
.
347,513d
343,345c
Linelen= 25,
};
.
340,341c
enum
.
336,337d
334c
lm78close(Chan*)
.
330c
return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
.
322,324c
lm78stat(Chan* c, char* dp)
{
devstat(c, dp, lm78dir, nelem(lm78dir), devgen);
.
318c
return devwalk(c, name, lm78dir, nelem(lm78dir), devgen);
.
315c
int
.
312c
return devattach('T', spec);
.
310c
lm78attach(char* spec)
.
305,306c
lm78smbus = piix4smbus();
if(lm78smbus != nil)
print("found piix4 smbus, base %lud\n", lm78smbus->base);
.
277,303d
161,275c
void
.
141,159c
extern SMBus* piix4smbus(void);
.
139c
SMBus *lm78smbus;
.
133,137d
119,131c
static Dirtab lm78dir[] = {
"temp", { Qtemp, 0 }, 0, 0444,
.
106,116d
2,103c
enum {
.
0a
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
.
## diffname pc/devlm78.c 1999/0725
## diff -e /n/emeliedump/1999/0715/sys/src/brazil/pc/devlm78.c /n/emeliedump/1999/0725/sys/src/brazil/pc/devlm78.c
105a
.
90,91c
lm78reset,
devinit,
.
82c
uchar *va = a;
int off, e;
off = offset;
switch(c->qid.path){
default:
error(Eperm);
case Qlm78vram:
if(off >= VRsize)
return 0;
if(waserror()){
qunlock(&lm78);
nexterror();
}
qlock(&lm78);
e = off + n;
if(e > VRsize)
e = VRsize;
for(; off < e; off++)
lm78wrreg(off, *va++);
qunlock(&lm78);
poperror();
return va - (uchar*)a;
}
.
73,74c
case Qlm78vram:
if(off >= VRsize)
return 0;
if(waserror()){
qunlock(&lm78);
nexterror();
}
qlock(&lm78);
e = off + n;
if(e > VRsize)
e = VRsize;
for(; off < e; off++)
*va++ = lm78rdreg(off);
qunlock(&lm78);
poperror();
return va - (uchar*)a;
.
69a
off = offset;
switch(c->qid.path & ~CHDIR){
.
68c
uchar *va = a;
int off, e;
.
33a
static int probed;
if(lm78.ifc == None)
error(Enodev);
if(probed == 0){
if(lm78probe() < 0)
error(Enodev);
probed = 1;
}
.
26,28c
int pcs;
Pcidev *p;
lm78.ifc = None;
p = nil;
while((p = pcimatch(p, IntelVendID, 0)) != nil){
switch(p->did){
// these bridges use the PCSC to map the lm78 into port space.
// for this case the lm78's CS# select is connected to the PIIX's
// PCS# output and the bottom 3 bits of address are passed to the
// LM78's A0-A2 inputs.
case PiixID:
case Piix3ID:
pcs = pcicfgr16(p, PCSC);
if(pcs & 3) {
/* already enabled */
lm78.port = pcs & ~3;
lm78.ifc = Parallel;
return;
}
// enable the chip, use default address 0x50
pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
pcs = pcicfgr16(p, PCSC);
lm78.port = pcs & ~3;
lm78.ifc = Parallel;
return;
// this bridge puts the lm78's serial interface on the smbus
case Piix4PMID:
lm78.smbus = piix4smbus();
if(lm78.smbus == nil)
continue;
print("found piix4 smbus, base %lud\n", lm78.smbus->base);
lm78.ifc = Smbus;
return;
}
}
.
24c
lm78reset(void)
.
22a
// routines that actually touch the device
static int
lm78wrreg(int reg, uchar val)
{
switch(lm78.ifc){
case Smbus:
lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
return 0;
}
return -1;
}
static int
lm78rdreg(int reg)
{
uchar rv;
switch(lm78.ifc){
case Smbus:
lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &rv);
return rv;
}
return -1;
}
static int
lm78probe(void)
{
switch(lm78.ifc){
case Smbus:
if(lm78rdreg(Raddr) != Serialaddr){
lm78.ifc = None;
break;
}
return 0;
}
return -1;
}
enum
{
IntelVendID= 0x8086,
PiixID= 0x122E,
Piix3ID= 0x7000,
Piix4PMID= 0x7113, // PIIX4 power management function
PCSC= 0x78, // programmable chip select control register
PCSC8bytes= 0x01,
};
// figure out what kind of interface and if there's an lm78 there
.
20a
static struct {
QLock;
int ifc;
// serial interface
SMBus *smbus;
// parallel interface
int port;
} lm78;
.
19c
// interface type
enum
{
None= 0,
Smbus,
Parallel,
};
.
16c
"lm78vram", { Qlm78vram, 0 }, 0, 0444,
.
12c
Qlm78vram,
.
10c
// this driver assumes that noone has changed the serial address
// of the device. if they have, there's no way we can figure it
// out -- presotto
enum
{
// address of chip on serial interface
Serialaddr= 0x2d,
// internal register addresses
Rconfig= 0x40,
Ristat1= 0x41,
Ristat2= 0x42,
Rsmimask1= 0x43,
Rsmimask2= 0x44,
Rnmimask1= 0x45,
Rnmimask2= 0x46,
Rvidfan= 0x47, // set fan counter, and read voltage level
Mvid= 0x0f,
Mfan= 0xf0,
Raddr= 0x48, // address used on serial bus
Rresetid= 0x49, // chip reset and ID register
Rpost= 0x00, // start of post ram
Rvalue= 0x20, // start of value ram
VRsize= 0x20, // size of value ram
};
enum
{
.
## diffname pc/devlm78.c 1999/0726
## diff -e /n/emeliedump/1999/0725/sys/src/brazil/pc/devlm78.c /n/emeliedump/1999/0726/sys/src/brazil/pc/devlm78.c
266,268c
lm78wrreg(Rvalue+off, *va++);
.
257,261d
234,236c
*va++ = lm78rdreg(Rvalue+off);
.
225,229d
171,178d
169c
lm78enable();
.
122c
// figure out what kind of interface we could have
.
107d
105c
lm78.probed = 1;
.
103c
error(Enodev);
} else {
// start the sampling
config = lm78rdreg(Rconfig);
pprint("config before %2.2ux\n", config);
config = (config | Bstart) & ~(Bintclr|Binit);
lm78wrreg(Rconfig, config);
pprint("config after %2.2ux\n", lm78rdreg(Rconfig));
.
99,100c
uchar config;
if(lm78.ifc == None)
error(Enodev);
if(lm78.probed == 0){
// make sure its really there
.
96,97c
// start the chip monitoring but don't change any smi
// interrupts and/or alarms that the BIOS may have set up.
//
// this isn't locked because it's thought to be idempotent
static void
lm78enable(void)
.
93c
qunlock(&lm78);
poperror();
return val;
.
90,91c
lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
break;
case Parallel:
setreg(reg);
val = inb(lm78.port+Rpdata);
break;
default:
error(Enodev);
break;
.
86a
if(waserror()){
qunlock(&lm78);
nexterror();
}
qlock(&lm78);
.
85c
uchar val;
.
79c
qunlock(&lm78);
poperror();
.
77c
break;
case Parallel:
setreg(reg);
outb(lm78.port+Rpdata, val);
break;
default:
error(Enodev);
break;
.
73a
if(waserror()){
qunlock(&lm78);
nexterror();
}
qlock(&lm78);
.
71c
static void
.
69a
// wait for device to become quiescent and then set the
// register address
static void
setreg(int reg)
{
int tries;
for(tries = 0; tries < 1000000; tries++)
if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
outb(lm78.port+Rpaddr, reg);
return;
}
error("lm78 broken");
}
.
58,65c
int probed;
int ifc; // which interface is connected
SMBus *smbus; // serial interface
int port; // parallel interface
.
20a
Bstart= (1<<0),
Bsmiena= (1<<1),
Birqena= (1<<2),
Bintclr= (1<<3),
Breset= (1<<4),
Bnmi= (1<<5), // if set, use nmi, else irq
Bpowbypass= (1<<6),
Binit= (1<<7),
.
18a
// parallel access registers
Rpaddr= 0x5,
Bbusy= (1<<7),
Rpdata= 0x6,
.
10,13c
// this driver doesn't implement the management interrupts. we
// leave the LM78 interrupts set to whatever the BIOS did. we do
// allow reading and writing the the readouts and alarm values.
// Read(2)ing or write(2)ing at offset 0x0-0x1f, is
// equivalent to reading or writing lm78 registers 0x20-0x3f.
.
## diffname pc/devlm78.c 1999/0728
## diff -e /n/emeliedump/1999/0726/sys/src/brazil/pc/devlm78.c /n/emeliedump/1999/0728/sys/src/brazil/pc/devlm78.c
175c
pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
.
172d
|