]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ether8003.c
kernel: cleanup the software mouse cursor mess
[plan9front.git] / sys / src / 9 / pc / ether8003.c
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 #include "../port/etherif.h"
10
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
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] = (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] = (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 = (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
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 = 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
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         ctlr = malloc(sizeof(Dp8390));
232         if(ctlr == nil){
233                 print("ether8003: can't allocate memory\n");
234                 iofree(ether->port);
235                 return -1;
236         }
237         ether->ctlr = ctlr;
238         ctlr->ram = 1;
239
240         if((id & 0xFE) == 0x2A)
241                 reset8216(ether, ic);
242         else
243                 reset8003(ether, ea, ic);
244
245         /*
246          * Set the DP8390 ring addresses.
247          */
248         ctlr->port = port+0x10;
249         ctlr->tstart = 0;
250         ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
251         ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
252
253         /*
254          * Finally, init the 8390, set the ethernet address
255          * and claim the memory used.
256          */
257         dp8390reset(ether);
258         memset(nullea, 0, Eaddrlen);
259         if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
260                 for(i = 0; i < sizeof(ether->ea); i++)
261                         ether->ea[i] = ea[i];
262         }
263         dp8390setea(ether);
264
265         if(umballoc(ether->mem, ether->size, 0) == -1)
266                 print("ether8003: warning - 0x%luX unavailable\n", ether->mem);
267
268         return 0;
269 }
270
271 void
272 ether8003link(void)
273 {
274         addethercard("WD8003", reset);
275 }