xref: /freebsd/sys/dev/le/am7990.c (revision 0c05ab41ab49810945b69e861df3bf3ac9e13308)
1a7ee7a7dSMarius Strobl /*	$NetBSD: am7990.c,v 1.68 2005/12/11 12:21:25 christos Exp $	*/
2a7ee7a7dSMarius Strobl 
3a7ee7a7dSMarius Strobl /*-
4a7ee7a7dSMarius Strobl  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5a7ee7a7dSMarius Strobl  * All rights reserved.
6a7ee7a7dSMarius Strobl  *
7a7ee7a7dSMarius Strobl  * This code is derived from software contributed to The NetBSD Foundation
8a7ee7a7dSMarius Strobl  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
9a7ee7a7dSMarius Strobl  * Simulation Facility, NASA Ames Research Center.
10a7ee7a7dSMarius Strobl  *
11a7ee7a7dSMarius Strobl  * Redistribution and use in source and binary forms, with or without
12a7ee7a7dSMarius Strobl  * modification, are permitted provided that the following conditions
13a7ee7a7dSMarius Strobl  * are met:
14a7ee7a7dSMarius Strobl  * 1. Redistributions of source code must retain the above copyright
15a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer.
16a7ee7a7dSMarius Strobl  * 2. Redistributions in binary form must reproduce the above copyright
17a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer in the
18a7ee7a7dSMarius Strobl  *    documentation and/or other materials provided with the distribution.
19a7ee7a7dSMarius Strobl  * 3. All advertising materials mentioning features or use of this software
20a7ee7a7dSMarius Strobl  *    must display the following acknowledgement:
21a7ee7a7dSMarius Strobl  *	This product includes software developed by the NetBSD
22a7ee7a7dSMarius Strobl  *	Foundation, Inc. and its contributors.
23a7ee7a7dSMarius Strobl  * 4. Neither the name of The NetBSD Foundation nor the names of its
24a7ee7a7dSMarius Strobl  *    contributors may be used to endorse or promote products derived
25a7ee7a7dSMarius Strobl  *    from this software without specific prior written permission.
26a7ee7a7dSMarius Strobl  *
27a7ee7a7dSMarius Strobl  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28a7ee7a7dSMarius Strobl  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29a7ee7a7dSMarius Strobl  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30a7ee7a7dSMarius Strobl  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31a7ee7a7dSMarius Strobl  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32a7ee7a7dSMarius Strobl  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33a7ee7a7dSMarius Strobl  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34a7ee7a7dSMarius Strobl  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35a7ee7a7dSMarius Strobl  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36a7ee7a7dSMarius Strobl  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37a7ee7a7dSMarius Strobl  * POSSIBILITY OF SUCH DAMAGE.
38a7ee7a7dSMarius Strobl  */
39a7ee7a7dSMarius Strobl 
40a7ee7a7dSMarius Strobl /*-
41a7ee7a7dSMarius Strobl  * Copyright (c) 1992, 1993
42a7ee7a7dSMarius Strobl  *	The Regents of the University of California.  All rights reserved.
43a7ee7a7dSMarius Strobl  *
44a7ee7a7dSMarius Strobl  * This code is derived from software contributed to Berkeley by
45a7ee7a7dSMarius Strobl  * Ralph Campbell and Rick Macklem.
46a7ee7a7dSMarius Strobl  *
47a7ee7a7dSMarius Strobl  * Redistribution and use in source and binary forms, with or without
48a7ee7a7dSMarius Strobl  * modification, are permitted provided that the following conditions
49a7ee7a7dSMarius Strobl  * are met:
50a7ee7a7dSMarius Strobl  * 1. Redistributions of source code must retain the above copyright
51a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer.
52a7ee7a7dSMarius Strobl  * 2. Redistributions in binary form must reproduce the above copyright
53a7ee7a7dSMarius Strobl  *    notice, this list of conditions and the following disclaimer in the
54a7ee7a7dSMarius Strobl  *    documentation and/or other materials provided with the distribution.
55a7ee7a7dSMarius Strobl  * 3. Neither the name of the University nor the names of its contributors
56a7ee7a7dSMarius Strobl  *    may be used to endorse or promote products derived from this software
57a7ee7a7dSMarius Strobl  *    without specific prior written permission.
58a7ee7a7dSMarius Strobl  *
59a7ee7a7dSMarius Strobl  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60a7ee7a7dSMarius Strobl  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61a7ee7a7dSMarius Strobl  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62a7ee7a7dSMarius Strobl  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63a7ee7a7dSMarius Strobl  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64a7ee7a7dSMarius Strobl  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65a7ee7a7dSMarius Strobl  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66a7ee7a7dSMarius Strobl  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67a7ee7a7dSMarius Strobl  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68a7ee7a7dSMarius Strobl  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69a7ee7a7dSMarius Strobl  * SUCH DAMAGE.
70a7ee7a7dSMarius Strobl  *
71a7ee7a7dSMarius Strobl  *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
72a7ee7a7dSMarius Strobl  */
73a7ee7a7dSMarius Strobl 
74a7ee7a7dSMarius Strobl #include <sys/cdefs.h>
75a7ee7a7dSMarius Strobl __FBSDID("$FreeBSD$");
76a7ee7a7dSMarius Strobl 
77a7ee7a7dSMarius Strobl #include <sys/param.h>
78a7ee7a7dSMarius Strobl #include <sys/bus.h>
79a7ee7a7dSMarius Strobl #include <sys/endian.h>
80a7ee7a7dSMarius Strobl #include <sys/lock.h>
81a7ee7a7dSMarius Strobl #include <sys/mbuf.h>
82a7ee7a7dSMarius Strobl #include <sys/mutex.h>
83a7ee7a7dSMarius Strobl #include <sys/socket.h>
84a7ee7a7dSMarius Strobl 
85a7ee7a7dSMarius Strobl #include <net/bpf.h>
86a7ee7a7dSMarius Strobl #include <net/ethernet.h>
87a7ee7a7dSMarius Strobl #include <net/if.h>
88a7ee7a7dSMarius Strobl #include <net/if_arp.h>
89a7ee7a7dSMarius Strobl #include <net/if_dl.h>
90a7ee7a7dSMarius Strobl #include <net/if_media.h>
91a7ee7a7dSMarius Strobl #include <net/if_var.h>
92a7ee7a7dSMarius Strobl 
93a7ee7a7dSMarius Strobl #include <dev/le/lancereg.h>
94a7ee7a7dSMarius Strobl #include <dev/le/lancevar.h>
95a7ee7a7dSMarius Strobl #include <dev/le/am7990reg.h>
96a7ee7a7dSMarius Strobl #include <dev/le/am7990var.h>
97a7ee7a7dSMarius Strobl 
98a7ee7a7dSMarius Strobl static void	am7990_meminit(struct lance_softc *);
99a7ee7a7dSMarius Strobl static void	am7990_rint(struct lance_softc *);
100a7ee7a7dSMarius Strobl static void	am7990_tint(struct lance_softc *);
101a7ee7a7dSMarius Strobl static void	am7990_start_locked(struct lance_softc *sc);
102a7ee7a7dSMarius Strobl 
103a7ee7a7dSMarius Strobl #ifdef LEDEBUG
104a7ee7a7dSMarius Strobl static void	am7990_recv_print(struct lance_softc *, int);
105a7ee7a7dSMarius Strobl static void	am7990_xmit_print(struct lance_softc *, int);
106a7ee7a7dSMarius Strobl #endif
107a7ee7a7dSMarius Strobl 
108a7ee7a7dSMarius Strobl int
109a7ee7a7dSMarius Strobl am7990_config(struct am7990_softc *sc, const char* name, int unit)
110a7ee7a7dSMarius Strobl {
111a7ee7a7dSMarius Strobl 	int error, mem;
112a7ee7a7dSMarius Strobl 
113a7ee7a7dSMarius Strobl 	sc->lsc.sc_meminit = am7990_meminit;
114a7ee7a7dSMarius Strobl 	sc->lsc.sc_start_locked = am7990_start_locked;
115a7ee7a7dSMarius Strobl 
116a7ee7a7dSMarius Strobl 	error = lance_config(&sc->lsc, name, unit);
117a7ee7a7dSMarius Strobl 	if (error != 0)
118a7ee7a7dSMarius Strobl 		return (error);
119a7ee7a7dSMarius Strobl 
120a7ee7a7dSMarius Strobl 	mem = 0;
121a7ee7a7dSMarius Strobl 	sc->lsc.sc_initaddr = mem;
122a7ee7a7dSMarius Strobl 	mem += sizeof(struct leinit);
123a7ee7a7dSMarius Strobl 	sc->lsc.sc_rmdaddr = mem;
124a7ee7a7dSMarius Strobl 	mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf;
125a7ee7a7dSMarius Strobl 	sc->lsc.sc_tmdaddr = mem;
126a7ee7a7dSMarius Strobl 	mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf;
127a7ee7a7dSMarius Strobl 	sc->lsc.sc_rbufaddr = mem;
128a7ee7a7dSMarius Strobl 	mem += LEBLEN * sc->lsc.sc_nrbuf;
129a7ee7a7dSMarius Strobl 	sc->lsc.sc_tbufaddr = mem;
130a7ee7a7dSMarius Strobl 	mem += LEBLEN * sc->lsc.sc_ntbuf;
131a7ee7a7dSMarius Strobl 
132a7ee7a7dSMarius Strobl 	if (mem > sc->lsc.sc_memsize)
133a7ee7a7dSMarius Strobl 		panic("%s: memsize", __func__);
134a7ee7a7dSMarius Strobl 
135a7ee7a7dSMarius Strobl 	lance_attach(&sc->lsc);
136a7ee7a7dSMarius Strobl 
137a7ee7a7dSMarius Strobl 	return (0);
138a7ee7a7dSMarius Strobl }
139a7ee7a7dSMarius Strobl 
140a7ee7a7dSMarius Strobl void
141a7ee7a7dSMarius Strobl am7990_detach(struct am7990_softc *sc)
142a7ee7a7dSMarius Strobl {
143a7ee7a7dSMarius Strobl 
144a7ee7a7dSMarius Strobl 	lance_detach(&sc->lsc);
145a7ee7a7dSMarius Strobl }
146a7ee7a7dSMarius Strobl 
147a7ee7a7dSMarius Strobl /*
148a7ee7a7dSMarius Strobl  * Set up the initialization block and the descriptor rings.
149a7ee7a7dSMarius Strobl  */
150a7ee7a7dSMarius Strobl static void
151a7ee7a7dSMarius Strobl am7990_meminit(struct lance_softc *sc)
152a7ee7a7dSMarius Strobl {
153a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
154a7ee7a7dSMarius Strobl 	struct leinit init;
155a7ee7a7dSMarius Strobl 	struct lermd rmd;
156a7ee7a7dSMarius Strobl 	struct letmd tmd;
157a7ee7a7dSMarius Strobl 	u_long a;
158a7ee7a7dSMarius Strobl 	int bix;
159a7ee7a7dSMarius Strobl 
160a7ee7a7dSMarius Strobl 	LE_LOCK_ASSERT(sc, MA_OWNED);
161a7ee7a7dSMarius Strobl 
162a7ee7a7dSMarius Strobl 	if (ifp->if_flags & IFF_PROMISC)
163a7ee7a7dSMarius Strobl 		init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM;
164a7ee7a7dSMarius Strobl 	else
165a7ee7a7dSMarius Strobl 		init.init_mode = LE_MODE_NORMAL;
166a7ee7a7dSMarius Strobl 
167a7ee7a7dSMarius Strobl 	init.init_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0];
168a7ee7a7dSMarius Strobl 	init.init_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2];
169a7ee7a7dSMarius Strobl 	init.init_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4];
170a7ee7a7dSMarius Strobl 	lance_setladrf(sc, init.init_ladrf);
171a7ee7a7dSMarius Strobl 
172a7ee7a7dSMarius Strobl 	sc->sc_last_rd = 0;
173a7ee7a7dSMarius Strobl 	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
174a7ee7a7dSMarius Strobl 
175a7ee7a7dSMarius Strobl 	a = sc->sc_addr + LE_RMDADDR(sc, 0);
176a7ee7a7dSMarius Strobl 	init.init_rdra = a;
177a7ee7a7dSMarius Strobl 	init.init_rlen = (a >> 16) | ((ffs(sc->sc_nrbuf) - 1) << 13);
178a7ee7a7dSMarius Strobl 
179a7ee7a7dSMarius Strobl 	a = sc->sc_addr + LE_TMDADDR(sc, 0);
180a7ee7a7dSMarius Strobl 	init.init_tdra = a;
181a7ee7a7dSMarius Strobl 	init.init_tlen = (a >> 16) | ((ffs(sc->sc_ntbuf) - 1) << 13);
182a7ee7a7dSMarius Strobl 
183a7ee7a7dSMarius Strobl 	(*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
184a7ee7a7dSMarius Strobl 
185a7ee7a7dSMarius Strobl 	/*
186a7ee7a7dSMarius Strobl 	 * Set up receive ring descriptors.
187a7ee7a7dSMarius Strobl 	 */
188a7ee7a7dSMarius Strobl 	for (bix = 0; bix < sc->sc_nrbuf; bix++) {
189a7ee7a7dSMarius Strobl 		a = sc->sc_addr + LE_RBUFADDR(sc, bix);
190a7ee7a7dSMarius Strobl 		rmd.rmd0 = a;
191a7ee7a7dSMarius Strobl 		rmd.rmd1_hadr = a >> 16;
192a7ee7a7dSMarius Strobl 		rmd.rmd1_bits = LE_R1_OWN;
193a7ee7a7dSMarius Strobl 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
194a7ee7a7dSMarius Strobl 		rmd.rmd3 = 0;
195a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
196a7ee7a7dSMarius Strobl 		    sizeof(rmd));
197a7ee7a7dSMarius Strobl 	}
198a7ee7a7dSMarius Strobl 
199a7ee7a7dSMarius Strobl 	/*
200a7ee7a7dSMarius Strobl 	 * Set up transmit ring descriptors.
201a7ee7a7dSMarius Strobl 	 */
202a7ee7a7dSMarius Strobl 	for (bix = 0; bix < sc->sc_ntbuf; bix++) {
203a7ee7a7dSMarius Strobl 		a = sc->sc_addr + LE_TBUFADDR(sc, bix);
204a7ee7a7dSMarius Strobl 		tmd.tmd0 = a;
205a7ee7a7dSMarius Strobl 		tmd.tmd1_hadr = a >> 16;
206a7ee7a7dSMarius Strobl 		tmd.tmd1_bits = 0;
207a7ee7a7dSMarius Strobl 		tmd.tmd2 = LE_XMD2_ONES;
208a7ee7a7dSMarius Strobl 		tmd.tmd3 = 0;
209a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
210a7ee7a7dSMarius Strobl 		    sizeof(tmd));
211a7ee7a7dSMarius Strobl 	}
212a7ee7a7dSMarius Strobl }
213a7ee7a7dSMarius Strobl 
214a7ee7a7dSMarius Strobl static void
215a7ee7a7dSMarius Strobl am7990_rint(struct lance_softc *sc)
216a7ee7a7dSMarius Strobl {
217a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
218a7ee7a7dSMarius Strobl 	struct lermd rmd;
219a7ee7a7dSMarius Strobl 	int bix, rp;
220a7ee7a7dSMarius Strobl 
221a7ee7a7dSMarius Strobl 	bix = sc->sc_last_rd;
222a7ee7a7dSMarius Strobl 
223a7ee7a7dSMarius Strobl 	/* Process all buffers with valid data. */
224a7ee7a7dSMarius Strobl 	for (;;) {
225a7ee7a7dSMarius Strobl 		rp = LE_RMDADDR(sc, bix);
226a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
227a7ee7a7dSMarius Strobl 
228a7ee7a7dSMarius Strobl 		if (rmd.rmd1_bits & LE_R1_OWN)
229a7ee7a7dSMarius Strobl 			break;
230a7ee7a7dSMarius Strobl 
231a7ee7a7dSMarius Strobl 		if (rmd.rmd1_bits & LE_R1_ERR) {
232a7ee7a7dSMarius Strobl 			if (rmd.rmd1_bits & LE_R1_ENP) {
233a7ee7a7dSMarius Strobl #ifdef LEDEBUG
234a7ee7a7dSMarius Strobl 				if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) {
235a7ee7a7dSMarius Strobl 					if (rmd.rmd1_bits & LE_R1_FRAM)
236a7ee7a7dSMarius Strobl 						if_printf(ifp,
237a7ee7a7dSMarius Strobl 						    "framing error\n");
238a7ee7a7dSMarius Strobl 					if (rmd.rmd1_bits & LE_R1_CRC)
239a7ee7a7dSMarius Strobl 						if_printf(ifp,
240a7ee7a7dSMarius Strobl 						    "crc mismatch\n");
241a7ee7a7dSMarius Strobl 				}
242a7ee7a7dSMarius Strobl #endif
243a7ee7a7dSMarius Strobl 			} else {
244a7ee7a7dSMarius Strobl 				if (rmd.rmd1_bits & LE_R1_OFLO)
245a7ee7a7dSMarius Strobl 					if_printf(ifp, "overflow\n");
246a7ee7a7dSMarius Strobl 			}
247a7ee7a7dSMarius Strobl 			if (rmd.rmd1_bits & LE_R1_BUFF)
248a7ee7a7dSMarius Strobl 				if_printf(ifp, "receive buffer error\n");
249a7ee7a7dSMarius Strobl 			ifp->if_ierrors++;
250a7ee7a7dSMarius Strobl 		} else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) !=
251a7ee7a7dSMarius Strobl 		    (LE_R1_STP | LE_R1_ENP)) {
252a7ee7a7dSMarius Strobl 			if_printf(ifp, "dropping chained buffer\n");
253a7ee7a7dSMarius Strobl 			ifp->if_ierrors++;
254a7ee7a7dSMarius Strobl 		} else {
255a7ee7a7dSMarius Strobl #ifdef LEDEBUG
256a7ee7a7dSMarius Strobl 			if (sc->sc_flags & LE_DEBUG)
257a7ee7a7dSMarius Strobl 				am7990_recv_print(sc, sc->sc_last_rd);
258a7ee7a7dSMarius Strobl #endif
259a7ee7a7dSMarius Strobl 			lance_read(sc, LE_RBUFADDR(sc, bix),
260a7ee7a7dSMarius Strobl 			    (int)rmd.rmd3 - ETHER_CRC_LEN);
261a7ee7a7dSMarius Strobl 		}
262a7ee7a7dSMarius Strobl 
263a7ee7a7dSMarius Strobl 		rmd.rmd1_bits = LE_R1_OWN;
264a7ee7a7dSMarius Strobl 		rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
265a7ee7a7dSMarius Strobl 		rmd.rmd3 = 0;
266a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
267a7ee7a7dSMarius Strobl 
268a7ee7a7dSMarius Strobl #ifdef LEDEBUG
269a7ee7a7dSMarius Strobl 		if (sc->sc_flags & LE_DEBUG)
270a7ee7a7dSMarius Strobl 			if_printf(ifp, "sc->sc_last_rd = %x, rmd: "
271a7ee7a7dSMarius Strobl 			    "ladr %04x, hadr %02x, flags %02x, "
272a7ee7a7dSMarius Strobl 			    "bcnt %04x, mcnt %04x\n",
273a7ee7a7dSMarius Strobl 			    sc->sc_last_rd, rmd.rmd0, rmd.rmd1_hadr,
274a7ee7a7dSMarius Strobl 			    rmd.rmd1_bits, rmd.rmd2, rmd.rmd3);
275a7ee7a7dSMarius Strobl #endif
276a7ee7a7dSMarius Strobl 
277a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_nrbuf)
278a7ee7a7dSMarius Strobl 			bix = 0;
279a7ee7a7dSMarius Strobl 	}
280a7ee7a7dSMarius Strobl 
281a7ee7a7dSMarius Strobl 	sc->sc_last_rd = bix;
282a7ee7a7dSMarius Strobl }
283a7ee7a7dSMarius Strobl 
284a7ee7a7dSMarius Strobl static void
285a7ee7a7dSMarius Strobl am7990_tint(struct lance_softc *sc)
286a7ee7a7dSMarius Strobl {
287a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
288a7ee7a7dSMarius Strobl 	struct letmd tmd;
289a7ee7a7dSMarius Strobl 	int bix;
290a7ee7a7dSMarius Strobl 
291a7ee7a7dSMarius Strobl 	bix = sc->sc_first_td;
292a7ee7a7dSMarius Strobl 
293a7ee7a7dSMarius Strobl 	for (;;) {
294a7ee7a7dSMarius Strobl 		if (sc->sc_no_td <= 0)
295a7ee7a7dSMarius Strobl 			break;
296a7ee7a7dSMarius Strobl 
297a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
298a7ee7a7dSMarius Strobl 		    sizeof(tmd));
299a7ee7a7dSMarius Strobl 
300a7ee7a7dSMarius Strobl #ifdef LEDEBUG
301a7ee7a7dSMarius Strobl 		if (sc->sc_flags & LE_DEBUG)
302a7ee7a7dSMarius Strobl 			if_printf(ifp, "trans tmd: "
303a7ee7a7dSMarius Strobl 			    "ladr %04x, hadr %02x, flags %02x, "
304a7ee7a7dSMarius Strobl 			    "bcnt %04x, mcnt %04x\n",
305a7ee7a7dSMarius Strobl 			    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits,
306a7ee7a7dSMarius Strobl 			    tmd.tmd2, tmd.tmd3);
307a7ee7a7dSMarius Strobl #endif
308a7ee7a7dSMarius Strobl 
309a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_OWN)
310a7ee7a7dSMarius Strobl 			break;
311a7ee7a7dSMarius Strobl 
312a7ee7a7dSMarius Strobl 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
313a7ee7a7dSMarius Strobl 
314a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_ERR) {
315a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_BUFF)
316a7ee7a7dSMarius Strobl 				if_printf(ifp, "transmit buffer error\n");
317a7ee7a7dSMarius Strobl 			else if (tmd.tmd3 & LE_T3_UFLO)
318a7ee7a7dSMarius Strobl 				if_printf(ifp, "underflow\n");
319a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) {
320a7ee7a7dSMarius Strobl 				lance_init_locked(sc);
321a7ee7a7dSMarius Strobl 				return;
322a7ee7a7dSMarius Strobl 			}
323a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_LCAR) {
324a7ee7a7dSMarius Strobl 				if (sc->sc_flags & LE_CARRIER)
325a7ee7a7dSMarius Strobl 					if_link_state_change(ifp,
326a7ee7a7dSMarius Strobl 					    LINK_STATE_DOWN);
327a7ee7a7dSMarius Strobl 				sc->sc_flags &= ~LE_CARRIER;
328a7ee7a7dSMarius Strobl 				if (sc->sc_nocarrier)
329a7ee7a7dSMarius Strobl 					(*sc->sc_nocarrier)(sc);
330a7ee7a7dSMarius Strobl 				else
331a7ee7a7dSMarius Strobl 					if_printf(ifp, "lost carrier\n");
332a7ee7a7dSMarius Strobl 			}
333a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_LCOL)
334a7ee7a7dSMarius Strobl 				ifp->if_collisions++;
335a7ee7a7dSMarius Strobl 			if (tmd.tmd3 & LE_T3_RTRY) {
336a7ee7a7dSMarius Strobl #ifdef LEDEBUG
337a7ee7a7dSMarius Strobl 				if_printf(ifp, "excessive collisions, tdr %d\n",
338a7ee7a7dSMarius Strobl 				    tmd.tmd3 & LE_T3_TDR_MASK);
339a7ee7a7dSMarius Strobl #endif
340a7ee7a7dSMarius Strobl 				ifp->if_collisions += 16;
341a7ee7a7dSMarius Strobl 			}
342a7ee7a7dSMarius Strobl 			ifp->if_oerrors++;
343a7ee7a7dSMarius Strobl 		} else {
344a7ee7a7dSMarius Strobl 			if (tmd.tmd1_bits & LE_T1_ONE)
345a7ee7a7dSMarius Strobl 				ifp->if_collisions++;
346a7ee7a7dSMarius Strobl 			else if (tmd.tmd1_bits & LE_T1_MORE)
347a7ee7a7dSMarius Strobl 				/* Real number is unknown. */
348a7ee7a7dSMarius Strobl 				ifp->if_collisions += 2;
349a7ee7a7dSMarius Strobl 			ifp->if_opackets++;
350a7ee7a7dSMarius Strobl 		}
351a7ee7a7dSMarius Strobl 
352a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_ntbuf)
353a7ee7a7dSMarius Strobl 			bix = 0;
354a7ee7a7dSMarius Strobl 
355a7ee7a7dSMarius Strobl 		--sc->sc_no_td;
356a7ee7a7dSMarius Strobl 	}
357a7ee7a7dSMarius Strobl 
358a7ee7a7dSMarius Strobl 	sc->sc_first_td = bix;
359a7ee7a7dSMarius Strobl 
3600c05ab41SMarius Strobl 	ifp->if_timer = sc->sc_no_td > 0 ? 5 : 0;
361a7ee7a7dSMarius Strobl }
362a7ee7a7dSMarius Strobl 
363a7ee7a7dSMarius Strobl /*
364a7ee7a7dSMarius Strobl  * Controller interrupt
365a7ee7a7dSMarius Strobl  */
366a7ee7a7dSMarius Strobl void
367a7ee7a7dSMarius Strobl am7990_intr(void *arg)
368a7ee7a7dSMarius Strobl {
369a7ee7a7dSMarius Strobl 	struct lance_softc *sc = arg;
370a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
371a7ee7a7dSMarius Strobl 	uint16_t isr;
372a7ee7a7dSMarius Strobl 
373a7ee7a7dSMarius Strobl 	LE_LOCK(sc);
374a7ee7a7dSMarius Strobl 
375a7ee7a7dSMarius Strobl 	if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) {
376a7ee7a7dSMarius Strobl 		ifp->if_ierrors++;
377a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
378a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
379a7ee7a7dSMarius Strobl 		return;
380a7ee7a7dSMarius Strobl 	}
381a7ee7a7dSMarius Strobl 
382a7ee7a7dSMarius Strobl 	isr = (*sc->sc_rdcsr)(sc, LE_CSR0);
383a7ee7a7dSMarius Strobl #if defined(LEDEBUG) && LEDEBUG > 1
384a7ee7a7dSMarius Strobl 	if (sc->sc_flags & LE_DEBUG)
385a7ee7a7dSMarius Strobl 		if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr);
386a7ee7a7dSMarius Strobl #endif
387a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_INTR) == 0) {
388a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
389a7ee7a7dSMarius Strobl 		return;
390a7ee7a7dSMarius Strobl 	}
391a7ee7a7dSMarius Strobl 
3920c05ab41SMarius Strobl 	/*
3930c05ab41SMarius Strobl 	 * Clear interrupt source flags and turn off interrupts. If we
3940c05ab41SMarius Strobl 	 * don't clear these flags before processing their sources we
3950c05ab41SMarius Strobl 	 * could completely miss some interrupt events as the the NIC
3960c05ab41SMarius Strobl 	 * can change these flags while we're in this handler. We turn
3970c05ab41SMarius Strobl 	 * of interrupts while processing them so we don't get another
3980c05ab41SMarius Strobl 	 * one while we still process the previous one in ifp->if_input()
3990c05ab41SMarius Strobl 	 * with the driver lock dropped.
4000c05ab41SMarius Strobl 	 */
4010c05ab41SMarius Strobl 	(*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD |
4020c05ab41SMarius Strobl 	    LE_C0_STOP | LE_C0_STRT | LE_C0_INIT));
4030c05ab41SMarius Strobl 
404a7ee7a7dSMarius Strobl 	if (isr & LE_C0_ERR) {
405a7ee7a7dSMarius Strobl 		if (isr & LE_C0_BABL) {
406a7ee7a7dSMarius Strobl #ifdef LEDEBUG
407a7ee7a7dSMarius Strobl 			if_printf(ifp, "babble\n");
408a7ee7a7dSMarius Strobl #endif
409a7ee7a7dSMarius Strobl 			ifp->if_oerrors++;
410a7ee7a7dSMarius Strobl 		}
411a7ee7a7dSMarius Strobl #if 0
412a7ee7a7dSMarius Strobl 		if (isr & LE_C0_CERR) {
413a7ee7a7dSMarius Strobl 			if_printf(ifp, "collision error\n");
414a7ee7a7dSMarius Strobl 			ifp->if_collisions++;
415a7ee7a7dSMarius Strobl 		}
416a7ee7a7dSMarius Strobl #endif
417a7ee7a7dSMarius Strobl 		if (isr & LE_C0_MISS) {
418a7ee7a7dSMarius Strobl #ifdef LEDEBUG
419a7ee7a7dSMarius Strobl 			if_printf(ifp, "missed packet\n");
420a7ee7a7dSMarius Strobl #endif
421a7ee7a7dSMarius Strobl 			ifp->if_ierrors++;
422a7ee7a7dSMarius Strobl 		}
423a7ee7a7dSMarius Strobl 		if (isr & LE_C0_MERR) {
424a7ee7a7dSMarius Strobl 			if_printf(ifp, "memory error\n");
425a7ee7a7dSMarius Strobl 			lance_init_locked(sc);
426a7ee7a7dSMarius Strobl 			LE_UNLOCK(sc);
427a7ee7a7dSMarius Strobl 			return;
428a7ee7a7dSMarius Strobl 		}
429a7ee7a7dSMarius Strobl 	}
430a7ee7a7dSMarius Strobl 
431a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_RXON) == 0) {
432a7ee7a7dSMarius Strobl 		if_printf(ifp, "receiver disabled\n");
433a7ee7a7dSMarius Strobl 		ifp->if_ierrors++;
434a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
435a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
436a7ee7a7dSMarius Strobl 		return;
437a7ee7a7dSMarius Strobl 	}
438a7ee7a7dSMarius Strobl 	if ((isr & LE_C0_TXON) == 0) {
439a7ee7a7dSMarius Strobl 		if_printf(ifp, "transmitter disabled\n");
440a7ee7a7dSMarius Strobl 		ifp->if_oerrors++;
441a7ee7a7dSMarius Strobl 		lance_init_locked(sc);
442a7ee7a7dSMarius Strobl 		LE_UNLOCK(sc);
443a7ee7a7dSMarius Strobl 		return;
444a7ee7a7dSMarius Strobl 	}
445a7ee7a7dSMarius Strobl 
446a7ee7a7dSMarius Strobl 	/*
447a7ee7a7dSMarius Strobl 	 * Pretend we have carrier; if we don't this will be cleared shortly.
448a7ee7a7dSMarius Strobl 	 */
449a7ee7a7dSMarius Strobl 	if (!(sc->sc_flags & LE_CARRIER))
450a7ee7a7dSMarius Strobl 		if_link_state_change(ifp, LINK_STATE_UP);
451a7ee7a7dSMarius Strobl 	sc->sc_flags |= LE_CARRIER;
452a7ee7a7dSMarius Strobl 
453a7ee7a7dSMarius Strobl 	if (isr & LE_C0_RINT)
454a7ee7a7dSMarius Strobl 		am7990_rint(sc);
455a7ee7a7dSMarius Strobl 	if (isr & LE_C0_TINT)
456a7ee7a7dSMarius Strobl 		am7990_tint(sc);
457a7ee7a7dSMarius Strobl 
4580c05ab41SMarius Strobl 	/* Enable interrupts again. */
4590c05ab41SMarius Strobl 	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
4600c05ab41SMarius Strobl 
4610c05ab41SMarius Strobl 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
4620c05ab41SMarius Strobl 		am7990_start_locked(sc);
463a7ee7a7dSMarius Strobl 
464a7ee7a7dSMarius Strobl 	LE_UNLOCK(sc);
465a7ee7a7dSMarius Strobl }
466a7ee7a7dSMarius Strobl 
467a7ee7a7dSMarius Strobl /*
468a7ee7a7dSMarius Strobl  * Set up output on interface.
469a7ee7a7dSMarius Strobl  * Get another datagram to send off of the interface queue, and map it to the
470a7ee7a7dSMarius Strobl  * interface before starting the output.
471a7ee7a7dSMarius Strobl  */
472a7ee7a7dSMarius Strobl static void
473a7ee7a7dSMarius Strobl am7990_start_locked(struct lance_softc *sc)
474a7ee7a7dSMarius Strobl {
475a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
476a7ee7a7dSMarius Strobl 	struct letmd tmd;
477a7ee7a7dSMarius Strobl 	struct mbuf *m;
4780c05ab41SMarius Strobl 	int bix, enq, len, rp;
479a7ee7a7dSMarius Strobl 
480a7ee7a7dSMarius Strobl 	LE_LOCK_ASSERT(sc, MA_OWNED);
481a7ee7a7dSMarius Strobl 
482a7ee7a7dSMarius Strobl 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
483a7ee7a7dSMarius Strobl 	    IFF_DRV_RUNNING)
484a7ee7a7dSMarius Strobl 		return;
485a7ee7a7dSMarius Strobl 
486a7ee7a7dSMarius Strobl 	bix = sc->sc_last_td;
4870c05ab41SMarius Strobl 	enq = 0;
488a7ee7a7dSMarius Strobl 
489a7ee7a7dSMarius Strobl 	for (; sc->sc_no_td < sc->sc_ntbuf &&
490a7ee7a7dSMarius Strobl 	    !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) {
491a7ee7a7dSMarius Strobl 		rp = LE_TMDADDR(sc, bix);
492a7ee7a7dSMarius Strobl 		(*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
493a7ee7a7dSMarius Strobl 
494a7ee7a7dSMarius Strobl 		if (tmd.tmd1_bits & LE_T1_OWN) {
495a7ee7a7dSMarius Strobl 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
496a7ee7a7dSMarius Strobl 			if_printf(ifp,
497a7ee7a7dSMarius Strobl 			    "missing buffer, no_td = %d, last_td = %d\n",
498a7ee7a7dSMarius Strobl 			    sc->sc_no_td, sc->sc_last_td);
499a7ee7a7dSMarius Strobl 		}
500a7ee7a7dSMarius Strobl 
501a7ee7a7dSMarius Strobl 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
502a7ee7a7dSMarius Strobl 		if (m == 0)
503a7ee7a7dSMarius Strobl 			break;
504a7ee7a7dSMarius Strobl 
505a7ee7a7dSMarius Strobl 		/*
506a7ee7a7dSMarius Strobl 		 * If BPF is listening on this interface, let it see the packet
507a7ee7a7dSMarius Strobl 		 * before we commit it to the wire.
508a7ee7a7dSMarius Strobl 		 */
509a7ee7a7dSMarius Strobl 		BPF_MTAP(ifp, m);
510a7ee7a7dSMarius Strobl 
511a7ee7a7dSMarius Strobl 		/*
512a7ee7a7dSMarius Strobl 		 * Copy the mbuf chain into the transmit buffer.
513a7ee7a7dSMarius Strobl 		 */
514a7ee7a7dSMarius Strobl 		len = lance_put(sc, LE_TBUFADDR(sc, bix), m);
515a7ee7a7dSMarius Strobl 
516a7ee7a7dSMarius Strobl #ifdef LEDEBUG
517a7ee7a7dSMarius Strobl 		if (len > ETHERMTU + ETHER_HDR_LEN)
518a7ee7a7dSMarius Strobl 			if_printf(ifp, "packet length %d\n", len);
519a7ee7a7dSMarius Strobl #endif
520a7ee7a7dSMarius Strobl 
521a7ee7a7dSMarius Strobl 		/*
522a7ee7a7dSMarius Strobl 		 * Init transmit registers, and set transmit start flag.
523a7ee7a7dSMarius Strobl 		 */
524a7ee7a7dSMarius Strobl 		tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
525a7ee7a7dSMarius Strobl 		tmd.tmd2 = -len | LE_XMD2_ONES;
526a7ee7a7dSMarius Strobl 		tmd.tmd3 = 0;
527a7ee7a7dSMarius Strobl 
528a7ee7a7dSMarius Strobl 		(*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
529a7ee7a7dSMarius Strobl 
530a7ee7a7dSMarius Strobl #ifdef LEDEBUG
531a7ee7a7dSMarius Strobl 		if (sc->sc_flags & LE_DEBUG)
532a7ee7a7dSMarius Strobl 			am7990_xmit_print(sc, sc->sc_last_td);
533a7ee7a7dSMarius Strobl #endif
534a7ee7a7dSMarius Strobl 
535a7ee7a7dSMarius Strobl 		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
5360c05ab41SMarius Strobl 		enq++;
537a7ee7a7dSMarius Strobl 
538a7ee7a7dSMarius Strobl 		if (++bix == sc->sc_ntbuf)
539a7ee7a7dSMarius Strobl 			bix = 0;
540a7ee7a7dSMarius Strobl 
541a7ee7a7dSMarius Strobl 		if (++sc->sc_no_td == sc->sc_ntbuf) {
542a7ee7a7dSMarius Strobl 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
543a7ee7a7dSMarius Strobl 			break;
544a7ee7a7dSMarius Strobl 		}
545a7ee7a7dSMarius Strobl 	}
546a7ee7a7dSMarius Strobl 
547a7ee7a7dSMarius Strobl 	sc->sc_last_td = bix;
5480c05ab41SMarius Strobl 
5490c05ab41SMarius Strobl 	if (enq > 0)
5500c05ab41SMarius Strobl 		ifp->if_timer = 5;
551a7ee7a7dSMarius Strobl }
552a7ee7a7dSMarius Strobl 
553a7ee7a7dSMarius Strobl #ifdef LEDEBUG
554a7ee7a7dSMarius Strobl static void
555a7ee7a7dSMarius Strobl am7990_recv_print(struct lance_softc *sc, int no)
556a7ee7a7dSMarius Strobl {
557a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
558a7ee7a7dSMarius Strobl 	struct ether_header eh;
559a7ee7a7dSMarius Strobl 	struct lermd rmd;
560a7ee7a7dSMarius Strobl 	uint16_t len;
561a7ee7a7dSMarius Strobl 
562a7ee7a7dSMarius Strobl 	(*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
563a7ee7a7dSMarius Strobl 	len = rmd.rmd3;
564a7ee7a7dSMarius Strobl 	if_printf(ifp, "receive buffer %d, len = %d\n", no, len);
565a7ee7a7dSMarius Strobl 	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
566a7ee7a7dSMarius Strobl 	if_printf(ifp,
567a7ee7a7dSMarius Strobl 	    "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
568a7ee7a7dSMarius Strobl 	    rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3);
569a7ee7a7dSMarius Strobl 	if (len - ETHER_CRC_LEN >= sizeof(eh)) {
570a7ee7a7dSMarius Strobl 		(*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
571a7ee7a7dSMarius Strobl 		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
572a7ee7a7dSMarius Strobl 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
573a7ee7a7dSMarius Strobl 		    ntohs(eh.ether_type));
574a7ee7a7dSMarius Strobl 	}
575a7ee7a7dSMarius Strobl }
576a7ee7a7dSMarius Strobl 
577a7ee7a7dSMarius Strobl static void
578a7ee7a7dSMarius Strobl am7990_xmit_print(struct lance_softc *sc, int no)
579a7ee7a7dSMarius Strobl {
580a7ee7a7dSMarius Strobl 	struct ifnet *ifp = sc->sc_ifp;
581a7ee7a7dSMarius Strobl 	struct ether_header eh;
582a7ee7a7dSMarius Strobl 	struct letmd tmd;
583a7ee7a7dSMarius Strobl 	uint16_t len;
584a7ee7a7dSMarius Strobl 
585a7ee7a7dSMarius Strobl 	(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
586a7ee7a7dSMarius Strobl 	len = -tmd.tmd2;
587a7ee7a7dSMarius Strobl 	if_printf(ifp, "transmit buffer %d, len = %d\n", no, len);
588a7ee7a7dSMarius Strobl 	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
589a7ee7a7dSMarius Strobl 	if_printf(ifp,
590a7ee7a7dSMarius Strobl 	    "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
591a7ee7a7dSMarius Strobl 	    tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3);
592a7ee7a7dSMarius Strobl 	if (len >= sizeof(eh)) {
593a7ee7a7dSMarius Strobl 		(*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
594a7ee7a7dSMarius Strobl 		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
595a7ee7a7dSMarius Strobl 		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
596a7ee7a7dSMarius Strobl 		    ntohs(eh.ether_type));
597a7ee7a7dSMarius Strobl 	}
598a7ee7a7dSMarius Strobl }
599a7ee7a7dSMarius Strobl #endif /* LEDEBUG */
600