1 /* $NetBSD: am7990.c,v 1.8 2020/08/24 05:37:40 msaitoh 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 void *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(void)61 am7990_stop(void)
62 {
63 long l;
64
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;
72 }
73 }
74 }
75
76 /*
77 * Reset ethernet board
78 */
79 void
am7990_init(void)80 am7990_init(void)
81 {
82 long l;
83 u_long addr;
84 int i;
85
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! */
91
92 initblock = (initblock_t *)(((unsigned long)dmamem + 4) & -8);
93 tmd = (tmde_t *)(initblock + 1);
94 rmd = (rmde_t *)(tmd + 1);
95
96 /* stop the chip, and make sure it did */
97 am7990_stop();
98
99 /* fill lance initialization block */
100 memset(initblock, 0, sizeof(initblock_t));
101
102 /* set my ethernet address */
103 for (i = 0; i < 6; i++)
104 initblock->ib_padr[i] = eth_myaddr[i];
105
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;
111
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;
117
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;
126 }
127
128 /* zero transmit ring */
129 memset(tmd, 0, sizeof(tmde_t));
130
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);
139
140 /* and initialize it */
141 outw(LA_RAP, RDP_CSR0);
142 outw(LA_CSR, CSR_INIT|CSR_STRT);
143
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;
149 }
150 }
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;
155 }
156 }
157 }
158
159 /*
160 * Stop ethernet board and free resources
161 */
162 void
EtherStop(void)163 EtherStop(void)
164 {
165 am7990_stop();
166
167 dealloc(dmamem, sizeof(initblock_t) +
168 sizeof(tmde_t) + NRCVRING * sizeof(rmde_t) + 4);
169 }
170
171 /*
172 * Send an ethernet packet
173 */
174 int
EtherSend(char * pkt,int len)175 EtherSend(char *pkt, int len)
176 {
177 long l;
178 u_long addr;
179 u_short csr;
180 int savlen = len;
181
182 if (len < 60)
183 len = 60;
184 if (len > LANCEBUFSIZE) {
185 printf("packet too long\n");
186 return -1;
187 }
188
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;
193 }
194 addr = LA(pkt);
195 if (addr & 1) {
196 printf("unaligned data\n");
197 return -1;
198 }
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;
204
205 /* start transmission */
206 outw(LA_CSR, CSR_TDMD);
207
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;
217 }
218 delay(10); /* don't poll too much on PCI, seems
219 to disturb DMA on poor hardware */
220 }
221 return savlen;
222 }
223
224 /*
225 * Poll the LANCE just see if there's an Ethernet packet
226 * available. If there is, its contents is returned.
227 */
228 int
EtherReceive(char * pkt,int maxlen)229 EtherReceive(char *pkt, int maxlen)
230 {
231 rmde_t *rp;
232 u_short csr;
233 int len = 0;
234
235 csr = inw(LA_CSR);
236 outw(LA_CSR, csr & (CSR_BABL | CSR_MISS | CSR_MERR | CSR_RINT));
237
238 if ((next_rmd < 0) || (next_rmd >= NRCVRING)) {
239 printf("next_rmd bad\n");
240 return 0;
241 }
242 rp = &rmd[next_rmd];
243
244 if (rp->rmd_flags & RMD_OWN)
245 return 0;
246
247 if (csr & (CSR_BABL | CSR_CERR | CSR_MISS | CSR_MERR))
248 printf("le: csr %x\n", csr);
249
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;
253 }
254
255 if (rp->rmd_flags != (RMD_STP|RMD_ENP)) {
256 printf("le: rmd_flags %x\n", rp->rmd_flags);
257 return -1;
258 }
259
260 len = rp->rmd_mcnt - 4;
261
262 if ((len < 0) || (len >= LANCEBUFSIZE)) {
263 printf("bad pkt len\n");
264 return -1;
265 }
266
267 if (len <= maxlen)
268 memcpy(pkt, rbuffer[next_rmd], len);
269 else
270 len = 0;
271
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);
278
279 return len;
280 }
281