xref: /plan9/sys/src/9/pc/ether8003.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1bd389b36SDavid du Colombier #include "u.h"
2bd389b36SDavid du Colombier #include "../port/lib.h"
3bd389b36SDavid du Colombier #include "mem.h"
4bd389b36SDavid du Colombier #include "dat.h"
5bd389b36SDavid du Colombier #include "fns.h"
6bd389b36SDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier #include "../port/error.h"
87dd7cddfSDavid du Colombier #include "../port/netif.h"
9bd389b36SDavid du Colombier 
107dd7cddfSDavid du Colombier #include "etherif.h"
117dd7cddfSDavid du Colombier #include "ether8390.h"
12bd389b36SDavid du Colombier 
13219b2ee8SDavid du Colombier /*
14219b2ee8SDavid du Colombier  * Western Digital/Standard Microsystems Corporation cards (WD80[01]3).
157dd7cddfSDavid du Colombier  * Also handles 8216 cards (Elite Ultra).
167dd7cddfSDavid du Colombier  * Configuration code based on that provided by SMC a long time ago.
17219b2ee8SDavid du Colombier  */
18bd389b36SDavid du Colombier enum {					/* 83C584 Bus Interface Controller */
19bd389b36SDavid du Colombier 	Msr		= 0x00,		/* Memory Select Register */
20bd389b36SDavid du Colombier 	Icr		= 0x01,		/* Interface Configuration Register */
21bd389b36SDavid du Colombier 	Iar		= 0x02,		/* I/O Address Register */
22bd389b36SDavid du Colombier 	Bio		= 0x03,		/* BIOS ROM Address Register */
23219b2ee8SDavid du Colombier 	Ear		= 0x03,		/* EEROM Address Register (shared with Bio) */
24bd389b36SDavid du Colombier 	Irr		= 0x04,		/* Interrupt Request Register */
257dd7cddfSDavid du Colombier 	Hcr		= 0x04,		/* 8216 hardware control */
26bd389b36SDavid du Colombier 	Laar		= 0x05,		/* LA Address Register */
27bd389b36SDavid du Colombier 	Ijr		= 0x06,		/* Initialisation Jumpers */
28bd389b36SDavid du Colombier 	Gp2		= 0x07,		/* General Purpose Data Register */
29bd389b36SDavid du Colombier 	Lar		= 0x08,		/* LAN Address Registers */
30219b2ee8SDavid du Colombier 	Id		= 0x0E,		/* Card ID byte */
31bd389b36SDavid du Colombier 	Cksum		= 0x0F,		/* Checksum */
32bd389b36SDavid du Colombier };
33bd389b36SDavid du Colombier 
34bd389b36SDavid du Colombier enum {					/* Msr */
35bd389b36SDavid du Colombier 	Rst		= 0x80,		/* software reset */
36bd389b36SDavid du Colombier 	Menb		= 0x40,		/* memory enable */
37bd389b36SDavid du Colombier };
38bd389b36SDavid du Colombier 
39219b2ee8SDavid du Colombier enum {					/* Icr */
40219b2ee8SDavid du Colombier 	Bit16		= 0x01,		/* 16-bit bus */
41219b2ee8SDavid du Colombier 	Other		= 0x02,		/* other register access */
42219b2ee8SDavid du Colombier 	Ir2		= 0x04,		/* IR2 */
43219b2ee8SDavid du Colombier 	Msz		= 0x08,		/* SRAM size */
44219b2ee8SDavid du Colombier 	Rla		= 0x10,		/* recall LAN address */
45219b2ee8SDavid du Colombier 	Rx7		= 0x20,		/* recall all but I/O and LAN address */
46219b2ee8SDavid du Colombier 	Rio		= 0x40,		/* recall I/O address from EEROM */
47219b2ee8SDavid du Colombier 	Sto		= 0x80,		/* non-volatile EEROM store */
48219b2ee8SDavid du Colombier };
49219b2ee8SDavid du Colombier 
50bd389b36SDavid du Colombier enum {					/* Laar */
517dd7cddfSDavid du Colombier 	ZeroWS16	= 0x20,		/* zero wait states for 16-bit ops */
527dd7cddfSDavid du Colombier 	L16en		= 0x40,		/* enable 16-bit LAN operation */
537dd7cddfSDavid du Colombier 	M16en		= 0x80,		/* enable 16-bit memory access */
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier enum {					/* Ijr */
577dd7cddfSDavid du Colombier 	Ienable		= 0x01,		/* 8216 interrupt enable */
58bd389b36SDavid du Colombier };
59bd389b36SDavid du Colombier 
60bd389b36SDavid du Colombier /*
61bd389b36SDavid du Colombier  * Mapping from configuration bits to interrupt level.
62bd389b36SDavid du Colombier  */
637dd7cddfSDavid du Colombier static int irq8003[8] = {
64bd389b36SDavid du Colombier 	9, 3, 5, 7, 10, 11, 15, 4,
65bd389b36SDavid du Colombier };
66bd389b36SDavid du Colombier 
677dd7cddfSDavid du Colombier static int irq8216[8] = {
687dd7cddfSDavid du Colombier 	0, 9, 3, 5, 7, 10, 11, 15,
697dd7cddfSDavid du Colombier };
707dd7cddfSDavid du Colombier 
717dd7cddfSDavid du Colombier static void
reset8003(Ether * ether,uchar ea[Eaddrlen],uchar ic[8])727dd7cddfSDavid du Colombier reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8])
73bd389b36SDavid du Colombier {
747dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
757dd7cddfSDavid du Colombier 	ulong port;
767dd7cddfSDavid du Colombier 
777dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
787dd7cddfSDavid du Colombier 	port = ether->port;
797dd7cddfSDavid du Colombier 
80bd389b36SDavid du Colombier 	/*
817dd7cddfSDavid du Colombier 	 * Check for old, dumb 8003E, which doesn't have an interface
827dd7cddfSDavid du Colombier 	 * chip. Only Msr exists out of the 1st eight registers, reads
837dd7cddfSDavid du Colombier 	 * of the others just alias the 2nd eight registers, the LAN
847dd7cddfSDavid du Colombier 	 * address ROM. Can check Icr, Irr and Laar against the ethernet
857dd7cddfSDavid du Colombier 	 * address read above and if they match it's an 8003E (or an
867dd7cddfSDavid du Colombier 	 * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which
877dd7cddfSDavid du Colombier 	 * case the default irq gets used.
88bd389b36SDavid du Colombier 	 */
897dd7cddfSDavid du Colombier 	if(memcmp(&ea[1], &ic[1], 5) == 0){
907dd7cddfSDavid du Colombier 		memset(ic, 0, sizeof(ic));
917dd7cddfSDavid du Colombier 		ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
927dd7cddfSDavid du Colombier 	}
937dd7cddfSDavid du Colombier 	else{
947dd7cddfSDavid du Colombier 		/*
957dd7cddfSDavid du Colombier 		 * As a final sanity check for the 8013EBT, which doesn't have
967dd7cddfSDavid du Colombier 		 * the 83C584 interface chip, but has 2 real registers, write Gp2
977dd7cddfSDavid du Colombier 		 * and if it reads back the same, it's not an 8013EBT.
987dd7cddfSDavid du Colombier 		 */
997dd7cddfSDavid du Colombier 		outb(port+Gp2, 0xAA);
1007dd7cddfSDavid du Colombier 		inb(port+Msr);				/* wiggle bus */
1017dd7cddfSDavid du Colombier 		if(inb(port+Gp2) != 0xAA){
1027dd7cddfSDavid du Colombier 			memset(ic, 0, sizeof(ic));
1037dd7cddfSDavid du Colombier 			ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
1047dd7cddfSDavid du Colombier 		}
1057dd7cddfSDavid du Colombier 		else
1067dd7cddfSDavid du Colombier 			ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)];
1077dd7cddfSDavid du Colombier 
1087dd7cddfSDavid du Colombier 		/*
1097dd7cddfSDavid du Colombier 		 * Check if 16-bit card.
1107dd7cddfSDavid du Colombier 		 * If Bit16 is read/write, then it's an 8-bit card.
1117dd7cddfSDavid du Colombier 		 * If Bit16 is set, it's in a 16-bit slot.
1127dd7cddfSDavid du Colombier 		 */
1137dd7cddfSDavid du Colombier 		outb(port+Icr, ic[Icr]^Bit16);
1147dd7cddfSDavid du Colombier 		inb(port+Msr);				/* wiggle bus */
1157dd7cddfSDavid du Colombier 		if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){
1167dd7cddfSDavid du Colombier 			ctlr->width = 2;
1177dd7cddfSDavid du Colombier 			ic[Icr] &= ~Bit16;
1187dd7cddfSDavid du Colombier 		}
1197dd7cddfSDavid du Colombier 		outb(port+Icr, ic[Icr]);
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 		if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0)
1227dd7cddfSDavid du Colombier 			ctlr->width = 1;
123bd389b36SDavid du Colombier 	}
124bd389b36SDavid du Colombier 
1257dd7cddfSDavid du Colombier 	ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13);
1267dd7cddfSDavid du Colombier 	if(ctlr->width == 2)
1277dd7cddfSDavid du Colombier 		ether->mem |= (ic[Laar] & 0x1F)<<19;
1287dd7cddfSDavid du Colombier 	else
1297dd7cddfSDavid du Colombier 		ether->mem |= 0x80000;
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier 	if(ic[Icr] & (1<<3))
1327dd7cddfSDavid du Colombier 		ether->size = 32*1024;
1337dd7cddfSDavid du Colombier 	if(ctlr->width == 2)
1347dd7cddfSDavid du Colombier 		ether->size <<= 1;
1357dd7cddfSDavid du Colombier 
136bd389b36SDavid du Colombier 	/*
1377dd7cddfSDavid du Colombier 	 * Enable interface RAM, set interface width.
138bd389b36SDavid du Colombier 	 */
1397dd7cddfSDavid du Colombier 	outb(port+Msr, ic[Msr]|Menb);
1407dd7cddfSDavid du Colombier 	if(ctlr->width == 2)
1417dd7cddfSDavid du Colombier 		outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16);
142bd389b36SDavid du Colombier }
143bd389b36SDavid du Colombier 
144bd389b36SDavid du Colombier static void
reset8216(Ether * ether,uchar[8])1457dd7cddfSDavid du Colombier reset8216(Ether* ether, uchar[8])
146bd389b36SDavid du Colombier {
1477dd7cddfSDavid du Colombier 	uchar hcr, irq, x;
1487dd7cddfSDavid du Colombier 	ulong addr, port;
1497dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
150bd389b36SDavid du Colombier 
1517dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
1527dd7cddfSDavid du Colombier 	port = ether->port;
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier 	ctlr->width = 2;
1557dd7cddfSDavid du Colombier 
156bd389b36SDavid du Colombier 	/*
1577dd7cddfSDavid du Colombier 	 * Switch to the alternate register set and retrieve the memory
1587dd7cddfSDavid du Colombier 	 * and irq information.
159bd389b36SDavid du Colombier 	 */
1607dd7cddfSDavid du Colombier 	hcr = inb(port+Hcr);
1617dd7cddfSDavid du Colombier 	outb(port+Hcr, 0x80|hcr);
1627dd7cddfSDavid du Colombier 	addr = inb(port+0x0B) & 0xFF;
1637dd7cddfSDavid du Colombier 	irq = inb(port+0x0D);
1647dd7cddfSDavid du Colombier 	outb(port+Hcr, hcr);
165bd389b36SDavid du Colombier 
1667dd7cddfSDavid du Colombier 	ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13));
1677dd7cddfSDavid du Colombier 	ether->size = 8192*(1<<((addr>>4) & 0x03));
1687dd7cddfSDavid du Colombier 	ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)];
169bd389b36SDavid du Colombier 
1707dd7cddfSDavid du Colombier 	/*
1717dd7cddfSDavid du Colombier 	 * Enable interface RAM, set interface width, and enable interrupts.
1727dd7cddfSDavid du Colombier 	 */
1737dd7cddfSDavid du Colombier 	x = inb(port+Msr) & ~Rst;
1747dd7cddfSDavid du Colombier 	outb(port+Msr, Menb|x);
1757dd7cddfSDavid du Colombier 	x = inb(port+Laar);
1767dd7cddfSDavid du Colombier 	outb(port+Laar, M16en|x);
1777dd7cddfSDavid du Colombier 	outb(port+Ijr, Ienable);
178bd389b36SDavid du Colombier }
179bd389b36SDavid du Colombier 
180bd389b36SDavid du Colombier /*
181219b2ee8SDavid du Colombier  * Get configuration parameters, enable memory.
1827dd7cddfSDavid du Colombier  * There are opportunities here for buckets of code, try to resist.
183bd389b36SDavid du Colombier  */
1847dd7cddfSDavid du Colombier static int
reset(Ether * ether)1857dd7cddfSDavid du Colombier reset(Ether* ether)
186219b2ee8SDavid du Colombier {
187219b2ee8SDavid du Colombier 	int i;
1887dd7cddfSDavid du Colombier 	uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum;
1897dd7cddfSDavid du Colombier 	ulong port;
1907dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
191bd389b36SDavid du Colombier 
192219b2ee8SDavid du Colombier 	/*
193219b2ee8SDavid du Colombier 	 * Set up the software configuration.
194219b2ee8SDavid du Colombier 	 * Use defaults for port, irq, mem and size if not specified.
195219b2ee8SDavid du Colombier 	 * Defaults are set for the dumb 8003E which can't be
196219b2ee8SDavid du Colombier 	 * autoconfigured.
197219b2ee8SDavid du Colombier 	 */
1987dd7cddfSDavid du Colombier 	if(ether->port == 0)
1997dd7cddfSDavid du Colombier 		ether->port = 0x280;
2007dd7cddfSDavid du Colombier 	if(ether->irq == 0)
2017dd7cddfSDavid du Colombier 		ether->irq = 3;
2027dd7cddfSDavid du Colombier 	if(ether->mem == 0)
2037dd7cddfSDavid du Colombier 		ether->mem = 0xD0000;
2047dd7cddfSDavid du Colombier 	if(ether->size == 0)
2057dd7cddfSDavid du Colombier 		ether->size = 8*1024;
2067dd7cddfSDavid du Colombier 	if(ioalloc(ether->port, 0x20, 0, "wd8003") < 0)
207219b2ee8SDavid du Colombier 		return -1;
208219b2ee8SDavid du Colombier 
209219b2ee8SDavid du Colombier 	/*
2107dd7cddfSDavid du Colombier 	 * Look for the interface. Read the LAN address ROM
2117dd7cddfSDavid du Colombier 	 * and validate the checksum - the sum of all 8 bytes
2127dd7cddfSDavid du Colombier 	 * should be 0xFF.
2137dd7cddfSDavid du Colombier 	 * At the same time, get the (possible) interface chip
2147dd7cddfSDavid du Colombier 	 * registers, they'll be used later to check for aliasing.
215219b2ee8SDavid du Colombier 	 */
2167dd7cddfSDavid du Colombier 	port = ether->port;
2177dd7cddfSDavid du Colombier 	sum = 0;
2187dd7cddfSDavid du Colombier 	for(i = 0; i < sizeof(ea); i++){
2197dd7cddfSDavid du Colombier 		ea[i] = inb(port+Lar+i);
2207dd7cddfSDavid du Colombier 		sum += ea[i];
2217dd7cddfSDavid du Colombier 		ic[i] = inb(port+i);
222219b2ee8SDavid du Colombier 	}
2237dd7cddfSDavid du Colombier 	id = inb(port+Id);
2247dd7cddfSDavid du Colombier 	sum += id;
2257dd7cddfSDavid du Colombier 	sum += inb(port+Cksum);
2267dd7cddfSDavid du Colombier 	if(sum != 0xFF){
2277dd7cddfSDavid du Colombier 		iofree(ether->port);
2287dd7cddfSDavid du Colombier 		return -1;
229219b2ee8SDavid du Colombier 	}
2307dd7cddfSDavid du Colombier 
2317dd7cddfSDavid du Colombier 	ether->ctlr = malloc(sizeof(Dp8390));
2327dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
233*aa72973aSDavid du Colombier 	if(ctlr == nil)
234*aa72973aSDavid du Colombier 		error(Enomem);
2357dd7cddfSDavid du Colombier 	ctlr->ram = 1;
2367dd7cddfSDavid du Colombier 
2377dd7cddfSDavid du Colombier 	if((id & 0xFE) == 0x2A)
2387dd7cddfSDavid du Colombier 		reset8216(ether, ic);
239219b2ee8SDavid du Colombier 	else
2407dd7cddfSDavid du Colombier 		reset8003(ether, ea, ic);
241219b2ee8SDavid du Colombier 
242219b2ee8SDavid du Colombier 	/*
243219b2ee8SDavid du Colombier 	 * Set the DP8390 ring addresses.
244219b2ee8SDavid du Colombier 	 */
2457dd7cddfSDavid du Colombier 	ctlr->port = port+0x10;
2467dd7cddfSDavid du Colombier 	ctlr->tstart = 0;
2477dd7cddfSDavid du Colombier 	ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
2487dd7cddfSDavid du Colombier 	ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
249219b2ee8SDavid du Colombier 
250219b2ee8SDavid du Colombier 	/*
2517dd7cddfSDavid du Colombier 	 * Finally, init the 8390, set the ethernet address
2527dd7cddfSDavid du Colombier 	 * and claim the memory used.
253219b2ee8SDavid du Colombier 	 */
2547dd7cddfSDavid du Colombier 	dp8390reset(ether);
2557dd7cddfSDavid du Colombier 	memset(nullea, 0, Eaddrlen);
2567dd7cddfSDavid du Colombier 	if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
2577dd7cddfSDavid du Colombier 		for(i = 0; i < sizeof(ether->ea); i++)
2587dd7cddfSDavid du Colombier 			ether->ea[i] = ea[i];
2597dd7cddfSDavid du Colombier 	}
2607dd7cddfSDavid du Colombier 	dp8390setea(ether);
261219b2ee8SDavid du Colombier 
2627dd7cddfSDavid du Colombier 	if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0)
26314414594SDavid du Colombier 		print("ether8003: warning - 0x%luX unavailable\n",
26414414594SDavid du Colombier 			PADDR(ether->mem));
265219b2ee8SDavid du Colombier 
266219b2ee8SDavid du Colombier 	return 0;
267219b2ee8SDavid du Colombier }
268219b2ee8SDavid du Colombier 
269219b2ee8SDavid du Colombier void
ether8003link(void)270219b2ee8SDavid du Colombier ether8003link(void)
271219b2ee8SDavid du Colombier {
2727dd7cddfSDavid du Colombier 	addethercard("WD8003", reset);
273219b2ee8SDavid du Colombier }
274