xref: /inferno-os/os/pc/ether589.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 /*
2  * 3C589 and 3C562.
3  * To do:
4  *	check xcvr10Base2 still works (is GlobalReset necessary?).
5  */
6 #include "u.h"
7 #include "../port/lib.h"
8 #include "mem.h"
9 #include "dat.h"
10 #include "fns.h"
11 #include "io.h"
12 #include "../port/error.h"
13 #include "../port/netif.h"
14 
15 #include "etherif.h"
16 
17 enum {						/* all windows */
18 	CommandR		= 0x000E,
19 	IntStatusR		= 0x000E,
20 };
21 
22 enum {						/* Commands */
23 	GlobalReset		= 0x0000,
24 	SelectRegisterWindow	= 0x0001,
25 	RxReset			= 0x0005,
26 	TxReset			= 0x000B,
27 	AcknowledgeInterrupt	= 0x000D,
28 };
29 
30 enum {						/* IntStatus bits */
31 	commandInProgress	= 0x1000,
32 };
33 
34 #define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
35 #define STATUS(port)		ins((port)+IntStatusR)
36 
37 enum {						/* Window 0 - setup */
38 	Wsetup			= 0x0000,
39 						/* registers */
40 	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
41 	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
42 	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
43 	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
44 	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
45 	EepromCommand		= 0x000A,
46 	EepromData		= 0x000C,
47 						/* AddressConfig Bits */
48 	autoSelect9		= 0x0080,
49 	xcvrMask9		= 0xC000,
50 						/* ConfigControl bits */
51 	Ena			= 0x0001,
52 	base10TAvailable9	= 0x0200,
53 	coaxAvailable9		= 0x1000,
54 	auiAvailable9		= 0x2000,
55 						/* EepromCommand bits */
56 	EepromReadRegister	= 0x0080,
57 	EepromBusy		= 0x8000,
58 };
59 
60 enum {						/* Window 1 - operating set */
61 	Wop			= 0x0001,
62 };
63 
64 enum {						/* Window 3 - FIFO management */
65 	Wfifo			= 0x0003,
66 						/* registers */
67 	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
68 						/* InternalConfig bits */
69 	xcvr10BaseT		= 0x00000000,
70 	xcvr10Base2		= 0x00300000,
71 };
72 
73 enum {						/* Window 4 - diagnostic */
74 	Wdiagnostic		= 0x0004,
75 						/* registers */
76 	MediaStatus		= 0x000A,
77 						/* MediaStatus bits */
78 	linkBeatDetect		= 0x0800,
79 };
80 
81 extern int etherelnk3reset(Ether*);
82 
83 static char *tcmpcmcia[] = {
84 	"3C589",			/* 3COM 589[ABCD] */
85 	"3C562",			/* 3COM 562 */
86 	"589E",				/* 3COM Megahertz 589E */
87 	nil,
88 };
89 
90 static int
91 configASIC(Ether* ether, int port, int xcvr)
92 {
93 	int x;
94 
95 	/* set Window 0 configuration registers */
96 	COMMAND(port, SelectRegisterWindow, Wsetup);
97 	outs(port+ConfigControl, Ena);
98 
99 	/* IRQ must be 3 on 3C589/3C562 */
100 	outs(port + ResourceConfig, 0x3F00);
101 
102 	x = ins(port+AddressConfig) & ~xcvrMask9;
103 	x |= (xcvr>>20)<<14;
104 	outs(port+AddressConfig, x);
105 
106 	COMMAND(port, TxReset, 0);
107 	while(STATUS(port) & commandInProgress)
108 		;
109 	COMMAND(port, RxReset, 0);
110 	while(STATUS(port) & commandInProgress)
111 		;
112 
113 	return etherelnk3reset(ether);
114 }
115 
116 static int
117 reset(Ether* ether)
118 {
119 	int i, t, slot;
120 	char *type;
121 	int port;
122 	enum { WantAny, Want10BT, Want10B2 };
123 	int want;
124 	uchar ea[6];
125 	char *p;
126 
127 	if(ether->irq == 0)
128 		ether->irq = 10;
129 	if(ether->port == 0)
130 		ether->port = 0x240;
131 	port = ether->port;
132 
133 	if(ioalloc(port, 0x10, 0, "3C589") < 0)
134 		return -1;
135 
136 	type = nil;
137 	slot = -1;
138 	for(i = 0; tcmpcmcia[i] != nil; i++){
139 		type = tcmpcmcia[i];
140 		if((slot = pcmspecial(type, ether)) >= 0)
141 			break;
142 	}
143 	if(slot < 0){
144 		iofree(port);
145 		return -1;
146 	}
147 
148 	/*
149 	 * Read Ethernet address from card memory
150 	 * on 3C562, but only if the user has not
151 	 * overridden it.
152 	 */
153 	memset(ea, 0, sizeof ea);
154 	if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
155 		if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
156 			for(i = 0; i < 6; i += 2){
157 				t = ea[i];
158 				ea[i] = ea[i+1];
159 				ea[i+1] = t;
160 			}
161 			memmove(ether->ea, ea, 6);
162 		}
163 	}
164 	/*
165 	 * Allow user to specify desired media in plan9.ini
166 	 */
167 	want = WantAny;
168 	for(i = 0; i < ether->nopt; i++){
169 		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
170 			continue;
171 		p = ether->opt[i]+6;
172 		if(cistrcmp(p, "10base2") == 0)
173 			want = Want10B2;
174 		else if(cistrcmp(p, "10baseT") == 0)
175 			want = Want10BT;
176 	}
177 
178 	/* try configuring as a 10BaseT */
179 	if(want==WantAny || want==Want10BT){
180 		if(configASIC(ether, port, xcvr10BaseT) < 0){
181 			pcmspecialclose(slot);
182 			iofree(port);
183 			return -1;
184 		}
185 		delay(100);
186 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
187 		if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
188 			COMMAND(port, SelectRegisterWindow, Wop);
189 			print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
190 			return 0;
191 		}
192 	}
193 
194 	/* try configuring as a 10base2 */
195 	if(want==WantAny || want==Want10B2){
196 		COMMAND(port, GlobalReset, 0);
197 		if(configASIC(ether, port, xcvr10Base2) < 0){
198 			pcmspecialclose(slot);
199 			iofree(port);
200 			return -1;
201 		}
202 		print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
203 		return 0;
204 	}
205 	return -1;		/* not reached */
206 }
207 
208 void
209 ether589link(void)
210 {
211 	addethercard("3C589", reset);
212 	addethercard("3C562", reset);
213 	addethercard("589E", reset);
214 }
215