xref: /plan9/sys/src/9/pc/ether8390.c (revision ca045b349d1e3b05eaae4b7fb24367eecc5b0e8b)
1bd389b36SDavid du Colombier /*
27dd7cddfSDavid du Colombier  * National Semiconductor DP8390 and clone
3bd389b36SDavid du Colombier  * Network Interface Controller.
4bd389b36SDavid du Colombier  */
5bd389b36SDavid du Colombier #include "u.h"
6bd389b36SDavid du Colombier #include "../port/lib.h"
7bd389b36SDavid du Colombier #include "mem.h"
8bd389b36SDavid du Colombier #include "dat.h"
9bd389b36SDavid du Colombier #include "fns.h"
10bd389b36SDavid du Colombier #include "io.h"
117dd7cddfSDavid du Colombier #include "../port/error.h"
127dd7cddfSDavid du Colombier #include "../port/netif.h"
13bd389b36SDavid du Colombier 
147dd7cddfSDavid du Colombier #include "etherif.h"
157dd7cddfSDavid du Colombier #include "ether8390.h"
16bd389b36SDavid du Colombier 
177dd7cddfSDavid du Colombier enum {					/* NIC core registers */
18bd389b36SDavid du Colombier 	Cr		= 0x00,		/* command register, all pages */
19bd389b36SDavid du Colombier 
207dd7cddfSDavid du Colombier 					/* Page 0, read */
21bd389b36SDavid du Colombier 	Clda0		= 0x01,		/* current local DMA address 0 */
22bd389b36SDavid du Colombier 	Clda1		= 0x02,		/* current local DMA address 1 */
23bd389b36SDavid du Colombier 	Bnry		= 0x03,		/* boundary pointer (R/W) */
24bd389b36SDavid du Colombier 	Tsr		= 0x04,		/* transmit status register */
25bd389b36SDavid du Colombier 	Ncr		= 0x05,		/* number of collisions register */
26bd389b36SDavid du Colombier 	Fifo		= 0x06,		/* FIFO */
27bd389b36SDavid du Colombier 	Isr		= 0x07,		/* interrupt status register (R/W) */
28bd389b36SDavid du Colombier 	Crda0		= 0x08,		/* current remote DMA address 0 */
29219b2ee8SDavid du Colombier 	Crda1		= 0x09,		/* current remote DMA address 1 */
30bd389b36SDavid du Colombier 	Rsr		= 0x0C,		/* receive status register */
31e288d156SDavid du Colombier 	Ref0		= 0x0D,		/* frame alignment errors */
32e288d156SDavid du Colombier 	Ref1		= 0x0E,		/* CRC errors */
33e288d156SDavid du Colombier 	Ref2		= 0x0F,		/* missed packet errors */
34bd389b36SDavid du Colombier 
357dd7cddfSDavid du Colombier 					/* Page 0, write */
36bd389b36SDavid du Colombier 	Pstart		= 0x01,		/* page start register */
37bd389b36SDavid du Colombier 	Pstop		= 0x02,		/* page stop register */
38bd389b36SDavid du Colombier 	Tpsr		= 0x04,		/* transmit page start address */
39bd389b36SDavid du Colombier 	Tbcr0		= 0x05,		/* transmit byte count register 0 */
40bd389b36SDavid du Colombier 	Tbcr1		= 0x06,		/* transmit byte count register 1 */
41bd389b36SDavid du Colombier 	Rsar0		= 0x08,		/* remote start address register 0 */
42bd389b36SDavid du Colombier 	Rsar1		= 0x09,		/* remote start address register 1 */
43bd389b36SDavid du Colombier 	Rbcr0		= 0x0A,		/* remote byte count register 0 */
44bd389b36SDavid du Colombier 	Rbcr1		= 0x0B,		/* remote byte count register 1 */
45bd389b36SDavid du Colombier 	Rcr		= 0x0C,		/* receive configuration register */
46bd389b36SDavid du Colombier 	Tcr		= 0x0D,		/* transmit configuration register */
47bd389b36SDavid du Colombier 	Dcr		= 0x0E,		/* data configuration register */
48bd389b36SDavid du Colombier 	Imr		= 0x0F,		/* interrupt mask */
49bd389b36SDavid du Colombier 
507dd7cddfSDavid du Colombier 					/* Page 1, read/write */
51bd389b36SDavid du Colombier 	Par0		= 0x01,		/* physical address register 0 */
52bd389b36SDavid du Colombier 	Curr		= 0x07,		/* current page register */
53bd389b36SDavid du Colombier 	Mar0		= 0x08,		/* multicast address register 0 */
54bd389b36SDavid du Colombier };
55bd389b36SDavid du Colombier 
567dd7cddfSDavid du Colombier enum {					/* Cr */
577dd7cddfSDavid du Colombier 	Stp		= 0x01,		/* stop */
587dd7cddfSDavid du Colombier 	Sta		= 0x02,		/* start */
597dd7cddfSDavid du Colombier 	Txp		= 0x04,		/* transmit packet */
607dd7cddfSDavid du Colombier 	Rd0		= 0x08,		/* remote DMA command */
617dd7cddfSDavid du Colombier 	Rd1		= 0x10,
627dd7cddfSDavid du Colombier 	Rd2		= 0x20,
637dd7cddfSDavid du Colombier 	RdREAD		= Rd0,		/* remote read */
647dd7cddfSDavid du Colombier 	RdWRITE		= Rd1,		/* remote write */
657dd7cddfSDavid du Colombier 	RdSEND		= Rd1|Rd0,	/* send packet */
667dd7cddfSDavid du Colombier 	RdABORT		= Rd2,		/* abort/complete remote DMA */
677dd7cddfSDavid du Colombier 	Ps0		= 0x40,		/* page select */
687dd7cddfSDavid du Colombier 	Ps1		= 0x80,
697dd7cddfSDavid du Colombier 	Page0		= 0x00,
707dd7cddfSDavid du Colombier 	Page1		= Ps0,
717dd7cddfSDavid du Colombier 	Page2		= Ps1,
727dd7cddfSDavid du Colombier };
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier enum {					/* Isr/Imr */
75bd389b36SDavid du Colombier 	Prx		= 0x01,		/* packet received */
76bd389b36SDavid du Colombier 	Ptx		= 0x02,		/* packet transmitted */
77bd389b36SDavid du Colombier 	Rxe		= 0x04,		/* receive error */
78bd389b36SDavid du Colombier 	Txe		= 0x08,		/* transmit error */
79bd389b36SDavid du Colombier 	Ovw		= 0x10,		/* overwrite warning */
80bd389b36SDavid du Colombier 	Cnt		= 0x20,		/* counter overflow */
81bd389b36SDavid du Colombier 	Rdc		= 0x40,		/* remote DMA complete */
82bd389b36SDavid du Colombier 	Rst		= 0x80,		/* reset status */
83bd389b36SDavid du Colombier };
84bd389b36SDavid du Colombier 
857dd7cddfSDavid du Colombier enum {					/* Dcr */
86bd389b36SDavid du Colombier 	Wts		= 0x01,		/* word transfer select */
87bd389b36SDavid du Colombier 	Bos		= 0x02,		/* byte order select */
88bd389b36SDavid du Colombier 	Las		= 0x04,		/* long address select */
89bd389b36SDavid du Colombier 	Ls		= 0x08,		/* loopback select */
90bd389b36SDavid du Colombier 	Arm		= 0x10,		/* auto-initialise remote */
917dd7cddfSDavid du Colombier 	Ft0		= 0x20,		/* FIFO threshold select */
927dd7cddfSDavid du Colombier 	Ft1		= 0x40,
937dd7cddfSDavid du Colombier 	Ft1WORD		= 0x00,
947dd7cddfSDavid du Colombier 	Ft2WORD		= Ft0,
957dd7cddfSDavid du Colombier 	Ft4WORD		= Ft1,
967dd7cddfSDavid du Colombier 	Ft6WORD		= Ft1|Ft0,
97bd389b36SDavid du Colombier };
98bd389b36SDavid du Colombier 
997dd7cddfSDavid du Colombier enum {					/* Tcr */
100bd389b36SDavid du Colombier 	Crc		= 0x01,		/* inhibit CRC */
1017dd7cddfSDavid du Colombier 	Lb0		= 0x02,		/* encoded loopback control */
1027dd7cddfSDavid du Colombier 	Lb1		= 0x04,
1037dd7cddfSDavid du Colombier 	LpbkNORMAL	= 0x00,		/* normal operation */
1047dd7cddfSDavid du Colombier 	LpbkNIC		= Lb0,		/* internal NIC module loopback */
1057dd7cddfSDavid du Colombier 	LpbkENDEC	= Lb1,		/* internal ENDEC module loopback */
1067dd7cddfSDavid du Colombier 	LpbkEXTERNAL	= Lb1|Lb0,	/* external loopback */
107bd389b36SDavid du Colombier 	Atd		= 0x08,		/* auto transmit disable */
108bd389b36SDavid du Colombier 	Ofst		= 0x10,		/* collision offset enable */
109bd389b36SDavid du Colombier };
110bd389b36SDavid du Colombier 
1117dd7cddfSDavid du Colombier enum {					/* Tsr */
112bd389b36SDavid du Colombier 	Ptxok		= 0x01,		/* packet transmitted */
113bd389b36SDavid du Colombier 	Col		= 0x04,		/* transmit collided */
114bd389b36SDavid du Colombier 	Abt		= 0x08,		/* tranmit aborted */
115bd389b36SDavid du Colombier 	Crs		= 0x10,		/* carrier sense lost */
116bd389b36SDavid du Colombier 	Fu		= 0x20,		/* FIFO underrun */
117bd389b36SDavid du Colombier 	Cdh		= 0x40,		/* CD heartbeat */
118bd389b36SDavid du Colombier 	Owc		= 0x80,		/* out of window collision */
119bd389b36SDavid du Colombier };
120bd389b36SDavid du Colombier 
1217dd7cddfSDavid du Colombier enum {					/* Rcr */
122bd389b36SDavid du Colombier 	Sep		= 0x01,		/* save errored packets */
123bd389b36SDavid du Colombier 	Ar		= 0x02,		/* accept runt packets */
124bd389b36SDavid du Colombier 	Ab		= 0x04,		/* accept broadcast */
125bd389b36SDavid du Colombier 	Am		= 0x08,		/* accept multicast */
126bd389b36SDavid du Colombier 	Pro		= 0x10,		/* promiscuous physical */
127bd389b36SDavid du Colombier 	Mon		= 0x20,		/* monitor mode */
128bd389b36SDavid du Colombier };
129bd389b36SDavid du Colombier 
1307dd7cddfSDavid du Colombier enum {					/* Rsr */
131bd389b36SDavid du Colombier 	Prxok		= 0x01,		/* packet received intact */
132bd389b36SDavid du Colombier 	Crce		= 0x02,		/* CRC error */
133bd389b36SDavid du Colombier 	Fae		= 0x04,		/* frame alignment error */
134bd389b36SDavid du Colombier 	Fo		= 0x08,		/* FIFO overrun */
135bd389b36SDavid du Colombier 	Mpa		= 0x10,		/* missed packet */
136bd389b36SDavid du Colombier 	Phy		= 0x20,		/* physical/multicast address */
137bd389b36SDavid du Colombier 	Dis		= 0x40,		/* receiver disabled */
138bd389b36SDavid du Colombier 	Dfr		= 0x80,		/* deferring */
139bd389b36SDavid du Colombier };
140bd389b36SDavid du Colombier 
14159c21d95SDavid du Colombier typedef struct Hdr Hdr;
14259c21d95SDavid du Colombier struct Hdr {
143bd389b36SDavid du Colombier 	uchar	status;
144bd389b36SDavid du Colombier 	uchar	next;
145bd389b36SDavid du Colombier 	uchar	len0;
146bd389b36SDavid du Colombier 	uchar	len1;
14759c21d95SDavid du Colombier };
148bd389b36SDavid du Colombier 
149bd389b36SDavid du Colombier void
dp8390getea(Ether * ether,uchar * ea)1507dd7cddfSDavid du Colombier dp8390getea(Ether* ether, uchar* ea)
151bd389b36SDavid du Colombier {
1527dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
153bd389b36SDavid du Colombier 	uchar cr;
154bd389b36SDavid du Colombier 	int i;
155bd389b36SDavid du Colombier 
1567dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
1577dd7cddfSDavid du Colombier 
158bd389b36SDavid du Colombier 	/*
1597dd7cddfSDavid du Colombier 	 * Get the ethernet address from the chip.
160bd389b36SDavid du Colombier 	 * Take care to restore the command register
1617dd7cddfSDavid du Colombier 	 * afterwards.
162bd389b36SDavid du Colombier 	 */
1637dd7cddfSDavid du Colombier 	ilock(ctlr);
1647dd7cddfSDavid du Colombier 	cr = regr(ctlr, Cr) & ~Txp;
1657dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
1667dd7cddfSDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
1677dd7cddfSDavid du Colombier 		ea[i] = regr(ctlr, Par0+i);
1687dd7cddfSDavid du Colombier 	regw(ctlr, Cr, cr);
1697dd7cddfSDavid du Colombier 	iunlock(ctlr);
170219b2ee8SDavid du Colombier }
171219b2ee8SDavid du Colombier 
172219b2ee8SDavid du Colombier void
dp8390setea(Ether * ether)1737dd7cddfSDavid du Colombier dp8390setea(Ether* ether)
174219b2ee8SDavid du Colombier {
175219b2ee8SDavid du Colombier 	int i;
1767dd7cddfSDavid du Colombier 	uchar cr;
1777dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
178219b2ee8SDavid du Colombier 
1797dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
1807dd7cddfSDavid du Colombier 
181219b2ee8SDavid du Colombier 	/*
182219b2ee8SDavid du Colombier 	 * Set the ethernet address into the chip.
183219b2ee8SDavid du Colombier 	 * Take care to restore the command register
1847dd7cddfSDavid du Colombier 	 * afterwards. Don't care about multicast
1857dd7cddfSDavid du Colombier 	 * addresses as multicast is never enabled
1867dd7cddfSDavid du Colombier 	 * (currently).
187219b2ee8SDavid du Colombier 	 */
1887dd7cddfSDavid du Colombier 	ilock(ctlr);
1897dd7cddfSDavid du Colombier 	cr = regr(ctlr, Cr) & ~Txp;
1907dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
1917dd7cddfSDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
1927dd7cddfSDavid du Colombier 		regw(ctlr, Par0+i, ether->ea[i]);
1937dd7cddfSDavid du Colombier 	regw(ctlr, Cr, cr);
1947dd7cddfSDavid du Colombier 	iunlock(ctlr);
195bd389b36SDavid du Colombier }
196bd389b36SDavid du Colombier 
1977dd7cddfSDavid du Colombier static void*
_dp8390read(Dp8390 * ctlr,void * to,ulong from,ulong len)1987dd7cddfSDavid du Colombier _dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
199bd389b36SDavid du Colombier {
200bd389b36SDavid du Colombier 	uchar cr;
201bd389b36SDavid du Colombier 	int timo;
202bd389b36SDavid du Colombier 
203bd389b36SDavid du Colombier 	/*
204219b2ee8SDavid du Colombier 	 * Read some data at offset 'from' in the card's memory
205bd389b36SDavid du Colombier 	 * using the DP8390 remote DMA facility, and place it at
206bd389b36SDavid du Colombier 	 * 'to' in main memory, via the I/O data port.
207bd389b36SDavid du Colombier 	 */
2087dd7cddfSDavid du Colombier 	cr = regr(ctlr, Cr) & ~Txp;
2097dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Sta);
2107dd7cddfSDavid du Colombier 	regw(ctlr, Isr, Rdc);
211bd389b36SDavid du Colombier 
212bd389b36SDavid du Colombier 	/*
213bd389b36SDavid du Colombier 	 * Set up the remote DMA address and count.
214bd389b36SDavid du Colombier 	 */
2157dd7cddfSDavid du Colombier 	len = ROUNDUP(len, ctlr->width);
2167dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr0, len & 0xFF);
2177dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr1, (len>>8) & 0xFF);
2187dd7cddfSDavid du Colombier 	regw(ctlr, Rsar0, from & 0xFF);
2197dd7cddfSDavid du Colombier 	regw(ctlr, Rsar1, (from>>8) & 0xFF);
220bd389b36SDavid du Colombier 
221bd389b36SDavid du Colombier 	/*
222bd389b36SDavid du Colombier 	 * Start the remote DMA read and suck the data
223bd389b36SDavid du Colombier 	 * out of the I/O port.
224bd389b36SDavid du Colombier 	 */
2257dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdREAD|Sta);
2267dd7cddfSDavid du Colombier 	rdread(ctlr, to, len);
227bd389b36SDavid du Colombier 
228bd389b36SDavid du Colombier 	/*
229bd389b36SDavid du Colombier 	 * Wait for the remote DMA to complete. The timeout
2307dd7cddfSDavid du Colombier 	 * is necessary because this routine may be called on
231bd389b36SDavid du Colombier 	 * a non-existent chip during initialisation and, due
2327dd7cddfSDavid du Colombier 	 * to the miracles of the bus, it's possible to get this
2337dd7cddfSDavid du Colombier 	 * far and still be talking to a slot full of nothing.
234bd389b36SDavid du Colombier 	 */
2357dd7cddfSDavid du Colombier 	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
236bd389b36SDavid du Colombier 			;
237bd389b36SDavid du Colombier 
2387dd7cddfSDavid du Colombier 	regw(ctlr, Isr, Rdc);
2397dd7cddfSDavid du Colombier 	regw(ctlr, Cr, cr);
2407dd7cddfSDavid du Colombier 
241bd389b36SDavid du Colombier 	return to;
242bd389b36SDavid du Colombier }
243bd389b36SDavid du Colombier 
244bd389b36SDavid du Colombier void*
dp8390read(Dp8390 * ctlr,void * to,ulong from,ulong len)2457dd7cddfSDavid du Colombier dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
246bd389b36SDavid du Colombier {
2477dd7cddfSDavid du Colombier 	void *v;
248bd389b36SDavid du Colombier 
2497dd7cddfSDavid du Colombier 	ilock(ctlr);
2507dd7cddfSDavid du Colombier 	v = _dp8390read(ctlr, to, from, len);
2517dd7cddfSDavid du Colombier 	iunlock(ctlr);
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier 	return v;
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier static void*
dp8390write(Dp8390 * ctlr,ulong to,void * from,ulong len)2577dd7cddfSDavid du Colombier dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
2587dd7cddfSDavid du Colombier {
2597dd7cddfSDavid du Colombier 	ulong crda;
2607dd7cddfSDavid du Colombier 	uchar cr;
2617dd7cddfSDavid du Colombier 	int timo, width;
2627dd7cddfSDavid du Colombier 
263a22b0629SDavid du Colombier top:
264bd389b36SDavid du Colombier 	/*
265219b2ee8SDavid du Colombier 	 * Write some data to offset 'to' in the card's memory
266bd389b36SDavid du Colombier 	 * using the DP8390 remote DMA facility, reading it at
267bd389b36SDavid du Colombier 	 * 'from' in main memory, via the I/O data port.
268bd389b36SDavid du Colombier 	 */
2697dd7cddfSDavid du Colombier 	cr = regr(ctlr, Cr) & ~Txp;
2707dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Sta);
2717dd7cddfSDavid du Colombier 	regw(ctlr, Isr, Rdc);
272219b2ee8SDavid du Colombier 
2737dd7cddfSDavid du Colombier 	len = ROUNDUP(len, ctlr->width);
274bd389b36SDavid du Colombier 
275bd389b36SDavid du Colombier 	/*
276bd389b36SDavid du Colombier 	 * Set up the remote DMA address and count.
2777dd7cddfSDavid du Colombier 	 * This is straight from the DP8390[12D] datasheet,
2787dd7cddfSDavid du Colombier 	 * hence the initial set up for read.
2797dd7cddfSDavid du Colombier 	 * Assumption here that the A7000 EtherV card will
2807dd7cddfSDavid du Colombier 	 * never need a dummyrr.
281bd389b36SDavid du Colombier 	 */
2827dd7cddfSDavid du Colombier 	if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
2837dd7cddfSDavid du Colombier 		if(ctlr->width == 2)
2847dd7cddfSDavid du Colombier 			width = 1;
2857dd7cddfSDavid du Colombier 		else
2867dd7cddfSDavid du Colombier 			width = 0;
2877dd7cddfSDavid du Colombier 		crda = to-1-width;
2887dd7cddfSDavid du Colombier 		regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
2897dd7cddfSDavid du Colombier 		regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
2907dd7cddfSDavid du Colombier 		regw(ctlr, Rsar0, crda & 0xFF);
2917dd7cddfSDavid du Colombier 		regw(ctlr, Rsar1, (crda>>8) & 0xFF);
2927dd7cddfSDavid du Colombier 		regw(ctlr, Cr, Page0|RdREAD|Sta);
293bd389b36SDavid du Colombier 
294a22b0629SDavid du Colombier 		for(timo=0;; timo++){
295a22b0629SDavid du Colombier 			if(timo > 10000){
296a22b0629SDavid du Colombier 				print("ether8390: dummyrr timeout; assuming nodummyrr\n");
297a22b0629SDavid du Colombier 				ctlr->dummyrr = 0;
298a22b0629SDavid du Colombier 				goto top;
299a22b0629SDavid du Colombier 			}
3007dd7cddfSDavid du Colombier 			crda = regr(ctlr, Crda0);
3017dd7cddfSDavid du Colombier 			crda |= regr(ctlr, Crda1)<<8;
302219b2ee8SDavid du Colombier 			if(crda == to){
303219b2ee8SDavid du Colombier 				/*
304219b2ee8SDavid du Colombier 				 * Start the remote DMA write and make sure
305219b2ee8SDavid du Colombier 				 * the registers are correct.
306219b2ee8SDavid du Colombier 				 */
3077dd7cddfSDavid du Colombier 				regw(ctlr, Cr, Page0|RdWRITE|Sta);
308219b2ee8SDavid du Colombier 
3097dd7cddfSDavid du Colombier 				crda = regr(ctlr, Crda0);
3107dd7cddfSDavid du Colombier 				crda |= regr(ctlr, Crda1)<<8;
311219b2ee8SDavid du Colombier 				if(crda != to)
3129a747e4fSDavid du Colombier 					panic("crda write %lud to %lud\n", crda, to);
313219b2ee8SDavid du Colombier 
314219b2ee8SDavid du Colombier 				break;
315219b2ee8SDavid du Colombier 			}
316219b2ee8SDavid du Colombier 		}
3177dd7cddfSDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 	else{
3197dd7cddfSDavid du Colombier 		regw(ctlr, Rsar0, to & 0xFF);
3207dd7cddfSDavid du Colombier 		regw(ctlr, Rsar1, (to>>8) & 0xFF);
3217dd7cddfSDavid du Colombier 		regw(ctlr, Rbcr0, len & 0xFF);
3227dd7cddfSDavid du Colombier 		regw(ctlr, Rbcr1, (len>>8) & 0xFF);
3237dd7cddfSDavid du Colombier 		regw(ctlr, Cr, Page0|RdWRITE|Sta);
3247dd7cddfSDavid du Colombier 	}
325bd389b36SDavid du Colombier 
326bd389b36SDavid du Colombier 	/*
3277dd7cddfSDavid du Colombier 	 * Pump the data into the I/O port
3287dd7cddfSDavid du Colombier 	 * then wait for the remote DMA to finish.
329bd389b36SDavid du Colombier 	 */
3307dd7cddfSDavid du Colombier 	rdwrite(ctlr, from, len);
3317dd7cddfSDavid du Colombier 	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
332bd389b36SDavid du Colombier 			;
333bd389b36SDavid du Colombier 
3347dd7cddfSDavid du Colombier 	regw(ctlr, Isr, Rdc);
3357dd7cddfSDavid du Colombier 	regw(ctlr, Cr, cr);
3367dd7cddfSDavid du Colombier 
337bd389b36SDavid du Colombier 	return (void*)to;
338bd389b36SDavid du Colombier }
339bd389b36SDavid du Colombier 
3407dd7cddfSDavid du Colombier static void
ringinit(Dp8390 * ctlr)3417dd7cddfSDavid du Colombier ringinit(Dp8390* ctlr)
3427dd7cddfSDavid du Colombier {
3437dd7cddfSDavid du Colombier 	regw(ctlr, Pstart, ctlr->pstart);
3447dd7cddfSDavid du Colombier 	regw(ctlr, Pstop, ctlr->pstop);
3457dd7cddfSDavid du Colombier 	regw(ctlr, Bnry, ctlr->pstop-1);
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page1|RdABORT|Stp);
3487dd7cddfSDavid du Colombier 	regw(ctlr, Curr, ctlr->pstart);
3497dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Stp);
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 	ctlr->nxtpkt = ctlr->pstart;
3527dd7cddfSDavid du Colombier }
3537dd7cddfSDavid du Colombier 
354219b2ee8SDavid du Colombier static uchar
getcurr(Dp8390 * ctlr)3557dd7cddfSDavid du Colombier getcurr(Dp8390* ctlr)
356bd389b36SDavid du Colombier {
357219b2ee8SDavid du Colombier 	uchar cr, curr;
358bd389b36SDavid du Colombier 
3597dd7cddfSDavid du Colombier 	cr = regr(ctlr, Cr) & ~Txp;
3607dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
3617dd7cddfSDavid du Colombier 	curr = regr(ctlr, Curr);
3627dd7cddfSDavid du Colombier 	regw(ctlr, Cr, cr);
3637dd7cddfSDavid du Colombier 
364219b2ee8SDavid du Colombier 	return curr;
365219b2ee8SDavid du Colombier }
366bd389b36SDavid du Colombier 
367219b2ee8SDavid du Colombier static void
receive(Ether * ether)3687dd7cddfSDavid du Colombier receive(Ether* ether)
369219b2ee8SDavid du Colombier {
3707dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
3717dd7cddfSDavid du Colombier 	uchar curr, *p;
372219b2ee8SDavid du Colombier 	Hdr hdr;
3737dd7cddfSDavid du Colombier 	ulong count, data, len;
3747dd7cddfSDavid du Colombier 	Block *bp;
375219b2ee8SDavid du Colombier 
3767dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
3777dd7cddfSDavid du Colombier 	for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
3787dd7cddfSDavid du Colombier 		data = ctlr->nxtpkt*Dp8390BufSz;
3797dd7cddfSDavid du Colombier 		if(ctlr->ram)
3807dd7cddfSDavid du Colombier 			memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
3817dd7cddfSDavid du Colombier 		else
3827dd7cddfSDavid du Colombier 			_dp8390read(ctlr, &hdr, data, sizeof(Hdr));
383bd389b36SDavid du Colombier 
384bd389b36SDavid du Colombier 		/*
385219b2ee8SDavid du Colombier 		 * Don't believe the upper byte count, work it
386219b2ee8SDavid du Colombier 		 * out from the software next-page pointer and
387219b2ee8SDavid du Colombier 		 * the current next-page pointer.
388bd389b36SDavid du Colombier 		 */
3897dd7cddfSDavid du Colombier 		if(hdr.next > ctlr->nxtpkt)
3907dd7cddfSDavid du Colombier 			len = hdr.next - ctlr->nxtpkt - 1;
391219b2ee8SDavid du Colombier 		else
3927dd7cddfSDavid du Colombier 			len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
393219b2ee8SDavid du Colombier 		if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
3947dd7cddfSDavid du Colombier 			len--;
395219b2ee8SDavid du Colombier 
3967dd7cddfSDavid du Colombier 		len = ((len<<8)|hdr.len0)-4;
397219b2ee8SDavid du Colombier 
398219b2ee8SDavid du Colombier 		/*
399219b2ee8SDavid du Colombier 		 * Chip is badly scrogged, reinitialise the ring.
400219b2ee8SDavid du Colombier 		 */
4017dd7cddfSDavid du Colombier 		if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
402219b2ee8SDavid du Colombier 		  || len < 60 || len > sizeof(Etherpkt)){
4034de34a7eSDavid du Colombier 			print("dp8390: H%2.2ux+%2.2ux+%2.2ux+%2.2ux,%lud\n",
404219b2ee8SDavid du Colombier 				hdr.status, hdr.next, hdr.len0, hdr.len1, len);
4057dd7cddfSDavid du Colombier 			regw(ctlr, Cr, Page0|RdABORT|Stp);
4067dd7cddfSDavid du Colombier 			ringinit(ctlr);
4077dd7cddfSDavid du Colombier 			regw(ctlr, Cr, Page0|RdABORT|Sta);
4087dd7cddfSDavid du Colombier 
409bd389b36SDavid du Colombier 			return;
410bd389b36SDavid du Colombier 		}
411bd389b36SDavid du Colombier 
412219b2ee8SDavid du Colombier 		/*
4137dd7cddfSDavid du Colombier 		 * If it's a good packet read it in to the software buffer.
4147dd7cddfSDavid du Colombier 		 * If the packet wraps round the hardware ring, read it in
4157dd7cddfSDavid du Colombier 		 * two pieces.
416219b2ee8SDavid du Colombier 		 */
4177dd7cddfSDavid du Colombier 		if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){
4187dd7cddfSDavid du Colombier 			p = bp->rp;
4197dd7cddfSDavid du Colombier 			bp->wp = p+len;
420bd389b36SDavid du Colombier 			data += sizeof(Hdr);
421219b2ee8SDavid du Colombier 
4227dd7cddfSDavid du Colombier 			if((data+len) >= ctlr->pstop*Dp8390BufSz){
4237dd7cddfSDavid du Colombier 				count = ctlr->pstop*Dp8390BufSz - data;
4247dd7cddfSDavid du Colombier 				if(ctlr->ram)
4257dd7cddfSDavid du Colombier 					memmove(p, (void*)(ether->mem+data), count);
4267dd7cddfSDavid du Colombier 				else
4277dd7cddfSDavid du Colombier 					_dp8390read(ctlr, p, data, count);
4287dd7cddfSDavid du Colombier 				p += count;
4297dd7cddfSDavid du Colombier 				data = ctlr->pstart*Dp8390BufSz;
430219b2ee8SDavid du Colombier 				len -= count;
431bd389b36SDavid du Colombier 			}
4327dd7cddfSDavid du Colombier 			if(len){
4337dd7cddfSDavid du Colombier 				if(ctlr->ram)
4347dd7cddfSDavid du Colombier 					memmove(p, (void*)(ether->mem+data), len);
4357dd7cddfSDavid du Colombier 				else
4367dd7cddfSDavid du Colombier 					_dp8390read(ctlr, p, data, len);
437bd389b36SDavid du Colombier 			}
438bd389b36SDavid du Colombier 
439219b2ee8SDavid du Colombier 			/*
4407dd7cddfSDavid du Colombier 			 * Copy the packet to whoever wants it.
4417dd7cddfSDavid du Colombier 			 */
4427dd7cddfSDavid du Colombier 			etheriq(ether, bp, 1);
4437dd7cddfSDavid du Colombier 		}
4447dd7cddfSDavid du Colombier 
4457dd7cddfSDavid du Colombier 		/*
4467dd7cddfSDavid du Colombier 		 * Finished with this packet, update the
447219b2ee8SDavid du Colombier 		 * hardware and software ring pointers.
448219b2ee8SDavid du Colombier 		 */
4497dd7cddfSDavid du Colombier 		ctlr->nxtpkt = hdr.next;
450219b2ee8SDavid du Colombier 
451219b2ee8SDavid du Colombier 		hdr.next--;
4527dd7cddfSDavid du Colombier 		if(hdr.next < ctlr->pstart)
4537dd7cddfSDavid du Colombier 			hdr.next = ctlr->pstop-1;
4547dd7cddfSDavid du Colombier 		regw(ctlr, Bnry, hdr.next);
455219b2ee8SDavid du Colombier 	}
456219b2ee8SDavid du Colombier }
457219b2ee8SDavid du Colombier 
4587dd7cddfSDavid du Colombier static void
txstart(Ether * ether)4597dd7cddfSDavid du Colombier txstart(Ether* ether)
4607dd7cddfSDavid du Colombier {
4617dd7cddfSDavid du Colombier 	int len;
4627dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
4637dd7cddfSDavid du Colombier 	Block *bp;
4647dd7cddfSDavid du Colombier 	uchar minpkt[ETHERMINTU], *rp;
4657dd7cddfSDavid du Colombier 
4667dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
4677dd7cddfSDavid du Colombier 
468219b2ee8SDavid du Colombier 	/*
4697dd7cddfSDavid du Colombier 	 * This routine is called both from the top level and from interrupt
4707dd7cddfSDavid du Colombier 	 * level and expects to be called with ctlr already locked.
471219b2ee8SDavid du Colombier 	 */
4727dd7cddfSDavid du Colombier 	if(ctlr->txbusy)
4737dd7cddfSDavid du Colombier 		return;
4747dd7cddfSDavid du Colombier 	bp = qget(ether->oq);
4757dd7cddfSDavid du Colombier 	if(bp == nil)
4767dd7cddfSDavid du Colombier 		return;
477219b2ee8SDavid du Colombier 
4787dd7cddfSDavid du Colombier 	/*
4797dd7cddfSDavid du Colombier 	 * Make sure the packet is of minimum length;
4807dd7cddfSDavid du Colombier 	 * copy it to the card's memory by the appropriate means;
4817dd7cddfSDavid du Colombier 	 * start the transmission.
4827dd7cddfSDavid du Colombier 	 */
4837dd7cddfSDavid du Colombier 	len = BLEN(bp);
4847dd7cddfSDavid du Colombier 	rp = bp->rp;
4857dd7cddfSDavid du Colombier 	if(len < ETHERMINTU){
4867dd7cddfSDavid du Colombier 		rp = minpkt;
4877dd7cddfSDavid du Colombier 		memmove(rp, bp->rp, len);
4887dd7cddfSDavid du Colombier 		memset(rp+len, 0, ETHERMINTU-len);
4897dd7cddfSDavid du Colombier 		len = ETHERMINTU;
490bd389b36SDavid du Colombier 	}
491bd389b36SDavid du Colombier 
4927dd7cddfSDavid du Colombier 	if(ctlr->ram)
4937dd7cddfSDavid du Colombier 		memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
4947dd7cddfSDavid du Colombier 	else
4957dd7cddfSDavid du Colombier 		dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
4967dd7cddfSDavid du Colombier 	freeb(bp);
4977dd7cddfSDavid du Colombier 
4987dd7cddfSDavid du Colombier 	regw(ctlr, Tbcr0, len & 0xFF);
4997dd7cddfSDavid du Colombier 	regw(ctlr, Tbcr1, (len>>8) & 0xFF);
5007dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier 	ether->outpackets++;
5037dd7cddfSDavid du Colombier 	ctlr->txbusy = 1;
5047dd7cddfSDavid du Colombier }
5057dd7cddfSDavid du Colombier 
5067dd7cddfSDavid du Colombier static void
transmit(Ether * ether)5077dd7cddfSDavid du Colombier transmit(Ether* ether)
508bd389b36SDavid du Colombier {
5097dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5127dd7cddfSDavid du Colombier 
5137dd7cddfSDavid du Colombier 	ilock(ctlr);
5147dd7cddfSDavid du Colombier 	txstart(ether);
5157dd7cddfSDavid du Colombier 	iunlock(ctlr);
5167dd7cddfSDavid du Colombier }
5177dd7cddfSDavid du Colombier 
5187dd7cddfSDavid du Colombier static void
overflow(Ether * ether)5197dd7cddfSDavid du Colombier overflow(Ether *ether)
5207dd7cddfSDavid du Colombier {
5217dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
522219b2ee8SDavid du Colombier 	uchar txp;
523219b2ee8SDavid du Colombier 	int resend;
524219b2ee8SDavid du Colombier 
5257dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5267dd7cddfSDavid du Colombier 
527219b2ee8SDavid du Colombier 	/*
528219b2ee8SDavid du Colombier 	 * The following procedure is taken from the DP8390[12D] datasheet,
529219b2ee8SDavid du Colombier 	 * it seems pretty adamant that this is what has to be done.
530219b2ee8SDavid du Colombier 	 */
5317dd7cddfSDavid du Colombier 	txp = regr(ctlr, Cr) & Txp;
5327dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Stp);
533219b2ee8SDavid du Colombier 	delay(2);
5347dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr0, 0);
5357dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr1, 0);
536219b2ee8SDavid du Colombier 
537219b2ee8SDavid du Colombier 	resend = 0;
5387dd7cddfSDavid du Colombier 	if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
539219b2ee8SDavid du Colombier 		resend = 1;
540219b2ee8SDavid du Colombier 
5417dd7cddfSDavid du Colombier 	regw(ctlr, Tcr, LpbkNIC);
5427dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Sta);
5437dd7cddfSDavid du Colombier 	receive(ether);
5447dd7cddfSDavid du Colombier 	regw(ctlr, Isr, Ovw);
5457dd7cddfSDavid du Colombier 	regw(ctlr, Tcr, LpbkNORMAL);
546219b2ee8SDavid du Colombier 
547219b2ee8SDavid du Colombier 	if(resend)
5487dd7cddfSDavid du Colombier 		regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
549219b2ee8SDavid du Colombier }
550219b2ee8SDavid du Colombier 
5517dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)5527dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
553219b2ee8SDavid du Colombier {
5547dd7cddfSDavid du Colombier 	Ether *ether;
5557dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
556219b2ee8SDavid du Colombier 	uchar isr, r;
557bd389b36SDavid du Colombier 
5587dd7cddfSDavid du Colombier 	ether = arg;
5597dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5607dd7cddfSDavid du Colombier 
561bd389b36SDavid du Colombier 	/*
562bd389b36SDavid du Colombier 	 * While there is something of interest,
563bd389b36SDavid du Colombier 	 * clear all the interrupts and process.
564bd389b36SDavid du Colombier 	 */
5657dd7cddfSDavid du Colombier 	ilock(ctlr);
5667dd7cddfSDavid du Colombier 	regw(ctlr, Imr, 0x00);
5677dd7cddfSDavid du Colombier 	while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
568bd389b36SDavid du Colombier 		if(isr & Ovw){
5697dd7cddfSDavid du Colombier 			overflow(ether);
5707dd7cddfSDavid du Colombier 			regw(ctlr, Isr, Ovw);
5717dd7cddfSDavid du Colombier 			ether->overflows++;
572bd389b36SDavid du Colombier 		}
573bd389b36SDavid du Colombier 
574bd389b36SDavid du Colombier 		/*
5757dd7cddfSDavid du Colombier 		 * Packets have been received.
5767dd7cddfSDavid du Colombier 		 * Take a spin round the ring.
577bd389b36SDavid du Colombier 		 */
578bd389b36SDavid du Colombier 		if(isr & (Rxe|Prx)){
5797dd7cddfSDavid du Colombier 			receive(ether);
5807dd7cddfSDavid du Colombier 			regw(ctlr, Isr, Rxe|Prx);
581bd389b36SDavid du Colombier 		}
582bd389b36SDavid du Colombier 
583bd389b36SDavid du Colombier 		/*
584bd389b36SDavid du Colombier 		 * A packet completed transmission, successfully or
585bd389b36SDavid du Colombier 		 * not. Start transmission on the next buffered packet,
586bd389b36SDavid du Colombier 		 * and wake the output routine.
587bd389b36SDavid du Colombier 		 */
588bd389b36SDavid du Colombier 		if(isr & (Txe|Ptx)){
5897dd7cddfSDavid du Colombier 			r = regr(ctlr, Tsr);
5907dd7cddfSDavid du Colombier 			if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
5914de34a7eSDavid du Colombier 				print("dp8390: Tsr %#2.2ux", r);
5927dd7cddfSDavid du Colombier 				ether->oerrs++;
593219b2ee8SDavid du Colombier 			}
594219b2ee8SDavid du Colombier 
5957dd7cddfSDavid du Colombier 			regw(ctlr, Isr, Txe|Ptx);
596219b2ee8SDavid du Colombier 
597219b2ee8SDavid du Colombier 			if(isr & Ptx)
5987dd7cddfSDavid du Colombier 				ether->outpackets++;
5997dd7cddfSDavid du Colombier 			ctlr->txbusy = 0;
6007dd7cddfSDavid du Colombier 			txstart(ether);
601bd389b36SDavid du Colombier 		}
602219b2ee8SDavid du Colombier 
603219b2ee8SDavid du Colombier 		if(isr & Cnt){
604e288d156SDavid du Colombier 			ether->frames += regr(ctlr, Ref0);
605e288d156SDavid du Colombier 			ether->crcs += regr(ctlr, Ref1);
606e288d156SDavid du Colombier 			ether->buffs += regr(ctlr, Ref2);
6077dd7cddfSDavid du Colombier 			regw(ctlr, Isr, Cnt);
608bd389b36SDavid du Colombier 		}
609bd389b36SDavid du Colombier 	}
6107dd7cddfSDavid du Colombier 	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
6117dd7cddfSDavid du Colombier 	iunlock(ctlr);
6127dd7cddfSDavid du Colombier }
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier static uchar allmar[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
6157dd7cddfSDavid du Colombier 
6167dd7cddfSDavid du Colombier static void
setfilter(Ether * ether,Dp8390 * ctlr)6177dd7cddfSDavid du Colombier setfilter(Ether *ether, Dp8390 *ctlr)
6187dd7cddfSDavid du Colombier {
6197dd7cddfSDavid du Colombier 	uchar r, cr;
6207dd7cddfSDavid du Colombier 	int i;
6217dd7cddfSDavid du Colombier 	uchar *mar;
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier 	r = Ab;
6247dd7cddfSDavid du Colombier 	mar = 0;
6257dd7cddfSDavid du Colombier 	if(ether->prom){
6267dd7cddfSDavid du Colombier 		r |= Pro|Am;
6277dd7cddfSDavid du Colombier 		mar = allmar;
6287dd7cddfSDavid du Colombier 	} else if(ether->nmaddr){
6297dd7cddfSDavid du Colombier 		r |= Am;
6307dd7cddfSDavid du Colombier 		mar = ctlr->mar;
6317dd7cddfSDavid du Colombier 	}
6327dd7cddfSDavid du Colombier 	if(mar){
6337dd7cddfSDavid du Colombier 		cr = regr(ctlr, Cr) & ~Txp;
6347dd7cddfSDavid du Colombier 		regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
6357dd7cddfSDavid du Colombier 		for(i = 0; i < 8; i++)
6367dd7cddfSDavid du Colombier 			regw(ctlr, Mar0+i, *(mar++));
6377dd7cddfSDavid du Colombier 		regw(ctlr, Cr, cr);
6387dd7cddfSDavid du Colombier 	}
6397dd7cddfSDavid du Colombier 	regw(ctlr, Rcr, r);
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int)6437dd7cddfSDavid du Colombier promiscuous(void *arg, int )
6447dd7cddfSDavid du Colombier {
6457dd7cddfSDavid du Colombier 	Ether *ether;
6467dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
6477dd7cddfSDavid du Colombier 
6487dd7cddfSDavid du Colombier 	ether = arg;
6497dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier 	ilock(ctlr);
6527dd7cddfSDavid du Colombier 	setfilter(ether, ctlr);
6537dd7cddfSDavid du Colombier 	iunlock(ctlr);
6547dd7cddfSDavid du Colombier }
6557dd7cddfSDavid du Colombier 
6567dd7cddfSDavid du Colombier static void
setbit(Dp8390 * ctlr,int bit,int on)6577dd7cddfSDavid du Colombier setbit(Dp8390 *ctlr, int bit, int on)
6587dd7cddfSDavid du Colombier {
6597dd7cddfSDavid du Colombier 	int i, h;
6607dd7cddfSDavid du Colombier 
6617dd7cddfSDavid du Colombier 	i = bit/8;
6627dd7cddfSDavid du Colombier 	h = bit%8;
6637dd7cddfSDavid du Colombier 	if(on){
6647dd7cddfSDavid du Colombier 		if(++(ctlr->mref[bit]) == 1)
6657dd7cddfSDavid du Colombier 			ctlr->mar[i] |= 1<<h;
6667dd7cddfSDavid du Colombier 	} else {
6677dd7cddfSDavid du Colombier 		if(--(ctlr->mref[bit]) <= 0){
6687dd7cddfSDavid du Colombier 			ctlr->mref[bit] = 0;
6697dd7cddfSDavid du Colombier 			ctlr->mar[i] &= ~(1<<h);
6707dd7cddfSDavid du Colombier 		}
6717dd7cddfSDavid du Colombier 	}
6727dd7cddfSDavid du Colombier }
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier static uchar reverse[64];
6757dd7cddfSDavid du Colombier 
6767dd7cddfSDavid du Colombier static void
multicast(void * arg,uchar * addr,int on)6777dd7cddfSDavid du Colombier multicast(void* arg, uchar *addr, int on)
6787dd7cddfSDavid du Colombier {
6797dd7cddfSDavid du Colombier 	Ether *ether;
6807dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
6817dd7cddfSDavid du Colombier 	int i;
6827dd7cddfSDavid du Colombier 	ulong h;
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	ether = arg;
6857dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6867dd7cddfSDavid du Colombier 	if(reverse[1] == 0){
6877dd7cddfSDavid du Colombier 		for(i = 0; i < 64; i++)
6887dd7cddfSDavid du Colombier 			reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1)
6897dd7cddfSDavid du Colombier 				   | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5);
6907dd7cddfSDavid du Colombier 	}
6917dd7cddfSDavid du Colombier 
6927dd7cddfSDavid du Colombier 	/*
6937dd7cddfSDavid du Colombier 	 *  change filter bits
6947dd7cddfSDavid du Colombier 	 */
6957dd7cddfSDavid du Colombier 	h = ethercrc(addr, 6);
6967dd7cddfSDavid du Colombier 	ilock(ctlr);
6977dd7cddfSDavid du Colombier 	setbit(ctlr, reverse[h&0x3f], on);
6987dd7cddfSDavid du Colombier 	setfilter(ether, ctlr);
6997dd7cddfSDavid du Colombier 	iunlock(ctlr);
7007dd7cddfSDavid du Colombier }
7017dd7cddfSDavid du Colombier 
7027dd7cddfSDavid du Colombier static void
attach(Ether * ether)7037dd7cddfSDavid du Colombier attach(Ether* ether)
7047dd7cddfSDavid du Colombier {
7057dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
7067dd7cddfSDavid du Colombier 	uchar r;
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier 	/*
7117dd7cddfSDavid du Colombier 	 * Enable the chip for transmit/receive.
7127dd7cddfSDavid du Colombier 	 * The init routine leaves the chip in monitor
7137dd7cddfSDavid du Colombier 	 * mode. Clear the missed-packet counter, it
7147dd7cddfSDavid du Colombier 	 * increments while in monitor mode.
7157dd7cddfSDavid du Colombier 	 * Sometimes there's an interrupt pending at this
7167dd7cddfSDavid du Colombier 	 * point but there's nothing in the Isr, so
7177dd7cddfSDavid du Colombier 	 * any pending interrupts are cleared and the
7187dd7cddfSDavid du Colombier 	 * mask of acceptable interrupts is enabled here.
7197dd7cddfSDavid du Colombier 	 */
7207dd7cddfSDavid du Colombier 	r = Ab;
7217dd7cddfSDavid du Colombier 	if(ether->prom)
7227dd7cddfSDavid du Colombier 		r |= Pro;
7237dd7cddfSDavid du Colombier 	if(ether->nmaddr)
7247dd7cddfSDavid du Colombier 		r |= Am;
7257dd7cddfSDavid du Colombier 	ilock(ctlr);
7267dd7cddfSDavid du Colombier 	regw(ctlr, Isr, 0xFF);
7277dd7cddfSDavid du Colombier 	regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
7287dd7cddfSDavid du Colombier 	regw(ctlr, Rcr, r);
729e288d156SDavid du Colombier 	r = regr(ctlr, Ref2);
7307dd7cddfSDavid du Colombier 	regw(ctlr, Tcr, LpbkNORMAL);
7317dd7cddfSDavid du Colombier 	iunlock(ctlr);
7327dd7cddfSDavid du Colombier 	USED(r);
7337dd7cddfSDavid du Colombier }
7347dd7cddfSDavid du Colombier 
7357dd7cddfSDavid du Colombier static void
disable(Dp8390 * ctlr)7367dd7cddfSDavid du Colombier disable(Dp8390* ctlr)
7377dd7cddfSDavid du Colombier {
7387dd7cddfSDavid du Colombier 	int timo;
7397dd7cddfSDavid du Colombier 
7407dd7cddfSDavid du Colombier 	/*
7417dd7cddfSDavid du Colombier 	 * Stop the chip. Set the Stp bit and wait for the chip
7427dd7cddfSDavid du Colombier 	 * to finish whatever was on its tiny mind before it sets
7437dd7cddfSDavid du Colombier 	 * the Rst bit.
7447dd7cddfSDavid du Colombier 	 * The timeout is needed because there may not be a real
7457dd7cddfSDavid du Colombier 	 * chip there if this is called when probing for a device
7467dd7cddfSDavid du Colombier 	 * at boot.
7477dd7cddfSDavid du Colombier 	 */
7487dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Stp);
7497dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr0, 0);
7507dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr1, 0);
7517dd7cddfSDavid du Colombier 	for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
7527dd7cddfSDavid du Colombier 			;
7537dd7cddfSDavid du Colombier }
7547dd7cddfSDavid du Colombier 
755*ca045b34SDavid du Colombier static void
shutdown(Ether * ether)756*ca045b34SDavid du Colombier shutdown(Ether *ether)
757*ca045b34SDavid du Colombier {
758*ca045b34SDavid du Colombier 	Dp8390 *ctlr;
759*ca045b34SDavid du Colombier 
760*ca045b34SDavid du Colombier 	ctlr = ether->ctlr;
761*ca045b34SDavid du Colombier 	disable(ctlr);
762*ca045b34SDavid du Colombier }
763*ca045b34SDavid du Colombier 
7647dd7cddfSDavid du Colombier int
dp8390reset(Ether * ether)7657dd7cddfSDavid du Colombier dp8390reset(Ether* ether)
7667dd7cddfSDavid du Colombier {
7677dd7cddfSDavid du Colombier 	Dp8390 *ctlr;
7687dd7cddfSDavid du Colombier 
7697dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
7707dd7cddfSDavid du Colombier 
7717dd7cddfSDavid du Colombier 	/*
7727dd7cddfSDavid du Colombier 	 * This is the initialisation procedure described
7737dd7cddfSDavid du Colombier 	 * as 'mandatory' in the datasheet, with references
7747dd7cddfSDavid du Colombier 	 * to the 3C503 technical reference manual.
7757dd7cddfSDavid du Colombier 	 */
7767dd7cddfSDavid du Colombier 	disable(ctlr);
7777dd7cddfSDavid du Colombier 	if(ctlr->width != 1)
7787dd7cddfSDavid du Colombier 		regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
7797dd7cddfSDavid du Colombier 	else
7807dd7cddfSDavid du Colombier 		regw(ctlr, Dcr, Ft4WORD|Ls);
7817dd7cddfSDavid du Colombier 
7827dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr0, 0);
7837dd7cddfSDavid du Colombier 	regw(ctlr, Rbcr1, 0);
7847dd7cddfSDavid du Colombier 
7857dd7cddfSDavid du Colombier 	regw(ctlr, Tcr, LpbkNIC);
7867dd7cddfSDavid du Colombier 	regw(ctlr, Rcr, Mon);
7877dd7cddfSDavid du Colombier 
7887dd7cddfSDavid du Colombier 	/*
7897dd7cddfSDavid du Colombier 	 * Init the ring hardware and software ring pointers.
7907dd7cddfSDavid du Colombier 	 * Can't initialise ethernet address as it may not be
7917dd7cddfSDavid du Colombier 	 * known yet.
7927dd7cddfSDavid du Colombier 	 */
7937dd7cddfSDavid du Colombier 	ringinit(ctlr);
7947dd7cddfSDavid du Colombier 	regw(ctlr, Tpsr, ctlr->tstart);
7957dd7cddfSDavid du Colombier 
7967dd7cddfSDavid du Colombier 	/*
7977dd7cddfSDavid du Colombier 	 * Clear any pending interrupts and mask then all off.
7987dd7cddfSDavid du Colombier 	 */
7997dd7cddfSDavid du Colombier 	regw(ctlr, Isr, 0xFF);
8007dd7cddfSDavid du Colombier 	regw(ctlr, Imr, 0);
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	/*
8037dd7cddfSDavid du Colombier 	 * Leave the chip initialised,
8047dd7cddfSDavid du Colombier 	 * but in monitor mode.
8057dd7cddfSDavid du Colombier 	 */
8067dd7cddfSDavid du Colombier 	regw(ctlr, Cr, Page0|RdABORT|Sta);
8077dd7cddfSDavid du Colombier 
8087dd7cddfSDavid du Colombier 	/*
8097dd7cddfSDavid du Colombier 	 * Set up the software configuration.
8107dd7cddfSDavid du Colombier 	 */
8117dd7cddfSDavid du Colombier 	ether->attach = attach;
8127dd7cddfSDavid du Colombier 	ether->transmit = transmit;
8137dd7cddfSDavid du Colombier 	ether->interrupt = interrupt;
814*ca045b34SDavid du Colombier 	ether->shutdown = shutdown;
8157dd7cddfSDavid du Colombier 	ether->ifstat = 0;
8167dd7cddfSDavid du Colombier 
8177dd7cddfSDavid du Colombier 	ether->promiscuous = promiscuous;
8187dd7cddfSDavid du Colombier 	ether->multicast = multicast;
8197dd7cddfSDavid du Colombier 	ether->arg = ether;
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 	return 0;
822219b2ee8SDavid du Colombier }
823