xref: /minix3/sys/arch/i386/stand/lib/netif/am7990.c (revision 58a2b0008e28f606a7f7f5faaeaba4faac57a1ea)
1*58a2b000SEvgeniy Ivanov /*	$NetBSD: am7990.c,v 1.7 2008/12/14 18:46:33 christos Exp $	*/
2*58a2b000SEvgeniy Ivanov 
3*58a2b000SEvgeniy Ivanov /* mostly from netbsd:sys/arch/i386/netboot/ne2100.c
4*58a2b000SEvgeniy Ivanov  memory allocation now 1 chunk, added deallocation
5*58a2b000SEvgeniy Ivanov  receive function changed - don't use irq
6*58a2b000SEvgeniy Ivanov  */
7*58a2b000SEvgeniy Ivanov 
8*58a2b000SEvgeniy Ivanov /*
9*58a2b000SEvgeniy Ivanov  * source in this file came from
10*58a2b000SEvgeniy Ivanov  * the Mach ethernet boot written by Leendert van Doorn.
11*58a2b000SEvgeniy Ivanov  *
12*58a2b000SEvgeniy Ivanov  * A very simple network driver for NE2100 boards that polls.
13*58a2b000SEvgeniy Ivanov  *
14*58a2b000SEvgeniy Ivanov  * Copyright (c) 1992 by Leendert van Doorn
15*58a2b000SEvgeniy Ivanov  */
16*58a2b000SEvgeniy Ivanov 
17*58a2b000SEvgeniy Ivanov #include <sys/types.h>
18*58a2b000SEvgeniy Ivanov #include <machine/pio.h>
19*58a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
20*58a2b000SEvgeniy Ivanov #include <lib/libsa/stand.h>
21*58a2b000SEvgeniy Ivanov 
22*58a2b000SEvgeniy Ivanov #include <libi386.h>
23*58a2b000SEvgeniy Ivanov 
24*58a2b000SEvgeniy Ivanov #include "etherdrv.h"
25*58a2b000SEvgeniy Ivanov #include "lance.h"
26*58a2b000SEvgeniy Ivanov 
27*58a2b000SEvgeniy Ivanov extern u_char eth_myaddr[6];
28*58a2b000SEvgeniy Ivanov 
29*58a2b000SEvgeniy Ivanov extern int lance_rap, lance_rdp;
30*58a2b000SEvgeniy Ivanov 
31*58a2b000SEvgeniy Ivanov static void *dmamem;
32*58a2b000SEvgeniy Ivanov 
33*58a2b000SEvgeniy Ivanov #define LA(adr) vtophys(adr)
34*58a2b000SEvgeniy Ivanov 
35*58a2b000SEvgeniy Ivanov /* Lance register offsets */
36*58a2b000SEvgeniy Ivanov #define LA_CSR          lance_rdp
37*58a2b000SEvgeniy Ivanov #define LA_CSR1         lance_rdp
38*58a2b000SEvgeniy Ivanov #define LA_CSR2         lance_rdp
39*58a2b000SEvgeniy Ivanov #define LA_CSR3         lance_rdp
40*58a2b000SEvgeniy Ivanov #define LA_RAP          lance_rap
41*58a2b000SEvgeniy Ivanov 
42*58a2b000SEvgeniy Ivanov /*
43*58a2b000SEvgeniy Ivanov  * Some driver specific constants.
44*58a2b000SEvgeniy Ivanov  * Take care when tuning, this program only has 32 Kb
45*58a2b000SEvgeniy Ivanov  */
46*58a2b000SEvgeniy Ivanov #define	LANCEBUFSIZE	1518		/* plus 4 CRC bytes */
47*58a2b000SEvgeniy Ivanov #define	MAXLOOP		1000000L	/* arbitrary retry limit */
48*58a2b000SEvgeniy Ivanov #define	LOG2NRCVRING	2		/* log2(NRCVRING) */
49*58a2b000SEvgeniy Ivanov #define	NRCVRING	(1 << LOG2NRCVRING)
50*58a2b000SEvgeniy Ivanov 
51*58a2b000SEvgeniy Ivanov static int next_rmd;			/* next receive element */
52*58a2b000SEvgeniy Ivanov static initblock_t *initblock;		/* initialization block */
53*58a2b000SEvgeniy Ivanov static tmde_t *tmd;			/* transmit ring */
54*58a2b000SEvgeniy Ivanov static rmde_t *rmd;			/* receive ring */
55*58a2b000SEvgeniy Ivanov static char rbuffer[NRCVRING][LANCEBUFSIZE]; /* receive buffers */
56*58a2b000SEvgeniy Ivanov 
57*58a2b000SEvgeniy Ivanov /*
58*58a2b000SEvgeniy Ivanov  * Stop ethernet board
59*58a2b000SEvgeniy Ivanov  */
60*58a2b000SEvgeniy Ivanov void
am7990_stop(void)61*58a2b000SEvgeniy Ivanov am7990_stop(void)
62*58a2b000SEvgeniy Ivanov {
63*58a2b000SEvgeniy Ivanov 	long l;
64*58a2b000SEvgeniy Ivanov 
65*58a2b000SEvgeniy Ivanov 	/* stop chip and disable DMA access */
66*58a2b000SEvgeniy Ivanov 	outw(LA_RAP, RDP_CSR0);
67*58a2b000SEvgeniy Ivanov 	outw(LA_CSR, CSR_STOP);
68*58a2b000SEvgeniy Ivanov 	for (l = 0; (inw(LA_CSR) & CSR_STOP) == 0; l++) {
69*58a2b000SEvgeniy Ivanov 		if (l >= MAXLOOP) {
70*58a2b000SEvgeniy Ivanov 			printf("Lance failed to stop\n");
71*58a2b000SEvgeniy Ivanov 			return;
72*58a2b000SEvgeniy Ivanov 		}
73*58a2b000SEvgeniy Ivanov 	}
74*58a2b000SEvgeniy Ivanov }
75*58a2b000SEvgeniy Ivanov 
76*58a2b000SEvgeniy Ivanov /*
77*58a2b000SEvgeniy Ivanov  * Reset ethernet board
78*58a2b000SEvgeniy Ivanov  */
79*58a2b000SEvgeniy Ivanov void
am7990_init(void)80*58a2b000SEvgeniy Ivanov am7990_init(void)
81*58a2b000SEvgeniy Ivanov {
82*58a2b000SEvgeniy Ivanov 	long l;
83*58a2b000SEvgeniy Ivanov 	u_long addr;
84*58a2b000SEvgeniy Ivanov 	int i;
85*58a2b000SEvgeniy Ivanov 
86*58a2b000SEvgeniy Ivanov 	/* initblock, tmd, and rmd should be 8 byte aligned;
87*58a2b000SEvgeniy Ivanov 	   sizes of initblock_t and tmde_t are multiples of 8 */
88*58a2b000SEvgeniy Ivanov 	dmamem = alloc(sizeof(initblock_t) +
89*58a2b000SEvgeniy Ivanov 	    sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
90*58a2b000SEvgeniy Ivanov 	/* +4 is ok because alloc()'s result is 4-byte aligned! */
91*58a2b000SEvgeniy Ivanov 
92*58a2b000SEvgeniy Ivanov 	initblock = (initblock_t *)(((unsigned long)dmamem + 4) & -8);
93*58a2b000SEvgeniy Ivanov 	tmd = (tmde_t *)(initblock + 1);
94*58a2b000SEvgeniy Ivanov 	rmd = (rmde_t *)(tmd + 1);
95*58a2b000SEvgeniy Ivanov 
96*58a2b000SEvgeniy Ivanov 	/* stop the chip, and make sure it did */
97*58a2b000SEvgeniy Ivanov 	am7990_stop();
98*58a2b000SEvgeniy Ivanov 
99*58a2b000SEvgeniy Ivanov 	/* fill lance initialization block */
100*58a2b000SEvgeniy Ivanov 	memset(initblock, 0, sizeof(initblock_t));
101*58a2b000SEvgeniy Ivanov 
102*58a2b000SEvgeniy Ivanov 	/* set my ethernet address */
103*58a2b000SEvgeniy Ivanov 	for (i = 0; i < 6; i++)
104*58a2b000SEvgeniy Ivanov 		initblock->ib_padr[i] = eth_myaddr[i];
105*58a2b000SEvgeniy Ivanov 
106*58a2b000SEvgeniy Ivanov 	/* receive ring pointer */
107*58a2b000SEvgeniy Ivanov 	addr = LA(rmd);
108*58a2b000SEvgeniy Ivanov 	initblock->ib_rdralow = (u_short)addr;
109*58a2b000SEvgeniy Ivanov 	initblock->ib_rdrahigh = (u_char)(addr >> 16);
110*58a2b000SEvgeniy Ivanov 	initblock->ib_rlen = LOG2NRCVRING << 5;
111*58a2b000SEvgeniy Ivanov 
112*58a2b000SEvgeniy Ivanov 	/* transmit ring with one element */
113*58a2b000SEvgeniy Ivanov 	addr = LA(tmd);
114*58a2b000SEvgeniy Ivanov 	initblock->ib_tdralow = (u_short)addr;
115*58a2b000SEvgeniy Ivanov 	initblock->ib_tdrahigh = (u_char)(addr >> 16);
116*58a2b000SEvgeniy Ivanov 	initblock->ib_tlen = 0 << 5;
117*58a2b000SEvgeniy Ivanov 
118*58a2b000SEvgeniy Ivanov 	/* setup the receive ring entries */
119*58a2b000SEvgeniy Ivanov 	for (next_rmd = 0, i = 0; i < NRCVRING; i++) {
120*58a2b000SEvgeniy Ivanov 		addr = LA(&rbuffer[i]);
121*58a2b000SEvgeniy Ivanov 		rmd[i].rmd_ladr = (u_short)addr;
122*58a2b000SEvgeniy Ivanov 		rmd[i].rmd_hadr = (u_char)(addr >> 16);
123*58a2b000SEvgeniy Ivanov 		rmd[i].rmd_mcnt = 0;
124*58a2b000SEvgeniy Ivanov 		rmd[i].rmd_bcnt = -LANCEBUFSIZE;
125*58a2b000SEvgeniy Ivanov 		rmd[i].rmd_flags = RMD_OWN;
126*58a2b000SEvgeniy Ivanov 	}
127*58a2b000SEvgeniy Ivanov 
128*58a2b000SEvgeniy Ivanov 	/* zero transmit ring */
129*58a2b000SEvgeniy Ivanov 	memset(tmd, 0, sizeof(tmde_t));
130*58a2b000SEvgeniy Ivanov 
131*58a2b000SEvgeniy Ivanov 	/* give lance the init block */
132*58a2b000SEvgeniy Ivanov 	addr = LA(initblock);
133*58a2b000SEvgeniy Ivanov 	outw(LA_RAP, RDP_CSR1);
134*58a2b000SEvgeniy Ivanov 	outw(LA_CSR1, (u_short)addr);
135*58a2b000SEvgeniy Ivanov 	outw(LA_RAP, RDP_CSR2);
136*58a2b000SEvgeniy Ivanov 	outw(LA_CSR2, (char)(addr >> 16));
137*58a2b000SEvgeniy Ivanov 	outw(LA_RAP, RDP_CSR3);
138*58a2b000SEvgeniy Ivanov 	outw(LA_CSR3, 0);
139*58a2b000SEvgeniy Ivanov 
140*58a2b000SEvgeniy Ivanov 	/* and initialize it */
141*58a2b000SEvgeniy Ivanov 	outw(LA_RAP, RDP_CSR0);
142*58a2b000SEvgeniy Ivanov 	outw(LA_CSR, CSR_INIT|CSR_STRT);
143*58a2b000SEvgeniy Ivanov 
144*58a2b000SEvgeniy Ivanov 	/* wait for the lance to complete initialization and fire it up */
145*58a2b000SEvgeniy Ivanov 	for (l = 0; (inw(LA_CSR) & CSR_IDON) == 0; l++) {
146*58a2b000SEvgeniy Ivanov 		if (l >= MAXLOOP) {
147*58a2b000SEvgeniy Ivanov 			printf("Lance failed to initialize\n");
148*58a2b000SEvgeniy Ivanov 			break;
149*58a2b000SEvgeniy Ivanov 		}
150*58a2b000SEvgeniy Ivanov 	}
151*58a2b000SEvgeniy Ivanov 	for (l = 0; (inw(LA_CSR)&(CSR_TXON|CSR_RXON)) != (CSR_TXON|CSR_RXON); l++) {
152*58a2b000SEvgeniy Ivanov 		if (l >= MAXLOOP) {
153*58a2b000SEvgeniy Ivanov 			printf("Lance not started\n");
154*58a2b000SEvgeniy Ivanov 			break;
155*58a2b000SEvgeniy Ivanov 		}
156*58a2b000SEvgeniy Ivanov 	}
157*58a2b000SEvgeniy Ivanov }
158*58a2b000SEvgeniy Ivanov 
159*58a2b000SEvgeniy Ivanov /*
160*58a2b000SEvgeniy Ivanov  * Stop ethernet board and free ressources
161*58a2b000SEvgeniy Ivanov  */
162*58a2b000SEvgeniy Ivanov void
EtherStop(void)163*58a2b000SEvgeniy Ivanov EtherStop(void)
164*58a2b000SEvgeniy Ivanov {
165*58a2b000SEvgeniy Ivanov 	am7990_stop();
166*58a2b000SEvgeniy Ivanov 
167*58a2b000SEvgeniy Ivanov 	dealloc(dmamem, sizeof(initblock_t) +
168*58a2b000SEvgeniy Ivanov 	    sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
169*58a2b000SEvgeniy Ivanov }
170*58a2b000SEvgeniy Ivanov 
171*58a2b000SEvgeniy Ivanov /*
172*58a2b000SEvgeniy Ivanov  * Send an ethernet packet
173*58a2b000SEvgeniy Ivanov  */
174*58a2b000SEvgeniy Ivanov int
EtherSend(char * pkt,int len)175*58a2b000SEvgeniy Ivanov EtherSend(char *pkt, int len)
176*58a2b000SEvgeniy Ivanov {
177*58a2b000SEvgeniy Ivanov 	long l;
178*58a2b000SEvgeniy Ivanov 	u_long addr;
179*58a2b000SEvgeniy Ivanov 	u_short csr;
180*58a2b000SEvgeniy Ivanov 	int savlen = len;
181*58a2b000SEvgeniy Ivanov 
182*58a2b000SEvgeniy Ivanov 	if (len < 60)
183*58a2b000SEvgeniy Ivanov 		len = 60;
184*58a2b000SEvgeniy Ivanov 	if (len > LANCEBUFSIZE) {
185*58a2b000SEvgeniy Ivanov 		printf("packet too long\n");
186*58a2b000SEvgeniy Ivanov 		return -1;
187*58a2b000SEvgeniy Ivanov 	}
188*58a2b000SEvgeniy Ivanov 
189*58a2b000SEvgeniy Ivanov 	/* set up transmit ring element */
190*58a2b000SEvgeniy Ivanov 	if (tmd->tmd_flags & TMD_OWN) {
191*58a2b000SEvgeniy Ivanov 		printf("lesend: td busy, status=%x\n", tmd->tmd_flags);
192*58a2b000SEvgeniy Ivanov 		return -1;
193*58a2b000SEvgeniy Ivanov 	}
194*58a2b000SEvgeniy Ivanov 	addr = LA(pkt);
195*58a2b000SEvgeniy Ivanov 	if (addr & 1) {
196*58a2b000SEvgeniy Ivanov 		printf("unaligned data\n");
197*58a2b000SEvgeniy Ivanov 		return -1;
198*58a2b000SEvgeniy Ivanov 	}
199*58a2b000SEvgeniy Ivanov 	tmd->tmd_ladr = (u_short)addr;
200*58a2b000SEvgeniy Ivanov 	tmd->tmd_hadr = (u_char)(addr >> 16);
201*58a2b000SEvgeniy Ivanov 	tmd->tmd_bcnt = -len;
202*58a2b000SEvgeniy Ivanov 	tmd->tmd_err = 0;
203*58a2b000SEvgeniy Ivanov 	tmd->tmd_flags = TMD_OWN|TMD_STP|TMD_ENP;
204*58a2b000SEvgeniy Ivanov 
205*58a2b000SEvgeniy Ivanov 	/* start transmission */
206*58a2b000SEvgeniy Ivanov 	outw(LA_CSR, CSR_TDMD);
207*58a2b000SEvgeniy Ivanov 
208*58a2b000SEvgeniy Ivanov 	/* wait for interrupt and acknowledge it */
209*58a2b000SEvgeniy Ivanov 	for (l = 0; l < MAXLOOP; l++) {
210*58a2b000SEvgeniy Ivanov 		if ((csr = inw(LA_CSR)) & CSR_TINT) {
211*58a2b000SEvgeniy Ivanov 			outw(LA_CSR, CSR_TINT);
212*58a2b000SEvgeniy Ivanov #ifdef LEDEBUG
213*58a2b000SEvgeniy Ivanov 			if (tmd->tmd_flags & (TMD_ONE|TMD_MORE|TMD_ERR|TMD_DEF))
214*58a2b000SEvgeniy Ivanov 				printf("lesend: status=%x\n", tmd->tmd_flags);
215*58a2b000SEvgeniy Ivanov #endif
216*58a2b000SEvgeniy Ivanov 			break;
217*58a2b000SEvgeniy Ivanov 		}
218*58a2b000SEvgeniy Ivanov 		delay(10); /* don't poll too much on PCI, seems
219*58a2b000SEvgeniy Ivanov 			      to disturb DMA on poor hardware */
220*58a2b000SEvgeniy Ivanov 	}
221*58a2b000SEvgeniy Ivanov 	return savlen;
222*58a2b000SEvgeniy Ivanov }
223*58a2b000SEvgeniy Ivanov 
224*58a2b000SEvgeniy Ivanov /*
225*58a2b000SEvgeniy Ivanov  * Poll the LANCE just see if there's an Ethernet packet
226*58a2b000SEvgeniy Ivanov  * available. If there is, its contents is returned.
227*58a2b000SEvgeniy Ivanov  */
228*58a2b000SEvgeniy Ivanov int
EtherReceive(char * pkt,int maxlen)229*58a2b000SEvgeniy Ivanov EtherReceive(char *pkt, int maxlen)
230*58a2b000SEvgeniy Ivanov {
231*58a2b000SEvgeniy Ivanov 	rmde_t *rp;
232*58a2b000SEvgeniy Ivanov 	u_short csr;
233*58a2b000SEvgeniy Ivanov 	int len = 0;
234*58a2b000SEvgeniy Ivanov 
235*58a2b000SEvgeniy Ivanov 	csr = inw(LA_CSR);
236*58a2b000SEvgeniy Ivanov 	outw(LA_CSR, csr & (CSR_BABL | CSR_MISS | CSR_MERR | CSR_RINT));
237*58a2b000SEvgeniy Ivanov 
238*58a2b000SEvgeniy Ivanov 	if ((next_rmd < 0) || (next_rmd >= NRCVRING)) {
239*58a2b000SEvgeniy Ivanov 		printf("next_rmd bad\n");
240*58a2b000SEvgeniy Ivanov 		return 0;
241*58a2b000SEvgeniy Ivanov 	}
242*58a2b000SEvgeniy Ivanov 	rp = &rmd[next_rmd];
243*58a2b000SEvgeniy Ivanov 
244*58a2b000SEvgeniy Ivanov 	if (rp->rmd_flags & RMD_OWN)
245*58a2b000SEvgeniy Ivanov 		return 0;
246*58a2b000SEvgeniy Ivanov 
247*58a2b000SEvgeniy Ivanov 	if (csr & (CSR_BABL | CSR_CERR | CSR_MISS | CSR_MERR))
248*58a2b000SEvgeniy Ivanov 		printf("le: csr %x\n", csr);
249*58a2b000SEvgeniy Ivanov 
250*58a2b000SEvgeniy Ivanov 	if (rp->rmd_flags & (RMD_FRAM | RMD_OFLO | RMD_CRC | RMD_BUFF)) {
251*58a2b000SEvgeniy Ivanov 		printf("le: rmd_flags %x\n", rp->rmd_flags);
252*58a2b000SEvgeniy Ivanov 		goto cleanup;
253*58a2b000SEvgeniy Ivanov 	}
254*58a2b000SEvgeniy Ivanov 
255*58a2b000SEvgeniy Ivanov 	if (rp->rmd_flags != (RMD_STP|RMD_ENP)) {
256*58a2b000SEvgeniy Ivanov 		printf("le: rmd_flags %x\n", rp->rmd_flags);
257*58a2b000SEvgeniy Ivanov 		return -1;
258*58a2b000SEvgeniy Ivanov 	}
259*58a2b000SEvgeniy Ivanov 
260*58a2b000SEvgeniy Ivanov 	len = rp->rmd_mcnt - 4;
261*58a2b000SEvgeniy Ivanov 
262*58a2b000SEvgeniy Ivanov 	if ((len < 0) || (len >= LANCEBUFSIZE)) {
263*58a2b000SEvgeniy Ivanov 		printf("bad pkt len\n");
264*58a2b000SEvgeniy Ivanov 		return -1;
265*58a2b000SEvgeniy Ivanov 	}
266*58a2b000SEvgeniy Ivanov 
267*58a2b000SEvgeniy Ivanov 	if (len <= maxlen)
268*58a2b000SEvgeniy Ivanov 		memcpy(pkt, rbuffer[next_rmd], len);
269*58a2b000SEvgeniy Ivanov 	else
270*58a2b000SEvgeniy Ivanov 		len = 0;
271*58a2b000SEvgeniy Ivanov 
272*58a2b000SEvgeniy Ivanov  cleanup:
273*58a2b000SEvgeniy Ivanov 	/* give packet back to the lance */
274*58a2b000SEvgeniy Ivanov 	rp->rmd_bcnt = -LANCEBUFSIZE;
275*58a2b000SEvgeniy Ivanov 	rp->rmd_mcnt = 0;
276*58a2b000SEvgeniy Ivanov 	rp->rmd_flags = RMD_OWN;
277*58a2b000SEvgeniy Ivanov 	next_rmd = (next_rmd + 1) & (NRCVRING - 1);
278*58a2b000SEvgeniy Ivanov 
279*58a2b000SEvgeniy Ivanov 	return len;
280*58a2b000SEvgeniy Ivanov }
281