]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ether2000.c
pc/ether*: use 64-bit physical addresses and check pci membar types and sizes
[plan9front.git] / sys / src / 9 / pc / ether2000.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  * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
15  * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
16  * laptop. The manual says NE2000 compatible.
17  * The interface appears to be pretty well described in the National
18  * Semiconductor Local Area Network Databook (1992) as one of the
19  * AT evaluation cards.
20  *
21  * The NE2000 is really just a DP8390[12] plus a data port
22  * and a reset port.
23  */
24 enum {
25         Data            = 0x10,         /* offset from I/O base of data port */
26         Reset           = 0x1F,         /* offset from I/O base of reset port */
27 };
28
29 typedef struct Ctlr Ctlr;
30 typedef struct Ctlr {
31         Pcidev* pcidev;
32         Ctlr*   next;
33         int     active;
34 } Ctlr;
35
36 static Ctlr* ctlrhead;
37 static Ctlr* ctlrtail;
38
39 static struct {
40         char*   name;
41         int     id;
42 } ne2000pci[] = {
43         { "Realtek 8029",       (0x8029<<16)|0x10EC, },
44         { "Winbond 89C940",     (0x0940<<16)|0x1050, },
45         { nil },
46 };
47
48 static Ctlr*
49 ne2000match(Ether* edev, int id)
50 {
51         int port;
52         Pcidev *p;
53         Ctlr *ctlr;
54
55         /*
56          * Any adapter matches if no edev->port is supplied,
57          * otherwise the ports must match.
58          */
59         for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
60                 if(ctlr->active)
61                         continue;
62                 p = ctlr->pcidev;
63                 if(((p->did<<16)|p->vid) != id)
64                         continue;
65                 port = p->mem[0].bar & ~0x01;
66                 if(edev->port != 0 && edev->port != port)
67                         continue;
68
69                 /*
70                  * It suffices to fill these in,
71                  * the rest is gleaned from the card.
72                  */
73                 edev->port = port;
74                 edev->irq = p->intl;
75
76                 ctlr->active = 1;
77
78                 return ctlr;
79         }
80
81         return nil;
82 }
83
84 static void
85 ne2000pnp(Ether* edev)
86 {
87         int i, id;
88         Pcidev *p;
89         Ctlr *ctlr;
90
91         /*
92          * Make a list of all ethernet controllers
93          * if not already done.
94          */
95         if(ctlrhead == nil){
96                 p = nil;
97                 while(p = pcimatch(p, 0, 0)){
98                         if(p->ccrb != 0x02 || p->ccru != 0)
99                                 continue;
100                         ctlr = malloc(sizeof(Ctlr));
101                         if(ctlr == nil){
102                                 print("ne2000pnp: can't allocate memory\n");
103                                 continue;
104                         }
105                         ctlr->pcidev = p;
106                         if(ctlrhead != nil)
107                                 ctlrtail->next = ctlr;
108                         else
109                                 ctlrhead = ctlr;
110                         ctlrtail = ctlr;
111                 }
112         }
113
114         /*
115          * Is it a card with an unrecognised vid+did?
116          * Normally a search is made through all the found controllers
117          * for one which matches any of the known vid+did pairs.
118          * If a vid+did pair is specified a search is made for that
119          * specific controller only.
120          */
121         id = 0;
122         for(i = 0; i < edev->nopt; i++){
123                 if(cistrncmp(edev->opt[i], "id=", 3) == 0)
124                         id = strtol(&edev->opt[i][3], nil, 0);
125         }
126
127         if(id != 0)
128                 ne2000match(edev, id);
129         else for(i = 0; ne2000pci[i].name; i++){
130                 if(ne2000match(edev, ne2000pci[i].id) != nil)
131                         break;
132         }
133 }
134
135 static int
136 ne2000reset(Ether* edev)
137 {
138         ushort buf[16];
139         ulong port;
140         Dp8390 *dp8390;
141         int i;
142         uchar ea[Eaddrlen];
143
144         if(edev->port == 0)
145                 ne2000pnp(edev);
146
147         /*
148          * Set up the software configuration.
149          * Use defaults for irq, mem and size
150          * if not specified.
151          * Must have a port, no more default.
152          */
153         if(edev->port == 0)
154                 return -1;
155         if(edev->irq == 0)
156                 edev->irq = 2;
157         if(edev->mem == 0)
158                 edev->mem = 0x4000;
159         if(edev->size == 0)
160                 edev->size = 16*1024;
161         port = edev->port;
162
163         if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0)
164                 return -1;
165
166         edev->ctlr = malloc(sizeof(Dp8390));
167         if(edev->ctlr == nil){
168                 print("ne2000: can't allocate memory\n");
169                 iofree(port);
170                 return -1;
171         }
172         dp8390 = edev->ctlr;
173         dp8390->width = 2;
174         dp8390->ram = 0;
175
176         dp8390->port = port;
177         dp8390->data = port+Data;
178
179         dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz);
180         dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
181         dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz);
182
183         dp8390->dummyrr = 1;
184         for(i = 0; i < edev->nopt; i++){
185                 if(strcmp(edev->opt[i], "nodummyrr"))
186                         continue;
187                 dp8390->dummyrr = 0;
188                 break;
189         }
190
191         /*
192          * Reset the board. This is done by doing a read
193          * followed by a write to the Reset address.
194          */
195         buf[0] = inb(port+Reset);
196         delay(2);
197         outb(port+Reset, buf[0]);
198         delay(2);
199         
200         /*
201          * Init the (possible) chip, then use the (possible)
202          * chip to read the (possible) PROM for ethernet address
203          * and a marker byte.
204          * Could just look at the DP8390 command register after
205          * initialisation has been tried, but that wouldn't be
206          * enough, there are other ethernet boards which could
207          * match.
208          * Parallels has buf[0x0E] == 0x00 whereas real hardware
209          * usually has 0x57.
210          */
211         dp8390reset(edev);
212         memset(buf, 0, sizeof(buf));
213         dp8390read(dp8390, buf, 0, sizeof(buf));
214         i = buf[0x0E] & 0xFF;
215         if((i != 0x00 && i != 0x57) || (buf[0x0F] & 0xFF) != 0x57){
216                 iofree(edev->port);
217                 free(edev->ctlr);
218                 return -1;
219         }
220
221         /*
222          * Stupid machine. Shorts were asked for,
223          * shorts were delivered, although the PROM is a byte array.
224          * Set the ethernet address.
225          */
226         memset(ea, 0, Eaddrlen);
227         if(memcmp(ea, edev->ea, Eaddrlen) == 0){
228                 for(i = 0; i < sizeof(edev->ea); i++)
229                         edev->ea[i] = buf[i];
230         }
231         dp8390setea(edev);
232
233         return 0;
234 }
235
236 void
237 ether2000link(void)
238 {
239         addethercard("ne2000", ne2000reset);
240 }