unstack - fix ipcvecs
[minix.git] / sys / arch / i386 / stand / lib / netif / am7990.c
blob9d9eea88c7d1588ba864aaddfbdfd775e7c7f28a
1 /* $NetBSD: am7990.c,v 1.7 2008/12/14 18:46:33 christos Exp $ */
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 */
8 /*
9 * source in this file came from
10 * the Mach ethernet boot written by Leendert van Doorn.
12 * A very simple network driver for NE2100 boards that polls.
14 * Copyright (c) 1992 by Leendert van Doorn
17 #include <sys/types.h>
18 #include <machine/pio.h>
19 #include <lib/libkern/libkern.h>
20 #include <lib/libsa/stand.h>
22 #include <libi386.h>
24 #include "etherdrv.h"
25 #include "lance.h"
27 extern u_char eth_myaddr[6];
29 extern int lance_rap, lance_rdp;
31 static void *dmamem;
33 #define LA(adr) vtophys(adr)
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
43 * Some driver specific constants.
44 * Take care when tuning, this program only has 32 Kb
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)
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 */
58 * Stop ethernet board
60 void
61 am7990_stop(void)
63 long l;
65 /* stop chip and disable DMA access */
66 outw(LA_RAP, RDP_CSR0);
67 outw(LA_CSR, CSR_STOP);
68 for (l = 0; (inw(LA_CSR) & CSR_STOP) == 0; l++) {
69 if (l >= MAXLOOP) {
70 printf("Lance failed to stop\n");
71 return;
77 * Reset ethernet board
79 void
80 am7990_init(void)
82 long l;
83 u_long addr;
84 int i;
86 /* initblock, tmd, and rmd should be 8 byte aligned;
87 sizes of initblock_t and tmde_t are multiples of 8 */
88 dmamem = alloc(sizeof(initblock_t) +
89 sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
90 /* +4 is ok because alloc()'s result is 4-byte aligned! */
92 initblock = (initblock_t *)(((unsigned long)dmamem + 4) & -8);
93 tmd = (tmde_t *)(initblock + 1);
94 rmd = (rmde_t *)(tmd + 1);
96 /* stop the chip, and make sure it did */
97 am7990_stop();
99 /* fill lance initialization block */
100 memset(initblock, 0, sizeof(initblock_t));
102 /* set my ethernet address */
103 for (i = 0; i < 6; i++)
104 initblock->ib_padr[i] = eth_myaddr[i];
106 /* receive ring pointer */
107 addr = LA(rmd);
108 initblock->ib_rdralow = (u_short)addr;
109 initblock->ib_rdrahigh = (u_char)(addr >> 16);
110 initblock->ib_rlen = LOG2NRCVRING << 5;
112 /* transmit ring with one element */
113 addr = LA(tmd);
114 initblock->ib_tdralow = (u_short)addr;
115 initblock->ib_tdrahigh = (u_char)(addr >> 16);
116 initblock->ib_tlen = 0 << 5;
118 /* setup the receive ring entries */
119 for (next_rmd = 0, i = 0; i < NRCVRING; i++) {
120 addr = LA(&rbuffer[i]);
121 rmd[i].rmd_ladr = (u_short)addr;
122 rmd[i].rmd_hadr = (u_char)(addr >> 16);
123 rmd[i].rmd_mcnt = 0;
124 rmd[i].rmd_bcnt = -LANCEBUFSIZE;
125 rmd[i].rmd_flags = RMD_OWN;
128 /* zero transmit ring */
129 memset(tmd, 0, sizeof(tmde_t));
131 /* give lance the init block */
132 addr = LA(initblock);
133 outw(LA_RAP, RDP_CSR1);
134 outw(LA_CSR1, (u_short)addr);
135 outw(LA_RAP, RDP_CSR2);
136 outw(LA_CSR2, (char)(addr >> 16));
137 outw(LA_RAP, RDP_CSR3);
138 outw(LA_CSR3, 0);
140 /* and initialize it */
141 outw(LA_RAP, RDP_CSR0);
142 outw(LA_CSR, CSR_INIT|CSR_STRT);
144 /* wait for the lance to complete initialization and fire it up */
145 for (l = 0; (inw(LA_CSR) & CSR_IDON) == 0; l++) {
146 if (l >= MAXLOOP) {
147 printf("Lance failed to initialize\n");
148 break;
151 for (l = 0; (inw(LA_CSR)&(CSR_TXON|CSR_RXON)) != (CSR_TXON|CSR_RXON); l++) {
152 if (l >= MAXLOOP) {
153 printf("Lance not started\n");
154 break;
160 * Stop ethernet board and free ressources
162 void
163 EtherStop(void)
165 am7990_stop();
167 dealloc(dmamem, sizeof(initblock_t) +
168 sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
172 * Send an ethernet packet
175 EtherSend(char *pkt, int len)
177 long l;
178 u_long addr;
179 u_short csr;
180 int savlen = len;
182 if (len < 60)
183 len = 60;
184 if (len > LANCEBUFSIZE) {
185 printf("packet too long\n");
186 return -1;
189 /* set up transmit ring element */
190 if (tmd->tmd_flags & TMD_OWN) {
191 printf("lesend: td busy, status=%x\n", tmd->tmd_flags);
192 return -1;
194 addr = LA(pkt);
195 if (addr & 1) {
196 printf("unaligned data\n");
197 return -1;
199 tmd->tmd_ladr = (u_short)addr;
200 tmd->tmd_hadr = (u_char)(addr >> 16);
201 tmd->tmd_bcnt = -len;
202 tmd->tmd_err = 0;
203 tmd->tmd_flags = TMD_OWN|TMD_STP|TMD_ENP;
205 /* start transmission */
206 outw(LA_CSR, CSR_TDMD);
208 /* wait for interrupt and acknowledge it */
209 for (l = 0; l < MAXLOOP; l++) {
210 if ((csr = inw(LA_CSR)) & CSR_TINT) {
211 outw(LA_CSR, CSR_TINT);
212 #ifdef LEDEBUG
213 if (tmd->tmd_flags & (TMD_ONE|TMD_MORE|TMD_ERR|TMD_DEF))
214 printf("lesend: status=%x\n", tmd->tmd_flags);
215 #endif
216 break;
218 delay(10); /* don't poll too much on PCI, seems
219 to disturb DMA on poor hardware */
221 return savlen;
225 * Poll the LANCE just see if there's an Ethernet packet
226 * available. If there is, its contents is returned.
229 EtherReceive(char *pkt, int maxlen)
231 rmde_t *rp;
232 u_short csr;
233 int len = 0;
235 csr = inw(LA_CSR);
236 outw(LA_CSR, csr & (CSR_BABL | CSR_MISS | CSR_MERR | CSR_RINT));
238 if ((next_rmd < 0) || (next_rmd >= NRCVRING)) {
239 printf("next_rmd bad\n");
240 return 0;
242 rp = &rmd[next_rmd];
244 if (rp->rmd_flags & RMD_OWN)
245 return 0;
247 if (csr & (CSR_BABL | CSR_CERR | CSR_MISS | CSR_MERR))
248 printf("le: csr %x\n", csr);
250 if (rp->rmd_flags & (RMD_FRAM | RMD_OFLO | RMD_CRC | RMD_BUFF)) {
251 printf("le: rmd_flags %x\n", rp->rmd_flags);
252 goto cleanup;
255 if (rp->rmd_flags != (RMD_STP|RMD_ENP)) {
256 printf("le: rmd_flags %x\n", rp->rmd_flags);
257 return -1;
260 len = rp->rmd_mcnt - 4;
262 if ((len < 0) || (len >= LANCEBUFSIZE)) {
263 printf("bad pkt len\n");
264 return -1;
267 if (len <= maxlen)
268 memcpy(pkt, rbuffer[next_rmd], len);
269 else
270 len = 0;
272 cleanup:
273 /* give packet back to the lance */
274 rp->rmd_bcnt = -LANCEBUFSIZE;
275 rp->rmd_mcnt = 0;
276 rp->rmd_flags = RMD_OWN;
277 next_rmd = (next_rmd + 1) & (NRCVRING - 1);
279 return len;