1/*	$NetBSD: am7990.c,v 1.6 2007/03/04 05:59:59 christos 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
27extern u_char eth_myaddr[6];
28
29extern int lance_rap, lance_rdp;
30
31static 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
51static int next_rmd;			/* next receive element */
52static initblock_t *initblock;		/* initialization block */
53static tmde_t *tmd;			/* transmit ring */
54static rmde_t *rmd;			/* receive ring */
55static char rbuffer[NRCVRING][LANCEBUFSIZE]; /* receive buffers */
56
57/*
58 * Stop ethernet board
59 */
60void
61am7990_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 */
79void
80am7990_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 ressources
161 */
162void
163EtherStop(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 */
174int
175EtherSend(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 */
228int
229EtherReceive(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