/*
 * SiS 900
 * 
 * some registers not appearing in the documentation 
 * were 'stolen' from freebsd's if_sis.c (found at
 * the end of the enums -- the uncommented ones).
 * eeprom reading routines come from the same driver
 * but are untested (the chipset revision we have here
 * reads the mac address from cmos).
 *
 * andrey mirtchovski -- andrey@lanl.gov
 * 
 * TODO: 	handle cards with different revision chipsets
 * 			make sure sis_eeprom reads correct MAC 
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

enum {
	RxCnt 		= 16,		/* receiver list count */
	TxCnt		= 16,		/* tarnsmitter list count */
	TxLen		= ROUNDUP(sizeof(Etherpkt), 4),	
	RxLen		= ROUNDUP(sizeof(Etherpkt), 4),	
};

enum {					/* MAC operational registers */
	Cr			= 	0x00, 	/* command register */
	Cfg			=	0x04,	/* configuration register */
	Eeprom		=	0x08, 	/* EEPROM access register */
	Pcitest		=	0x0C, 	/* PCI test control register */
	Isr			=	0x10, 	/* interrupt status register */
	Imr			= 	0x14,	/* interrupt mask register */
	Ier			=	0x18, 	/* interrupt enable register */
	Ephy		=	0x1C, 	/* enhanced PHY access register */
	Tdr			=	0x20, 	/* transmit descriptor pointer */
	Tcr			=	0x24, 	/* transmit configuration register */
	Rdr			=	0x30, 	/* recieve descriptor pointer */
	Rcr			= 	0x34, 	/* receive configuration register */
	Fcr			=	0x38,	/* flow control register */
	Rfcr		=	0x48,	/* receive filter control register */
	Rfdr		=	0x4C, 	/* receive filter data register */
	Pmcr		=	0xB0, 	/* power management control register */
	Pmwer		=	0xB4, 	/* power management wake-up event register */
	Wakesfcr	=	0xBC,	/* wake-up sample frame CRC register */
	Wakesfmr	=	0xC0,	/* wake-up sample frame mask registers */
};

enum {					/* eeprom related stuff */
	EEread		= 0x0180, 
	EEwrite		= 0x0140, 
	EEerase 	= 0x01C0, 
	EEwriteEnable 	= 0x0130, 
	EEwriteDisable 	= 0x0100,
	EEeraseAll	= 0x0120, 
	EEwriteAll 	= 0x0110, 
	EEaddrMask	= 0x013F, 
	EEcmdShift 	= 16
};

enum {					/* eeprom access */
	Csel		=	0x08,	/* chip select */
	Clk			=	0x04,	/* serial clock */
	Dout		=	0x02,	/* data out */
	Din			=	0x01,	/* data in */
};


enum {					/* PHY configuration registers */
	M0Cr		=	0x00,	/* MI 0 Control register */
	M1Sr		=	0x01,	/* MI 1 Status register */
	M2Phy1		=	0x02,	/* MI 2 PHY ID #1 */
	M3Phy2		=	0x03,	/* MI 3 PHY ID #2 */
	M4Ana		=	0x04,	/* MI 4 Auto negotiation advertisement */
	M5Anrec		=	0x05, 	/* MI 5 Auto negotiation remote end capabilities */
	M16C1		=	0x10, 	/* MI 16 Configuration 1 */
	M17C2		=	0x11, 	/* MI 17 Configuration 2 */
	M18So		=	0x12, 	/* MI 18 Status Output */
	M19m		=	0x13,	/* MI 19 Mask */
	M20r		=	0x14,	/* MI 20 Reserved */
};

enum {					/* mac register space */
	PhySoftReset=	0x200,	/* HomePHY Software reset */
	Rst			=	0x100,	/* Reset */
	Intr		=	0x80, 	/* software interrupt */
	Rxrst		=	0x20,	/* receiver reset */
	Txrst		= 	0x10,	/* transmit reset */
	Rxdis		=	0x08,	/* receiver disable */
	Rxen		=	0x04,	/* receiver enable */
	Txdis		=	0x02,	/* transmit disable */
	Txen		=	0x01,	/* transmit enable */
};

enum {					/* configuration registers */
	BusRqAlg	=	0x80,	/* pci bus request algorithm */
	SnglBckoff	=	0x40,	/* single backoff */
	Prgoowt		=	0x20,	/* program out of window timer */
	Xdtd		=	0x10,	/* excessive deferral timer disable */
	Peda		=	0x08,	/* parity error detection action */
	BigEnd		=	0x01,	/* big endian mode */
};

enum { 					/* interrupt status */
	Wkevnt		=	0x10000000,		/* wake-up event */
	Etp			=	0x08000000,		/* end of transmission pause */
	Stp			=	0x04000000,		/* start of transmission pause */
	Trc			=	0x02000000,		/* transmit reset complete */
	Rrc			=	0x01000000,		/* receive reset complete */
	Dpe			=	0x00800000,		/* detected parity error */
	Sse			=	0x00400000,		/* sidnaled system error */
	Rma			=	0x00200000,		/* received master abort */
	Rta			=	0x00100000,		/* received target abort */
	FifoOvrrun	=	0x00010000,		/* rx status FIFO overrun */
	Hbes		=	0x00008000,		/* high bits error set */
	Sintr		=	0x00000800,		/* software interrupt */
	Txundr		=	0x00000400,		/* transmit underrun */
	Txidle		=	0x00000200,		/* transmit idle */
	Txerr		=	0x00000100,		/* transmit packet error */
	Txdesc		=	0x00000080,		/* transmit descriptor */
	TxOK		=	0x00000040,		/* transmit packet ok */
	RxOvrrun	=	0x00000020,		/* receiver fifo overrun */
	Rxidle		=	0x00000010,		/* receiver idle */
	Rxth		=	0x00000008,		/* receiver early threshold */
	Rxerr		=	0x00000004,		/* receiver packet error */
	Rxdesc		=	0x00000002,		/* receiver descriptor */
	RxOK		=	0x00000001,		/* receiver packet ok */
};

enum {
	Intrenbl	= 	0x01,			/* interrupt enable */
};

enum {					/* phy registers */
	PhyData		= 	0xFFFF0000,		/* phy R/W data */
	Phyaddr		=	0x0000F800,		/* register address of PHY */
	Phyregaddr	=	0x000007C0,		/* register address of PHY */
	Phycmd		=	0x00000020,		/* access cmd to phy */
	SwHw		=	0x00000010,		/* SW access request/HW done */
};

enum {					/* transmit configuration */
	CarrIgn		=	0x80000000,		/* carrier sence ignore */
	HbeatIgn	=	0x40000000,		/* heart beat ignore */
	MacLoopbk	=	0x20000000,		/* MAC Loopback */
	AutoTP		=	0x10000000,		/* automatic teansmit padding */
	TxDMA		=	0x00700000,		/* dma burst size */
	RxFill		=	0x00003F00,		/* TX Fill threshold */
	TxDrain		=	0x0000003F,		/* TX Drain threshold */
};

enum {					/* receive configuration */
	AccptErr	=	0x80000000,		/* accept error packets */
	AccptRunt	=	0x40000000,		/* accept runt packets */
	AccptTx		=	0x10000000,		/* accept TX packets */
	AccptJabb	=	0x08000000,		/* accept jabber packets */
	RxDMA		=	0x00700000,		/* receive dma burst size */
	RxDrain		=	0x0000003E,		/* RX drain threshold */
};

enum {					/* receive filter control */
	FiltEn		=	0x80000000,		/* receive filter enable */
	BroadEn		=	0x40000000,		/* broadcast enable */
	MultiEn		=	0x20000000,		/* accept all multicast */
	PhysEn		=	0x10000000,		/* accept all physical */
	HomePHY		=	0x08000000,		/* HomePHY/802.3PHY select */
};

enum {					/* mac address */
	Par0		=	0x00000000,		/* node address octets 1-0 */
	Par1		=	0x00010000,		/* node address octets 3-2 */
	Par2		=	0x00020000,		/* node address octets 5-4 */
	Mar0		=	0x00040000,		/* multicast hash table bits */
	Mar1		=	0x00050000,
	Mar2		=	0x00060000,
	Mar3		=	0x00070000,
	Mar4		=	0x00080000,
	Mar5		=	0x00090000,
	Mar6		=	0x000A0000,
	Mar7		=	0x000B0000,
};

enum { 					/* buffer descriptor status */
	Own			=	0x80000000,
	More		=	0x40000000,
	Intrpt		=	0x20000000,
	Crc			=	0x10000000,
	PktOK		=	0x08000000,
	Buflen		=	0x00000FFF,
};

enum {					/* rx buffer descriptor status */
	Ovrrun	 	= 0x02000000, 
	Dest 		= 0x00800000,     
	Bcast 		= 0x01800000,
	Mcast   	= 0x01000000, 
	Unimatch 	= 0x00800000, 
	Toolong 	= 0x00400000,
	Runt    	= 0x00200000, 
	Rxiserr  	= 0x00100000, 
	Crcerr  	= 0x00080000,
	Faerr   	= 0x00040000, 
	Loopbk   	= 0x00020000, 
	Rxcoll   	= 0x00010000,
};

enum {					/* tx buffer descriptor status */
	Abrt	   	= 0x04000000, 
	Undr	 	= 0x02000000, 
	Nocarr	 	= 0x01000000,
	Deferd  	= 0x00800000, 
	Exdefer 	= 0x00400000, 
	Owcoll   	= 0x00200000,
	Excoll 		= 0x00100000, 
	Txcoll   	= 0x000F0000,
};

	

#define		Ints 	(RxOvrrun|Txundr|TxOK|Txidle|RxOK|Rxerr|Sse)

typedef struct {
	uint	link;
	uint	cmdsts;
	uint	bufptr;
} Desc;


typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;

	QLock	alock;			/* attach */
	Lock	ilock;			/* interrupt */
	Lock	tlock;			/* transmit */

	uint rctrl;				/* receiver control */


	Desc *td;		/* transmit descriptors */
	Desc *rd;		/* receive descriptors */
	uchar *tdbuff[TxCnt];
	uchar *rdbuff[RxCnt];

	ulong currd;
	ulong dirtyrd;
	ulong curtd;
	ulong dirtytd;

	int tdfull;

	uint tdpacks;		/* transmitted packets */
	uint rdpacks;		/* received packets */
	uint errs;			/* errors */
	uint coll;			/* collisions */

	int	dis;			/* disconnect counter */
	int	fcsc;			/* false carrier sense counter */
	int	rxerr;			/* RX_ER counter */
} Ctlr;

static Ctlr* ctlrhead; 
static Ctlr* ctlrtail;

#define csr8r(c, r)	(inb((c)->port+(r)))
#define csr16r(c, r)	(ins((c)->port+(r)))
#define csr32r(c, r)	(inl((c)->port+(r)))
#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))

static void 
rcmos(Ctlr *ctlr, Ether *edev)
{
	Pcidev *p;
	uint		reg;
	int			i;
	ulong port;

	USED(ctlr);

	p = pcimatch(nil, 0x1039, 0x0008);
	if (p == nil)
		error("sis: cannot find isa bridge");
	port = p->mem[0].bar & ~0x01;

	reg = pcicfgr8(p, 0x48);
	pcicfgw8(p, 0x48, reg|0x40);


	for (i = 0; i < 6; i++) {
		outb(port+0x70, 0x09 + i);
		edev->ea[i] = inb(port+0x71);
	}

	pcicfgw8(p, 0x48, reg & ~0x40);
	return;
}


static void sis_delay(Ctlr *ctlr)
{
	int			idx;

	for (idx = (300 / 33) + 1; idx > 0; idx--)
		csr32r(ctlr, Cr);

	return;
}

#define Eeset(x)	csr32w(ctlr, Eeprom, csr32r(ctlr, Eeprom) | x)
#define Eeclr(x)	csr32w(ctlr, Eeprom, csr32r(ctlr, Eeprom) & ~x)
static void sis_eeprom_idle(Ctlr *ctlr)
{
	register int		i;

	Eeset(Csel);
	sis_delay(ctlr);
	Eeset(Clk);
	sis_delay(ctlr);

	for (i = 0; i < 25; i++) {
		Eeclr(Clk);
		sis_delay(ctlr);
		Eeset(Clk);
		sis_delay(ctlr);
	}

	Eeclr(Clk);
	sis_delay(ctlr);
	Eeclr(Csel);
	sis_delay(ctlr);
	csr32w(ctlr, Eeprom, 0x00000000);

	return;
}


static void sis_eeprom_putbyte(Ctlr *ctlr, int addr)

{
	register int		d, i;

	d = addr | EEread;

	/*
	 * Feed in each bit and stobe the clock.
	 */
	for (i = 0x400; i; i >>= 1) {
		if (d & i) {
			Eeset(Din);
		} else {
			Eeclr(Din);
		}
		sis_delay(ctlr);
		Eeset(Clk);
		sis_delay(ctlr);
		Eeclr(Clk);
		sis_delay(ctlr);
	}

	return;
}

/*
 * Read a word of data stored in the EEPROM at address 'addr.'
 */
static void sis_eeprom_getword(Ctlr *ctlr, int addr, uchar *dest)
{
	register int		i;
	uint		word = 0;

	/* Force EEPROM to idle state. */
	sis_eeprom_idle(ctlr);

	/* Enter EEPROM access mode. */
	sis_delay(ctlr);
	Eeclr(Clk);
	sis_delay(ctlr);
	Eeset(Csel);
	sis_delay(ctlr);

	/*
	 * Send address of word we want to read.
	 */
	sis_eeprom_putbyte(ctlr, addr);

	/*
	 * Start reading bits from EEPROM.
	 */
	for (i = 0x8000; i; i >>= 1) {
		Eeset(Clk);
		sis_delay(ctlr);
		if (csr32r(ctlr, Eeprom) & Dout)
			word |= i;
		sis_delay(ctlr);
		Eeclr(Clk);
		sis_delay(ctlr);
	}

	/* Turn off EEPROM access mode. */
	sis_eeprom_idle(ctlr);

	*dest = word;

//print("eeprom_readword: %4.4ux\n", word);

	return;
}

static void sis_read_eeprom(Ctlr *ctlr, uchar *dest, int off, int cnt, int swap)

{
	int			i;
	uchar		word = 0, *ptr;

	USED(swap);

	for (i = 0; i < cnt; i++) {
		sis_eeprom_getword(ctlr, off + i, &word);
		ptr = dest + (i * 2);

			*ptr = word;
	}

	return;
}





static void
sispromiscuous(void* arg, int on)
{
	Ether *edev;
	Ctlr * ctlr;


	edev = arg;
	ctlr = edev->ctlr;
	ilock(&ctlr->ilock);

	if(on)
		ctlr->rctrl |= PhysEn;
	else
		ctlr->rctrl &= ~PhysEn;

	csr32w(ctlr, Rfcr, ctlr->rctrl);
	iunlock(&ctlr->ilock);

}

static long
sisifstat(Ether* edev, void* a, long n, ulong offset)
{
	int l;
	char *p;
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	p = malloc(READSTR);
	l = snprint(p, READSTR, "Errors: %ud\n", ctlr->errs);
	l += snprint(p+l, READSTR-l, "sent: %d\n", ctlr->coll);
	l += snprint(p+l, READSTR-l, "received: %d\n", ctlr->rdpacks);
	l += snprint(p+l, READSTR-l, "transmitted: %d\n", ctlr->tdpacks);
	snprint(p+l, READSTR-l, "collisions: %d\n", ctlr->coll);

	n = readstr(offset, a, n, p);
	free(p);
	return n;
}

static int
sisreset(Ctlr* ctlr)
{
	/*
	 * Soft reset the controller.
	 */

	csr32w(ctlr, Ier, 0);
	csr32w(ctlr, Imr, 0);
	csr32w(ctlr, Rfcr, 0);

	csr32w(ctlr, Cr, Txrst | Rxrst | Rst);

	while(!csr32r(ctlr, Isr) & Isr)
		;

	csr32w(ctlr, Cfg, 0x08);	/* ? */

	return 0;
}

static void
sishalt(Ctlr* ctlr)
{

	csr32w(ctlr, Ier, 0);
	csr32w(ctlr, Imr, 0);
	csr32w(ctlr, Rfcr, 0);

	csr32w(ctlr, Cr, Txdis | Rxdis);
	
	csr32w(ctlr, Tdr, 0);
	csr32w(ctlr, Rdr, 0);


}

static void
sisinit(Ether* edev)
{
	int i;
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	ilock(&ctlr->ilock);

	//sishalt(ctlr);


	/*
	 * MAC Address.
	 */
	csr32w(ctlr, Rfcr, Par0);
	csr32w(ctlr, Rfdr, (edev->ea[1]<<8)|edev->ea[0]);
	csr32w(ctlr, Rfcr, Par1);
	csr32w(ctlr, Rfdr, (edev->ea[3]<<8)|edev->ea[2]);
	csr32w(ctlr, Rfcr, Par2);
	csr32w(ctlr, Rfdr, (edev->ea[5]<<8)|edev->ea[4]);

	/* receiver and transmitter descriptors are as follows:
	 * link: 32bit pointer to the next descriptor (circular in this case)
	 * cmdsts: command status
	 * bufptr: pointer to first byte in packet
 	 */	

	/*
	 * Receiver
	 */
	ctlr->currd = 0;
	ctlr->dirtyrd = 0;
	for(i = 0; i < RxCnt; i++) {
		uchar *r; Desc *d;

		d = ctlr->rd+i;
		d->link = PCIWADDR(i == RxCnt - 1 ? ctlr->rd : d + 1);
		d->cmdsts = 0;
		d->bufptr = 0;

		if((r = mallocz(RxLen+32+16, 0)) == nil) 
			error("sis: not enough memory for receive buffers\n");
		ctlr->rdbuff[i] = (uchar *)ROUNDUP((ulong)r, 32);
		d->cmdsts = RxLen;
		d->bufptr = PCIWADDR(ctlr->rdbuff[i]);
	}
	csr32w(ctlr, Rdr, PCIWADDR(ctlr->rd));
	

	/*
	 * Transmitter.
	 */

	ctlr->tdfull = 0;
	ctlr->dirtytd = ctlr->curtd  = 0;
	for(i = 0; i < TxCnt; i++){
		uchar *r; Desc *d;

		d = ctlr->td + i;
		d->link = PCIWADDR(i == TxCnt - 1 ? ctlr->td : d + 1);
		d->cmdsts = 0;
		d->bufptr = 0;

		if((r = mallocz(TxLen+32+16, 0)) == nil) 
			error("sis: not enough memory for transmit buffers\n");
		ctlr->tdbuff[i] = (uchar *)ROUNDUP((ulong)r, 32);

	}
	
	csr32w(ctlr, Tdr, PCIWADDR(ctlr->td));

	
	ctlr->rctrl = BroadEn | FiltEn |PhysEn | HomePHY;
	csr32w(ctlr, Rfcr, csr32r(ctlr, Rfcr) | ctlr->rctrl);



	/*
	 * Interrupts.
	 */
	csr32w(ctlr, Imr, 0xffffffff);
	csr32w(ctlr, Ier, 0x01);
	csr32w(ctlr, Cr, (Txen | Rxen));



	csr32w(ctlr, Rcr, (0x00700000 | (((64 >> 3) << 1) & 0x0000003E)));
	/* set 100baseT */
	csr32w(ctlr, Tcr, (0x00500000|0x10000000|(((64 >> 5) << 8) & 0x00003F00)|((1536 >> 5) & 0x0000003F)));
	
	csr32w(ctlr, Tcr, csr32r(ctlr, Tcr) | HbeatIgn | CarrIgn);
	csr32w(ctlr, Rcr, csr32r(ctlr, Rcr) | AccptTx);

	iunlock(&ctlr->ilock);
}

static void
sisattach(Ether* edev)
{
	Ctlr *ctlr;


	ctlr = edev->ctlr;
	qlock(&ctlr->alock);
	if(ctlr->rd == nil || ctlr->td == nil){
		ctlr->rd = (Desc *)mallocz(RxCnt * sizeof(Desc)+ 32, 0);
		ctlr->td = (Desc *)mallocz(RxCnt * sizeof(Desc)+ 32, 0);
		sisinit(edev);
	}
	qunlock(&ctlr->alock);


} 

static void
sistxstart(Ether* edev)
{
	int size, num;
	Block *bp;
	Ctlr *ctlr;
	Desc *td;
	uchar *buf;
	

	

	ctlr = edev->ctlr;

	if(ctlr->curtd - ctlr->dirtytd > TxCnt) {
		/* we're full.. discard packet */
		return;
	}

	bp = qget(edev->oq);
	if(bp == nil)
			return;
	size = BLEN(bp);

	num = ctlr->curtd % TxCnt;
	buf = ctlr->tdbuff[num];
	td = ctlr->td + num;
	if(((int)bp->rp) & 0x03){
		memmove(buf, bp->rp, size);
		freeb(bp);
		td->bufptr = PCIWADDR(buf);
	}
	else{
		td->bufptr = PCIWADDR(bp->rp);
	}
	td->cmdsts = (Own | size);

	csr32w(ctlr, Cr, Txen);

	ctlr->curtd++;

}

static void
sistransmit(Ether* edev)
{
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	ilock(&ctlr->tlock);
	sistxstart(edev);
	iunlock(&ctlr->tlock);

}

static void
sisreceive(Ether* edev)
{
	Block *bp;
	Ctlr *ctlr;
	uint size;

	int entry;
	int status;



	ctlr = edev->ctlr;
	entry = ctlr->currd % RxCnt;
	status = ctlr->rd[entry].cmdsts;
	size = status & Buflen - 4;

	while(status & Own) {


		if (!(status & PktOK)) {
			ctlr->errs++;
			if (status & Rxcoll)
				ctlr->coll++;
			ctlr->currd++;
			ctlr->dirtyrd++;
			entry = ctlr->currd % RxCnt;
			status = ctlr->rd[entry].cmdsts;

			continue;

		}

		if(ctlr->rdbuff[entry] == nil) {
			print("sis: null pointer in receive ring, skipping...");
			break;
		}

		bp = iallocb(size);
		
		if(size > 0 && bp != nil){
			memmove(bp->wp, ctlr->rdbuff[entry], size);
			//bp->rp = ctlr->rdbuff[entry];
			bp->wp += size;
		}

		ctlr->rd[entry].cmdsts = RxLen;
		if(bp != nil)
			etheriq(edev, bp, 1);

		ctlr->currd++;
		ctlr->dirtyrd++;
		entry = ctlr->currd % RxCnt;
		status = ctlr->rd[entry].cmdsts;
	}

	csr32w(ctlr, Cr, Rxen);


}

static void
sisinterrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Ether *edev;
	uint isr, status;

	edev = arg;
	ctlr = edev->ctlr;

	ilock(&ctlr->ilock);


	do {
		isr = csr32r(ctlr, Isr);
		if ((isr & Ints) == 0)
			break;

		if (isr & (Rxdesc | RxOK)) {
			/* Rx interrupt */
			sisreceive(edev);
		}
		if(isr & (TxOK | Txdesc)) {
			/* see how the last packet was tx-ed */
			status = ctlr->rd[ctlr->dirtytd].cmdsts;
		
			if(status & PktOK) 
				ctlr->tdpacks++;
			
			ctlr->coll += (status & Txcoll) >> 16;
			ctlr->dirtytd++;
		}

		if (isr & (Txundr | Txerr | RxOvrrun | Rxerr)) {
			ctlr->errs++;
		}

		/* something strange happened !!! */
		if (isr & 0x8000) {
			print("sis: abnormal interrupt, status %#8.8x.\n", isr);
			break;
		}
	} while (1);


	iunlock(&ctlr->ilock);

}

static Ctlr*
sismatch(Ether* edev, int id)
{
	int port;
	Pcidev *p;
	Ctlr *ctlr;

	/*
	 * Any adapter matches if no edev->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		p = ctlr->pcidev;
		if(((p->did<<16)|p->vid) != id)
			continue;
		port = p->mem[0].bar & ~0x01;
		if(edev->port != 0 && edev->port != port)
			continue;

		if(ioalloc(port, p->mem[0].size, 0, "sis") < 0){
			print("sis: port 0x%uX in use\n", port);
			continue;
		}

		ctlr->port = port;
		if(sisreset(ctlr))
			continue;
		pcisetbme(p);

		ctlr->active = 1;
		return ctlr;
	}
	return nil;
}

static struct {
	char*	name;
	int	id;
} sispci[] = {
	{ "sis",	(0x0900<<16)|0x1039, },	/* sis 900  */
	{ nil },
};

static int
sispnp(Ether* edev)
{
	int i, id;
	Pcidev *p;
	Ctlr *ctlr;
	uchar ea[Eaddrlen];

	/*
	 * Make a list of all ethernet controllers
	 * if not already done.
	 */
	if(ctlrhead == nil){
		p = nil;
		while(p = pcimatch(p, 0, 0)){
			if(p->ccrb != 0x02 || p->ccru != 0)
				continue;
			ctlr = malloc(sizeof(Ctlr));
			ctlr->pcidev = p;
			ctlr->id = (p->did<<16)|p->vid;

			if(ctlrhead != nil)
				ctlrtail->next = ctlr;
			else
				ctlrhead = ctlr;
			ctlrtail = ctlr;
		}
	}


	id = 0;
	for(i = 0; i < edev->nopt; i++){
		if(cistrncmp(edev->opt[i], "id=", 3) == 0)
			id = strtol(&edev->opt[i][3], nil, 0);
	}

	ctlr = nil;
	if(id != 0)
		ctlr = sismatch(edev, id);
	else for(i = 0; sispci[i].name; i++){
		if((ctlr = sismatch(edev, sispci[i].id)) != nil)
			break;
	}
	if(ctlr == nil)
		return -1;

	edev->ctlr = ctlr;
	edev->port = ctlr->port;
	edev->irq = ctlr->pcidev->intl;
	edev->tbdf = ctlr->pcidev->tbdf;


	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, edev->ea, Eaddrlen) == 0){

		if(ctlr->pcidev->rid == 0x82) {
			/* 630S revision ID supported */
			rcmos(ctlr, edev);
		} 
		else {
			print("sis: unsupported revision id.. cannot set mac address\n");
		}
	}

	edev->attach = sisattach;
	edev->transmit = sistransmit;
	edev->interrupt = sisinterrupt;
	edev->ifstat = sisifstat;

	edev->arg = edev;
	edev->promiscuous = sispromiscuous;

	edev->mbps = 100;	/* hardwire... */

	return 0;
}

void
ethersislink(void)
{
	addethercard("sis", sispnp);
}
