xref: /netbsd/sys/arch/i386/stand/lib/netif/am7990.c (revision 68c4fb5e)
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