xref: /plan9-contrib/sys/src/9/pc/ether2000.c (revision aa72973a2891ccbd3fb042462446761159389e19)
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