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