1bd389b36SDavid du Colombier #include "u.h"
2bd389b36SDavid du Colombier #include "../port/lib.h"
3bd389b36SDavid du Colombier #include "mem.h"
4bd389b36SDavid du Colombier #include "dat.h"
5bd389b36SDavid du Colombier #include "fns.h"
6bd389b36SDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier #include "../port/error.h"
87dd7cddfSDavid du Colombier #include "../port/netif.h"
9bd389b36SDavid du Colombier
107dd7cddfSDavid du Colombier #include "etherif.h"
117dd7cddfSDavid du Colombier #include "ether8390.h"
12bd389b36SDavid du Colombier
13bd389b36SDavid du Colombier /*
14bd389b36SDavid du Colombier * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
15bd389b36SDavid du Colombier * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
16bd389b36SDavid du Colombier * laptop. The manual says NE2000 compatible.
17bd389b36SDavid du Colombier * The interface appears to be pretty well described in the National
18bd389b36SDavid du Colombier * Semiconductor Local Area Network Databook (1992) as one of the
19219b2ee8SDavid du Colombier * AT evaluation cards.
20bd389b36SDavid du Colombier *
21219b2ee8SDavid du Colombier * The NE2000 is really just a DP8390[12] plus a data port
22bd389b36SDavid du Colombier * and a reset port.
23bd389b36SDavid du Colombier */
24bd389b36SDavid du Colombier enum {
25bd389b36SDavid du Colombier Data = 0x10, /* offset from I/O base of data port */
267dd7cddfSDavid du Colombier Reset = 0x1F, /* offset from I/O base of reset port */
27bd389b36SDavid du Colombier };
28bd389b36SDavid du Colombier
29375daca8SDavid du Colombier typedef struct Ctlr Ctlr;
30375daca8SDavid du Colombier typedef struct Ctlr {
31375daca8SDavid du Colombier Pcidev* pcidev;
32375daca8SDavid du Colombier Ctlr* next;
33375daca8SDavid du Colombier int active;
34375daca8SDavid du Colombier } Ctlr;
35375daca8SDavid du Colombier
36375daca8SDavid du Colombier static Ctlr* ctlrhead;
37375daca8SDavid du Colombier static Ctlr* ctlrtail;
38375daca8SDavid du Colombier
39375daca8SDavid du Colombier static struct {
40375daca8SDavid du Colombier char* name;
41375daca8SDavid du Colombier int id;
42375daca8SDavid du Colombier } ne2000pci[] = {
43375daca8SDavid du Colombier { "Realtek 8029", (0x8029<<16)|0x10EC, },
44375daca8SDavid du Colombier { "Winbond 89C940", (0x0940<<16)|0x1050, },
45375daca8SDavid du Colombier { nil },
46375daca8SDavid du Colombier };
47375daca8SDavid du Colombier
48375daca8SDavid du Colombier static Ctlr*
ne2000match(Ether * edev,int id)49375daca8SDavid du Colombier ne2000match(Ether* edev, int id)
50375daca8SDavid du Colombier {
51375daca8SDavid du Colombier int port;
52375daca8SDavid du Colombier Pcidev *p;
53375daca8SDavid du Colombier Ctlr *ctlr;
54375daca8SDavid du Colombier
55375daca8SDavid du Colombier /*
56375daca8SDavid du Colombier * Any adapter matches if no edev->port is supplied,
57375daca8SDavid du Colombier * otherwise the ports must match.
58375daca8SDavid du Colombier */
59375daca8SDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
60375daca8SDavid du Colombier if(ctlr->active)
61375daca8SDavid du Colombier continue;
62375daca8SDavid du Colombier p = ctlr->pcidev;
63375daca8SDavid du Colombier if(((p->did<<16)|p->vid) != id)
64375daca8SDavid du Colombier continue;
65375daca8SDavid du Colombier port = p->mem[0].bar & ~0x01;
66375daca8SDavid du Colombier if(edev->port != 0 && edev->port != port)
67375daca8SDavid du Colombier continue;
68375daca8SDavid du Colombier
69375daca8SDavid du Colombier /*
70375daca8SDavid du Colombier * It suffices to fill these in,
71375daca8SDavid du Colombier * the rest is gleaned from the card.
72375daca8SDavid du Colombier */
73375daca8SDavid du Colombier edev->port = port;
74375daca8SDavid du Colombier edev->irq = p->intl;
75375daca8SDavid du Colombier
76375daca8SDavid du Colombier ctlr->active = 1;
77375daca8SDavid du Colombier
78375daca8SDavid du Colombier return ctlr;
79375daca8SDavid du Colombier }
80375daca8SDavid du Colombier
81375daca8SDavid du Colombier return nil;
82375daca8SDavid du Colombier }
83375daca8SDavid du Colombier
84375daca8SDavid du Colombier static void
ne2000pnp(Ether * edev)85375daca8SDavid du Colombier ne2000pnp(Ether* edev)
86375daca8SDavid du Colombier {
87375daca8SDavid du Colombier int i, id;
88375daca8SDavid du Colombier Pcidev *p;
89375daca8SDavid du Colombier Ctlr *ctlr;
90375daca8SDavid du Colombier
91375daca8SDavid du Colombier /*
92375daca8SDavid du Colombier * Make a list of all ethernet controllers
93375daca8SDavid du Colombier * if not already done.
94375daca8SDavid du Colombier */
95375daca8SDavid du Colombier if(ctlrhead == nil){
96375daca8SDavid du Colombier p = nil;
97375daca8SDavid du Colombier while(p = pcimatch(p, 0, 0)){
98375daca8SDavid du Colombier if(p->ccrb != 0x02 || p->ccru != 0)
99375daca8SDavid du Colombier continue;
100375daca8SDavid du Colombier ctlr = malloc(sizeof(Ctlr));
101*aa72973aSDavid du Colombier if(ctlr == nil)
102*aa72973aSDavid du Colombier error(Enomem);
103375daca8SDavid du Colombier ctlr->pcidev = p;
104375daca8SDavid du Colombier
105375daca8SDavid du Colombier if(ctlrhead != nil)
106375daca8SDavid du Colombier ctlrtail->next = ctlr;
107375daca8SDavid du Colombier else
108375daca8SDavid du Colombier ctlrhead = ctlr;
109375daca8SDavid du Colombier ctlrtail = ctlr;
110375daca8SDavid du Colombier }
111375daca8SDavid du Colombier }
112375daca8SDavid du Colombier
113375daca8SDavid du Colombier /*
114375daca8SDavid du Colombier * Is it a card with an unrecognised vid+did?
115375daca8SDavid du Colombier * Normally a search is made through all the found controllers
116375daca8SDavid du Colombier * for one which matches any of the known vid+did pairs.
117375daca8SDavid du Colombier * If a vid+did pair is specified a search is made for that
118375daca8SDavid du Colombier * specific controller only.
119375daca8SDavid du Colombier */
120375daca8SDavid du Colombier id = 0;
121375daca8SDavid du Colombier for(i = 0; i < edev->nopt; i++){
122375daca8SDavid du Colombier if(cistrncmp(edev->opt[i], "id=", 3) == 0)
123375daca8SDavid du Colombier id = strtol(&edev->opt[i][3], nil, 0);
124375daca8SDavid du Colombier }
125375daca8SDavid du Colombier
126375daca8SDavid du Colombier if(id != 0)
127375daca8SDavid du Colombier ne2000match(edev, id);
128375daca8SDavid du Colombier else for(i = 0; ne2000pci[i].name; i++){
129375daca8SDavid du Colombier if(ne2000match(edev, ne2000pci[i].id) != nil)
130375daca8SDavid du Colombier break;
131375daca8SDavid du Colombier }
132375daca8SDavid du Colombier }
133375daca8SDavid du Colombier
1347dd7cddfSDavid du Colombier static int
ne2000reset(Ether * edev)135375daca8SDavid du Colombier ne2000reset(Ether* edev)
136bd389b36SDavid du Colombier {
137bd389b36SDavid du Colombier ushort buf[16];
1387dd7cddfSDavid du Colombier ulong port;
139375daca8SDavid du Colombier Dp8390 *dp8390;
140bd389b36SDavid du Colombier int i;
1417dd7cddfSDavid du Colombier uchar ea[Eaddrlen];
142bd389b36SDavid du Colombier
143375daca8SDavid du Colombier if(edev->port == 0)
144375daca8SDavid du Colombier ne2000pnp(edev);
145375daca8SDavid du Colombier
146bd389b36SDavid du Colombier /*
147219b2ee8SDavid du Colombier * Set up the software configuration.
148375daca8SDavid du Colombier * Use defaults for irq, mem and size
149219b2ee8SDavid du Colombier * if not specified.
150375daca8SDavid du Colombier * Must have a port, no more default.
151bd389b36SDavid du Colombier */
152375daca8SDavid du Colombier if(edev->port == 0)
153375daca8SDavid du Colombier return -1;
154375daca8SDavid du Colombier if(edev->irq == 0)
155375daca8SDavid du Colombier edev->irq = 2;
156375daca8SDavid du Colombier if(edev->mem == 0)
157375daca8SDavid du Colombier edev->mem = 0x4000;
158375daca8SDavid du Colombier if(edev->size == 0)
159375daca8SDavid du Colombier edev->size = 16*1024;
160375daca8SDavid du Colombier port = edev->port;
161219b2ee8SDavid du Colombier
162375daca8SDavid du Colombier if(ioalloc(edev->port, 0x20, 0, "ne2000") < 0)
1637dd7cddfSDavid du Colombier return -1;
164219b2ee8SDavid du Colombier
165375daca8SDavid du Colombier edev->ctlr = malloc(sizeof(Dp8390));
166375daca8SDavid du Colombier dp8390 = edev->ctlr;
167*aa72973aSDavid du Colombier if(dp8390 == nil)
168*aa72973aSDavid du Colombier error(Enomem);
169375daca8SDavid du Colombier dp8390->width = 2;
170375daca8SDavid du Colombier dp8390->ram = 0;
171219b2ee8SDavid du Colombier
172375daca8SDavid du Colombier dp8390->port = port;
173375daca8SDavid du Colombier dp8390->data = port+Data;
1747dd7cddfSDavid du Colombier
175375daca8SDavid du Colombier dp8390->tstart = HOWMANY(edev->mem, Dp8390BufSz);
176375daca8SDavid du Colombier dp8390->pstart = dp8390->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
177375daca8SDavid du Colombier dp8390->pstop = dp8390->tstart + HOWMANY(edev->size, Dp8390BufSz);
1787dd7cddfSDavid du Colombier
179375daca8SDavid du Colombier dp8390->dummyrr = 1;
180375daca8SDavid du Colombier for(i = 0; i < edev->nopt; i++){
181375daca8SDavid du Colombier if(strcmp(edev->opt[i], "nodummyrr"))
1827dd7cddfSDavid du Colombier continue;
183375daca8SDavid du Colombier dp8390->dummyrr = 0;
1847dd7cddfSDavid du Colombier break;
1857dd7cddfSDavid du Colombier }
186219b2ee8SDavid du Colombier
187bd389b36SDavid du Colombier /*
188bd389b36SDavid du Colombier * Reset the board. This is done by doing a read
189bd389b36SDavid du Colombier * followed by a write to the Reset address.
190bd389b36SDavid du Colombier */
1917dd7cddfSDavid du Colombier buf[0] = inb(port+Reset);
192219b2ee8SDavid du Colombier delay(2);
1937dd7cddfSDavid du Colombier outb(port+Reset, buf[0]);
1947dd7cddfSDavid du Colombier delay(2);
195bd389b36SDavid du Colombier
196bd389b36SDavid du Colombier /*
197bd389b36SDavid du Colombier * Init the (possible) chip, then use the (possible)
198bd389b36SDavid du Colombier * chip to read the (possible) PROM for ethernet address
199bd389b36SDavid du Colombier * and a marker byte.
2007dd7cddfSDavid du Colombier * Could just look at the DP8390 command register after
201bd389b36SDavid du Colombier * initialisation has been tried, but that wouldn't be
202bd389b36SDavid du Colombier * enough, there are other ethernet boards which could
203bd389b36SDavid du Colombier * match.
2044fafed5dSDavid du Colombier * Parallels has buf[0x0E] == 0x00 whereas real hardware
2054fafed5dSDavid du Colombier * usually has 0x57.
206bd389b36SDavid du Colombier */
207375daca8SDavid du Colombier dp8390reset(edev);
208bd389b36SDavid du Colombier memset(buf, 0, sizeof(buf));
209375daca8SDavid du Colombier dp8390read(dp8390, buf, 0, sizeof(buf));
2104fafed5dSDavid du Colombier i = buf[0x0E] & 0xFF;
2114fafed5dSDavid du Colombier if((i != 0x00 && i != 0x57) || (buf[0x0F] & 0xFF) != 0x57){
212375daca8SDavid du Colombier iofree(edev->port);
213375daca8SDavid du Colombier free(edev->ctlr);
214bd389b36SDavid du Colombier return -1;
2157dd7cddfSDavid du Colombier }
216bd389b36SDavid du Colombier
217bd389b36SDavid du Colombier /*
2187dd7cddfSDavid du Colombier * Stupid machine. Shorts were asked for,
2197dd7cddfSDavid du Colombier * shorts were delivered, although the PROM is a byte array.
2207dd7cddfSDavid du Colombier * Set the ethernet address.
221bd389b36SDavid du Colombier */
2227dd7cddfSDavid du Colombier memset(ea, 0, Eaddrlen);
223375daca8SDavid du Colombier if(memcmp(ea, edev->ea, Eaddrlen) == 0){
224375daca8SDavid du Colombier for(i = 0; i < sizeof(edev->ea); i++)
225375daca8SDavid du Colombier edev->ea[i] = buf[i];
226219b2ee8SDavid du Colombier }
227375daca8SDavid du Colombier dp8390setea(edev);
228bd389b36SDavid du Colombier
229bd389b36SDavid du Colombier return 0;
230bd389b36SDavid du Colombier }
231bd389b36SDavid du Colombier
232219b2ee8SDavid du Colombier void
ether2000link(void)233219b2ee8SDavid du Colombier ether2000link(void)
234219b2ee8SDavid du Colombier {
235375daca8SDavid du Colombier addethercard("NE2000", ne2000reset);
236219b2ee8SDavid du Colombier }
237