xref: /plan9-contrib/sys/src/9/pc/ether8003.c (revision aa72973a2891ccbd3fb042462446761159389e19)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 #include "../port/netif.h"
9 
10 #include "etherif.h"
11 #include "ether8390.h"
12 
13 /*
14  * Western Digital/Standard Microsystems Corporation cards (WD80[01]3).
15  * Also handles 8216 cards (Elite Ultra).
16  * Configuration code based on that provided by SMC a long time ago.
17  */
18 enum {					/* 83C584 Bus Interface Controller */
19 	Msr		= 0x00,		/* Memory Select Register */
20 	Icr		= 0x01,		/* Interface Configuration Register */
21 	Iar		= 0x02,		/* I/O Address Register */
22 	Bio		= 0x03,		/* BIOS ROM Address Register */
23 	Ear		= 0x03,		/* EEROM Address Register (shared with Bio) */
24 	Irr		= 0x04,		/* Interrupt Request Register */
25 	Hcr		= 0x04,		/* 8216 hardware control */
26 	Laar		= 0x05,		/* LA Address Register */
27 	Ijr		= 0x06,		/* Initialisation Jumpers */
28 	Gp2		= 0x07,		/* General Purpose Data Register */
29 	Lar		= 0x08,		/* LAN Address Registers */
30 	Id		= 0x0E,		/* Card ID byte */
31 	Cksum		= 0x0F,		/* Checksum */
32 };
33 
34 enum {					/* Msr */
35 	Rst		= 0x80,		/* software reset */
36 	Menb		= 0x40,		/* memory enable */
37 };
38 
39 enum {					/* Icr */
40 	Bit16		= 0x01,		/* 16-bit bus */
41 	Other		= 0x02,		/* other register access */
42 	Ir2		= 0x04,		/* IR2 */
43 	Msz		= 0x08,		/* SRAM size */
44 	Rla		= 0x10,		/* recall LAN address */
45 	Rx7		= 0x20,		/* recall all but I/O and LAN address */
46 	Rio		= 0x40,		/* recall I/O address from EEROM */
47 	Sto		= 0x80,		/* non-volatile EEROM store */
48 };
49 
50 enum {					/* Laar */
51 	ZeroWS16	= 0x20,		/* zero wait states for 16-bit ops */
52 	L16en		= 0x40,		/* enable 16-bit LAN operation */
53 	M16en		= 0x80,		/* enable 16-bit memory access */
54 };
55 
56 enum {					/* Ijr */
57 	Ienable		= 0x01,		/* 8216 interrupt enable */
58 };
59 
60 /*
61  * Mapping from configuration bits to interrupt level.
62  */
63 static int irq8003[8] = {
64 	9, 3, 5, 7, 10, 11, 15, 4,
65 };
66 
67 static int irq8216[8] = {
68 	0, 9, 3, 5, 7, 10, 11, 15,
69 };
70 
71 static void
reset8003(Ether * ether,uchar ea[Eaddrlen],uchar ic[8])72 reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8])
73 {
74 	Dp8390 *ctlr;
75 	ulong port;
76 
77 	ctlr = ether->ctlr;
78 	port = ether->port;
79 
80 	/*
81 	 * Check for old, dumb 8003E, which doesn't have an interface
82 	 * chip. Only Msr exists out of the 1st eight registers, reads
83 	 * of the others just alias the 2nd eight registers, the LAN
84 	 * address ROM. Can check Icr, Irr and Laar against the ethernet
85 	 * address read above and if they match it's an 8003E (or an
86 	 * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which
87 	 * case the default irq gets used.
88 	 */
89 	if(memcmp(&ea[1], &ic[1], 5) == 0){
90 		memset(ic, 0, sizeof(ic));
91 		ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
92 	}
93 	else{
94 		/*
95 		 * As a final sanity check for the 8013EBT, which doesn't have
96 		 * the 83C584 interface chip, but has 2 real registers, write Gp2
97 		 * and if it reads back the same, it's not an 8013EBT.
98 		 */
99 		outb(port+Gp2, 0xAA);
100 		inb(port+Msr);				/* wiggle bus */
101 		if(inb(port+Gp2) != 0xAA){
102 			memset(ic, 0, sizeof(ic));
103 			ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
104 		}
105 		else
106 			ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)];
107 
108 		/*
109 		 * Check if 16-bit card.
110 		 * If Bit16 is read/write, then it's an 8-bit card.
111 		 * If Bit16 is set, it's in a 16-bit slot.
112 		 */
113 		outb(port+Icr, ic[Icr]^Bit16);
114 		inb(port+Msr);				/* wiggle bus */
115 		if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){
116 			ctlr->width = 2;
117 			ic[Icr] &= ~Bit16;
118 		}
119 		outb(port+Icr, ic[Icr]);
120 
121 		if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0)
122 			ctlr->width = 1;
123 	}
124 
125 	ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13);
126 	if(ctlr->width == 2)
127 		ether->mem |= (ic[Laar] & 0x1F)<<19;
128 	else
129 		ether->mem |= 0x80000;
130 
131 	if(ic[Icr] & (1<<3))
132 		ether->size = 32*1024;
133 	if(ctlr->width == 2)
134 		ether->size <<= 1;
135 
136 	/*
137 	 * Enable interface RAM, set interface width.
138 	 */
139 	outb(port+Msr, ic[Msr]|Menb);
140 	if(ctlr->width == 2)
141 		outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16);
142 }
143 
144 static void
reset8216(Ether * ether,uchar[8])145 reset8216(Ether* ether, uchar[8])
146 {
147 	uchar hcr, irq, x;
148 	ulong addr, port;
149 	Dp8390 *ctlr;
150 
151 	ctlr = ether->ctlr;
152 	port = ether->port;
153 
154 	ctlr->width = 2;
155 
156 	/*
157 	 * Switch to the alternate register set and retrieve the memory
158 	 * and irq information.
159 	 */
160 	hcr = inb(port+Hcr);
161 	outb(port+Hcr, 0x80|hcr);
162 	addr = inb(port+0x0B) & 0xFF;
163 	irq = inb(port+0x0D);
164 	outb(port+Hcr, hcr);
165 
166 	ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13));
167 	ether->size = 8192*(1<<((addr>>4) & 0x03));
168 	ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)];
169 
170 	/*
171 	 * Enable interface RAM, set interface width, and enable interrupts.
172 	 */
173 	x = inb(port+Msr) & ~Rst;
174 	outb(port+Msr, Menb|x);
175 	x = inb(port+Laar);
176 	outb(port+Laar, M16en|x);
177 	outb(port+Ijr, Ienable);
178 }
179 
180 /*
181  * Get configuration parameters, enable memory.
182  * There are opportunities here for buckets of code, try to resist.
183  */
184 static int
reset(Ether * ether)185 reset(Ether* ether)
186 {
187 	int i;
188 	uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum;
189 	ulong port;
190 	Dp8390 *ctlr;
191 
192 	/*
193 	 * Set up the software configuration.
194 	 * Use defaults for port, irq, mem and size if not specified.
195 	 * Defaults are set for the dumb 8003E which can't be
196 	 * autoconfigured.
197 	 */
198 	if(ether->port == 0)
199 		ether->port = 0x280;
200 	if(ether->irq == 0)
201 		ether->irq = 3;
202 	if(ether->mem == 0)
203 		ether->mem = 0xD0000;
204 	if(ether->size == 0)
205 		ether->size = 8*1024;
206 	if(ioalloc(ether->port, 0x20, 0, "wd8003") < 0)
207 		return -1;
208 
209 	/*
210 	 * Look for the interface. Read the LAN address ROM
211 	 * and validate the checksum - the sum of all 8 bytes
212 	 * should be 0xFF.
213 	 * At the same time, get the (possible) interface chip
214 	 * registers, they'll be used later to check for aliasing.
215 	 */
216 	port = ether->port;
217 	sum = 0;
218 	for(i = 0; i < sizeof(ea); i++){
219 		ea[i] = inb(port+Lar+i);
220 		sum += ea[i];
221 		ic[i] = inb(port+i);
222 	}
223 	id = inb(port+Id);
224 	sum += id;
225 	sum += inb(port+Cksum);
226 	if(sum != 0xFF){
227 		iofree(ether->port);
228 		return -1;
229 	}
230 
231 	ether->ctlr = malloc(sizeof(Dp8390));
232 	ctlr = ether->ctlr;
233 	if(ctlr == nil)
234 		error(Enomem);
235 	ctlr->ram = 1;
236 
237 	if((id & 0xFE) == 0x2A)
238 		reset8216(ether, ic);
239 	else
240 		reset8003(ether, ea, ic);
241 
242 	/*
243 	 * Set the DP8390 ring addresses.
244 	 */
245 	ctlr->port = port+0x10;
246 	ctlr->tstart = 0;
247 	ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
248 	ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
249 
250 	/*
251 	 * Finally, init the 8390, set the ethernet address
252 	 * and claim the memory used.
253 	 */
254 	dp8390reset(ether);
255 	memset(nullea, 0, Eaddrlen);
256 	if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
257 		for(i = 0; i < sizeof(ether->ea); i++)
258 			ether->ea[i] = ea[i];
259 	}
260 	dp8390setea(ether);
261 
262 	if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0)
263 		print("ether8003: warning - 0x%luX unavailable\n",
264 			PADDR(ether->mem));
265 
266 	return 0;
267 }
268 
269 void
ether8003link(void)270 ether8003link(void)
271 {
272 	addethercard("WD8003", reset);
273 }
274