xref: /plan9-contrib/sys/src/9/pc/ether589.c (revision 162f803d803e7af3469d29bde78fb975a68a2888)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * 3C589 and 3C562.
37dd7cddfSDavid du Colombier  * To do:
47dd7cddfSDavid du Colombier  *	check xcvr10Base2 still works (is GlobalReset necessary?).
57dd7cddfSDavid du Colombier  */
6219b2ee8SDavid du Colombier #include "u.h"
7219b2ee8SDavid du Colombier #include "../port/lib.h"
8219b2ee8SDavid du Colombier #include "mem.h"
9219b2ee8SDavid du Colombier #include "dat.h"
10219b2ee8SDavid du Colombier #include "fns.h"
11219b2ee8SDavid du Colombier #include "io.h"
127dd7cddfSDavid du Colombier #include "../port/error.h"
137dd7cddfSDavid du Colombier #include "../port/netif.h"
14219b2ee8SDavid du Colombier 
157dd7cddfSDavid du Colombier #include "etherif.h"
16219b2ee8SDavid du Colombier 
177dd7cddfSDavid du Colombier enum {						/* all windows */
187dd7cddfSDavid du Colombier 	CommandR		= 0x000E,
197dd7cddfSDavid du Colombier 	IntStatusR		= 0x000E,
20219b2ee8SDavid du Colombier };
21219b2ee8SDavid du Colombier 
227dd7cddfSDavid du Colombier enum {						/* Commands */
237dd7cddfSDavid du Colombier 	GlobalReset		= 0x0000,
247dd7cddfSDavid du Colombier 	SelectRegisterWindow	= 0x0001,
257dd7cddfSDavid du Colombier 	RxReset			= 0x0005,
267dd7cddfSDavid du Colombier 	TxReset			= 0x000B,
277dd7cddfSDavid du Colombier 	AcknowledgeInterrupt	= 0x000D,
287dd7cddfSDavid du Colombier };
29219b2ee8SDavid du Colombier 
307dd7cddfSDavid du Colombier enum {						/* IntStatus bits */
317dd7cddfSDavid du Colombier 	commandInProgress	= 0x1000,
327dd7cddfSDavid du Colombier };
337dd7cddfSDavid du Colombier 
347dd7cddfSDavid du Colombier #define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
357dd7cddfSDavid du Colombier #define STATUS(port)		ins((port)+IntStatusR)
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier enum {						/* Window 0 - setup */
387dd7cddfSDavid du Colombier 	Wsetup			= 0x0000,
397dd7cddfSDavid du Colombier 						/* registers */
407dd7cddfSDavid du Colombier 	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
417dd7cddfSDavid du Colombier 	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
427dd7cddfSDavid du Colombier 	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
437dd7cddfSDavid du Colombier 	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
447dd7cddfSDavid du Colombier 	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
457dd7cddfSDavid du Colombier 	EepromCommand		= 0x000A,
467dd7cddfSDavid du Colombier 	EepromData		= 0x000C,
477dd7cddfSDavid du Colombier 						/* AddressConfig Bits */
487dd7cddfSDavid du Colombier 	autoSelect9		= 0x0080,
497dd7cddfSDavid du Colombier 	xcvrMask9		= 0xC000,
507dd7cddfSDavid du Colombier 						/* ConfigControl bits */
517dd7cddfSDavid du Colombier 	Ena			= 0x0001,
527dd7cddfSDavid du Colombier 	base10TAvailable9	= 0x0200,
537dd7cddfSDavid du Colombier 	coaxAvailable9		= 0x1000,
547dd7cddfSDavid du Colombier 	auiAvailable9		= 0x2000,
557dd7cddfSDavid du Colombier 						/* EepromCommand bits */
567dd7cddfSDavid du Colombier 	EepromReadRegister	= 0x0080,
577dd7cddfSDavid du Colombier 	EepromBusy		= 0x8000,
587dd7cddfSDavid du Colombier };
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier enum {						/* Window 1 - operating set */
617dd7cddfSDavid du Colombier 	Wop			= 0x0001,
627dd7cddfSDavid du Colombier };
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier enum {						/* Window 3 - FIFO management */
657dd7cddfSDavid du Colombier 	Wfifo			= 0x0003,
667dd7cddfSDavid du Colombier 						/* registers */
677dd7cddfSDavid du Colombier 	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
687dd7cddfSDavid du Colombier 						/* InternalConfig bits */
697dd7cddfSDavid du Colombier 	xcvr10BaseT		= 0x00000000,
707dd7cddfSDavid du Colombier 	xcvr10Base2		= 0x00300000,
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier enum {						/* Window 4 - diagnostic */
747dd7cddfSDavid du Colombier 	Wdiagnostic		= 0x0004,
757dd7cddfSDavid du Colombier 						/* registers */
767dd7cddfSDavid du Colombier 	MediaStatus		= 0x000A,
777dd7cddfSDavid du Colombier 						/* MediaStatus bits */
787dd7cddfSDavid du Colombier 	linkBeatDetect		= 0x0800,
797dd7cddfSDavid du Colombier };
807dd7cddfSDavid du Colombier 
817dd7cddfSDavid du Colombier extern int etherelnk3reset(Ether*);
827dd7cddfSDavid du Colombier 
837dd7cddfSDavid du Colombier static char *tcmpcmcia[] = {
847dd7cddfSDavid du Colombier 	"3C589",			/* 3COM 589[ABCD] */
857dd7cddfSDavid du Colombier 	"3C562",			/* 3COM 562 */
867dd7cddfSDavid du Colombier 	"589E",				/* 3COM Megahertz 589E */
877dd7cddfSDavid du Colombier 	nil,
887dd7cddfSDavid du Colombier };
89219b2ee8SDavid du Colombier 
90219b2ee8SDavid du Colombier static int
configASIC(Ether * ether,int port,int xcvr)917dd7cddfSDavid du Colombier configASIC(Ether* ether, int port, int xcvr)
92219b2ee8SDavid du Colombier {
937dd7cddfSDavid du Colombier 	int x;
94219b2ee8SDavid du Colombier 
95219b2ee8SDavid du Colombier 	/* set Window 0 configuration registers */
967dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wsetup);
977dd7cddfSDavid du Colombier 	outs(port+ConfigControl, Ena);
98219b2ee8SDavid du Colombier 
997dd7cddfSDavid du Colombier 	/* IRQ must be 3 on 3C589/3C562 */
1007dd7cddfSDavid du Colombier 	outs(port + ResourceConfig, 0x3F00);
101219b2ee8SDavid du Colombier 
1027dd7cddfSDavid du Colombier 	x = ins(port+AddressConfig) & ~xcvrMask9;
1037dd7cddfSDavid du Colombier 	x |= (xcvr>>20)<<14;
1047dd7cddfSDavid du Colombier 	outs(port+AddressConfig, x);
105219b2ee8SDavid du Colombier 
1067dd7cddfSDavid du Colombier 	COMMAND(port, TxReset, 0);
1077dd7cddfSDavid du Colombier 	while(STATUS(port) & commandInProgress)
108219b2ee8SDavid du Colombier 		;
1097dd7cddfSDavid du Colombier 	COMMAND(port, RxReset, 0);
1107dd7cddfSDavid du Colombier 	while(STATUS(port) & commandInProgress)
111219b2ee8SDavid du Colombier 		;
112219b2ee8SDavid du Colombier 
1137dd7cddfSDavid du Colombier 	return etherelnk3reset(ether);
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier static int
reset(Ether * ether)1177dd7cddfSDavid du Colombier reset(Ether* ether)
1187dd7cddfSDavid du Colombier {
1197dd7cddfSDavid du Colombier 	int i, t, slot;
1207dd7cddfSDavid du Colombier 	char *type;
1217dd7cddfSDavid du Colombier 	int port;
1227dd7cddfSDavid du Colombier 	enum { WantAny, Want10BT, Want10B2 };
1237dd7cddfSDavid du Colombier 	int want;
1247dd7cddfSDavid du Colombier 	uchar ea[6];
1257dd7cddfSDavid du Colombier 	char *p;
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier 	if(ether->irq == 0)
1287dd7cddfSDavid du Colombier 		ether->irq = 10;
1297dd7cddfSDavid du Colombier 	if(ether->port == 0)
1307dd7cddfSDavid du Colombier 		ether->port = 0x240;
1317dd7cddfSDavid du Colombier 	port = ether->port;
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier 	if(ioalloc(port, 0x10, 0, "3C589") < 0)
1347dd7cddfSDavid du Colombier 		return -1;
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	type = nil;
1377dd7cddfSDavid du Colombier 	slot = -1;
1387dd7cddfSDavid du Colombier 	for(i = 0; tcmpcmcia[i] != nil; i++){
1397dd7cddfSDavid du Colombier 		type = tcmpcmcia[i];
1407dd7cddfSDavid du Colombier 		if((slot = pcmspecial(type, ether)) >= 0)
1417dd7cddfSDavid du Colombier 			break;
1427dd7cddfSDavid du Colombier 	}
143*162f803dSDavid du Colombier 	ether->type = type;	/* must be set before calling configASIC */
1447dd7cddfSDavid du Colombier 	if(slot < 0){
1457dd7cddfSDavid du Colombier 		iofree(port);
146219b2ee8SDavid du Colombier 		return -1;
147219b2ee8SDavid du Colombier 	}
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier 	/*
1507dd7cddfSDavid du Colombier 	 * Read Ethernet address from card memory
1517dd7cddfSDavid du Colombier 	 * on 3C562, but only if the user has not
1527dd7cddfSDavid du Colombier 	 * overridden it.
1537dd7cddfSDavid du Colombier 	 */
1547dd7cddfSDavid du Colombier 	memset(ea, 0, sizeof ea);
1557dd7cddfSDavid du Colombier 	if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
15680ee5cbfSDavid du Colombier 		if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
1577dd7cddfSDavid du Colombier 			for(i = 0; i < 6; i += 2){
1587dd7cddfSDavid du Colombier 				t = ea[i];
1597dd7cddfSDavid du Colombier 				ea[i] = ea[i+1];
1607dd7cddfSDavid du Colombier 				ea[i+1] = t;
1617dd7cddfSDavid du Colombier 			}
1627dd7cddfSDavid du Colombier 			memmove(ether->ea, ea, 6);
1637dd7cddfSDavid du Colombier 		}
1647dd7cddfSDavid du Colombier 	}
1657dd7cddfSDavid du Colombier 	/*
1667dd7cddfSDavid du Colombier 	 * Allow user to specify desired media in plan9.ini
1677dd7cddfSDavid du Colombier 	 */
1687dd7cddfSDavid du Colombier 	want = WantAny;
1697dd7cddfSDavid du Colombier 	for(i = 0; i < ether->nopt; i++){
1707dd7cddfSDavid du Colombier 		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
1717dd7cddfSDavid du Colombier 			continue;
1727dd7cddfSDavid du Colombier 		p = ether->opt[i]+6;
1737dd7cddfSDavid du Colombier 		if(cistrcmp(p, "10base2") == 0)
1747dd7cddfSDavid du Colombier 			want = Want10B2;
1757dd7cddfSDavid du Colombier 		else if(cistrcmp(p, "10baseT") == 0)
1767dd7cddfSDavid du Colombier 			want = Want10BT;
1777dd7cddfSDavid du Colombier 	}
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier 	/* try configuring as a 10BaseT */
1807dd7cddfSDavid du Colombier 	if(want==WantAny || want==Want10BT){
1817dd7cddfSDavid du Colombier 		if(configASIC(ether, port, xcvr10BaseT) < 0){
1827dd7cddfSDavid du Colombier 			pcmspecialclose(slot);
1837dd7cddfSDavid du Colombier 			iofree(port);
1847dd7cddfSDavid du Colombier 			return -1;
1857dd7cddfSDavid du Colombier 		}
1867dd7cddfSDavid du Colombier 		delay(100);
1877dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
1887dd7cddfSDavid du Colombier 		if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
1897dd7cddfSDavid du Colombier 			COMMAND(port, SelectRegisterWindow, Wop);
1907dd7cddfSDavid du Colombier 			print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
191219b2ee8SDavid du Colombier 			return 0;
192219b2ee8SDavid du Colombier 		}
1937dd7cddfSDavid du Colombier 	}
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	/* try configuring as a 10base2 */
1967dd7cddfSDavid du Colombier 	if(want==WantAny || want==Want10B2){
1977dd7cddfSDavid du Colombier 		COMMAND(port, GlobalReset, 0);
1987dd7cddfSDavid du Colombier 		if(configASIC(ether, port, xcvr10Base2) < 0){
1997dd7cddfSDavid du Colombier 			pcmspecialclose(slot);
2007dd7cddfSDavid du Colombier 			iofree(port);
2017dd7cddfSDavid du Colombier 			return -1;
2027dd7cddfSDavid du Colombier 		}
2037dd7cddfSDavid du Colombier 		print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
2047dd7cddfSDavid du Colombier 		return 0;
2057dd7cddfSDavid du Colombier 	}
2067dd7cddfSDavid du Colombier 	return -1;		/* not reached */
2077dd7cddfSDavid du Colombier }
208219b2ee8SDavid du Colombier 
209219b2ee8SDavid du Colombier void
ether589link(void)210219b2ee8SDavid du Colombier ether589link(void)
211219b2ee8SDavid du Colombier {
212219b2ee8SDavid du Colombier 	addethercard("3C589", reset);
2137dd7cddfSDavid du Colombier 	addethercard("3C562", reset);
2147dd7cddfSDavid du Colombier 	addethercard("589E", reset);
215219b2ee8SDavid du Colombier }
216