Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/devlm78.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.