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