1155093Smarius/*	$NetBSD: am79900.c,v 1.17 2005/12/24 20:27:29 perry Exp $	*/
2155093Smarius
3155093Smarius/*-
4155093Smarius * Copyright (c) 1997 The NetBSD Foundation, Inc.
5155093Smarius * All rights reserved.
6155093Smarius *
7155093Smarius * This code is derived from software contributed to The NetBSD Foundation
8155093Smarius * by Jason R. Thorpe.
9155093Smarius *
10155093Smarius * Redistribution and use in source and binary forms, with or without
11155093Smarius * modification, are permitted provided that the following conditions
12155093Smarius * are met:
13155093Smarius * 1. Redistributions of source code must retain the above copyright
14155093Smarius *    notice, this list of conditions and the following disclaimer.
15155093Smarius * 2. Redistributions in binary form must reproduce the above copyright
16155093Smarius *    notice, this list of conditions and the following disclaimer in the
17155093Smarius *    documentation and/or other materials provided with the distribution.
18155093Smarius *
19155093Smarius * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20155093Smarius * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21155093Smarius * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22155093Smarius * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23155093Smarius * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24155093Smarius * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25155093Smarius * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26155093Smarius * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27155093Smarius * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28155093Smarius * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29155093Smarius * POSSIBILITY OF SUCH DAMAGE.
30155093Smarius */
31155093Smarius
32155093Smarius/*-
33155093Smarius * Copyright (c) 1992, 1993
34155093Smarius *	The Regents of the University of California.  All rights reserved.
35155093Smarius *
36155093Smarius * This code is derived from software contributed to Berkeley by
37155093Smarius * Ralph Campbell and Rick Macklem.
38155093Smarius *
39155093Smarius * Redistribution and use in source and binary forms, with or without
40155093Smarius * modification, are permitted provided that the following conditions
41155093Smarius * are met:
42155093Smarius * 1. Redistributions of source code must retain the above copyright
43155093Smarius *    notice, this list of conditions and the following disclaimer.
44155093Smarius * 2. Redistributions in binary form must reproduce the above copyright
45155093Smarius *    notice, this list of conditions and the following disclaimer in the
46155093Smarius *    documentation and/or other materials provided with the distribution.
47155093Smarius * 3. Neither the name of the University nor the names of its contributors
48155093Smarius *    may be used to endorse or promote products derived from this software
49155093Smarius *    without specific prior written permission.
50155093Smarius *
51155093Smarius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52155093Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53155093Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54155093Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55155093Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56155093Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57155093Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58155093Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59155093Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60155093Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61155093Smarius * SUCH DAMAGE.
62155093Smarius *
63155093Smarius *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
64155093Smarius */
65155093Smarius
66155093Smarius/*-
67155093Smarius * Copyright (c) 1998
68155093Smarius *	Matthias Drochner.  All rights reserved.
69155093Smarius * Copyright (c) 1995 Charles M. Hannum.  All rights reserved.
70155093Smarius *
71155093Smarius * This code is derived from software contributed to Berkeley by
72155093Smarius * Ralph Campbell and Rick Macklem.
73155093Smarius *
74155093Smarius * Redistribution and use in source and binary forms, with or without
75155093Smarius * modification, are permitted provided that the following conditions
76155093Smarius * are met:
77155093Smarius * 1. Redistributions of source code must retain the above copyright
78155093Smarius *    notice, this list of conditions and the following disclaimer.
79155093Smarius * 2. Redistributions in binary form must reproduce the above copyright
80155093Smarius *    notice, this list of conditions and the following disclaimer in the
81155093Smarius *    documentation and/or other materials provided with the distribution.
82155093Smarius * 3. All advertising materials mentioning features or use of this software
83155093Smarius *    must display the following acknowledgement:
84155093Smarius *	This product includes software developed by the University of
85155093Smarius *	California, Berkeley and its contributors.
86155093Smarius * 4. Neither the name of the University nor the names of its contributors
87155093Smarius *    may be used to endorse or promote products derived from this software
88155093Smarius *    without specific prior written permission.
89155093Smarius *
90155093Smarius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
91155093Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
92155093Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
93155093Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
94155093Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
95155093Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
96155093Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
97155093Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
98155093Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
99155093Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
100155093Smarius * SUCH DAMAGE.
101155093Smarius *
102155093Smarius *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
103155093Smarius */
104155093Smarius
105155093Smarius#include <sys/cdefs.h>
106155093Smarius__FBSDID("$FreeBSD$");
107155093Smarius
108155093Smarius#include <sys/param.h>
109155093Smarius#include <sys/bus.h>
110155093Smarius#include <sys/endian.h>
111155093Smarius#include <sys/lock.h>
112155093Smarius#include <sys/mbuf.h>
113155093Smarius#include <sys/mutex.h>
114155093Smarius#include <sys/socket.h>
115155093Smarius
116155093Smarius#include <net/bpf.h>
117155093Smarius#include <net/ethernet.h>
118155093Smarius#include <net/if.h>
119155093Smarius#include <net/if_arp.h>
120155093Smarius#include <net/if_dl.h>
121155093Smarius#include <net/if_media.h>
122155093Smarius#include <net/if_var.h>
123155093Smarius
124158663Smarius#include <machine/bus.h>
125158663Smarius
126155093Smarius#include <dev/le/lancereg.h>
127155093Smarius#include <dev/le/lancevar.h>
128155093Smarius#include <dev/le/am79900reg.h>
129155093Smarius#include <dev/le/am79900var.h>
130155093Smarius
131155093Smariusstatic void	am79900_meminit(struct lance_softc *);
132155093Smariusstatic void	am79900_rint(struct lance_softc *);
133155093Smariusstatic void	am79900_tint(struct lance_softc *);
134155093Smariusstatic void	am79900_start_locked(struct lance_softc *sc);
135155093Smarius
136155093Smarius#ifdef LEDEBUG
137155093Smariusstatic void	am79900_recv_print(struct lance_softc *, int);
138155093Smariusstatic void	am79900_xmit_print(struct lance_softc *, int);
139155093Smarius#endif
140155093Smarius
141155093Smariusint
142155093Smariusam79900_config(struct am79900_softc *sc, const char* name, int unit)
143155093Smarius{
144155093Smarius	int error, mem;
145155093Smarius
146155093Smarius	sc->lsc.sc_meminit = am79900_meminit;
147155093Smarius	sc->lsc.sc_start_locked = am79900_start_locked;
148155093Smarius
149155093Smarius	error = lance_config(&sc->lsc, name, unit);
150155093Smarius	if (error != 0)
151155093Smarius		return (error);
152155093Smarius
153155093Smarius	mem = 0;
154155093Smarius	sc->lsc.sc_initaddr = mem;
155155093Smarius	mem += sizeof(struct leinit);
156155093Smarius	sc->lsc.sc_rmdaddr = mem;
157155093Smarius	mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf;
158155093Smarius	sc->lsc.sc_tmdaddr = mem;
159155093Smarius	mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf;
160155093Smarius	sc->lsc.sc_rbufaddr = mem;
161155093Smarius	mem += LEBLEN * sc->lsc.sc_nrbuf;
162155093Smarius	sc->lsc.sc_tbufaddr = mem;
163155093Smarius	mem += LEBLEN * sc->lsc.sc_ntbuf;
164155093Smarius
165155093Smarius	if (mem > sc->lsc.sc_memsize)
166155093Smarius		panic("%s: memsize", __func__);
167155093Smarius
168155093Smarius	lance_attach(&sc->lsc);
169155093Smarius
170155093Smarius	return (0);
171155093Smarius}
172155093Smarius
173155093Smariusvoid
174155093Smariusam79900_detach(struct am79900_softc *sc)
175155093Smarius{
176155093Smarius
177155093Smarius	lance_detach(&sc->lsc);
178155093Smarius}
179155093Smarius
180155093Smarius/*
181155093Smarius * Set up the initialization block and the descriptor rings.
182155093Smarius */
183155093Smariusstatic void
184155093Smariusam79900_meminit(struct lance_softc *sc)
185155093Smarius{
186155093Smarius	struct ifnet *ifp = sc->sc_ifp;
187155093Smarius	struct leinit init;
188155093Smarius	struct lermd rmd;
189155093Smarius	struct letmd tmd;
190155093Smarius	u_long a;
191155093Smarius	int bix;
192155093Smarius
193155093Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
194155093Smarius
195155093Smarius	if (ifp->if_flags & IFF_PROMISC)
196155093Smarius		init.init_mode = LE_HTOLE32(LE_MODE_NORMAL | LE_MODE_PROM);
197155093Smarius	else
198155093Smarius		init.init_mode = LE_HTOLE32(LE_MODE_NORMAL);
199155093Smarius
200155093Smarius	init.init_mode |= LE_HTOLE32(((ffs(sc->sc_ntbuf) - 1) << 28) |
201155093Smarius	    ((ffs(sc->sc_nrbuf) - 1) << 20));
202155093Smarius
203155093Smarius	init.init_padr[0] = LE_HTOLE32(sc->sc_enaddr[0] |
204155093Smarius	    (sc->sc_enaddr[1] << 8) | (sc->sc_enaddr[2] << 16) |
205155093Smarius	    (sc->sc_enaddr[3] << 24));
206155093Smarius	init.init_padr[1] = LE_HTOLE32(sc->sc_enaddr[4] |
207155093Smarius	    (sc->sc_enaddr[5] << 8));
208155093Smarius	lance_setladrf(sc, init.init_ladrf);
209155093Smarius
210155093Smarius	sc->sc_last_rd = 0;
211155093Smarius	sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
212155093Smarius
213155093Smarius	a = sc->sc_addr + LE_RMDADDR(sc, 0);
214155093Smarius	init.init_rdra = LE_HTOLE32(a);
215155093Smarius
216155093Smarius	a = sc->sc_addr + LE_TMDADDR(sc, 0);
217155093Smarius	init.init_tdra = LE_HTOLE32(a);
218155093Smarius
219155093Smarius	(*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
220155093Smarius
221155093Smarius	/*
222155093Smarius	 * Set up receive ring descriptors.
223155093Smarius	 */
224155093Smarius	for (bix = 0; bix < sc->sc_nrbuf; bix++) {
225155093Smarius		a = sc->sc_addr + LE_RBUFADDR(sc, bix);
226155093Smarius		rmd.rmd0 = LE_HTOLE32(a);
227155093Smarius		rmd.rmd1 = LE_HTOLE32(LE_R1_OWN | LE_R1_ONES |
228155093Smarius		    (-LEBLEN & 0xfff));
229155093Smarius		rmd.rmd2 = 0;
230155093Smarius		rmd.rmd3 = 0;
231155093Smarius		(*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
232155093Smarius		    sizeof(rmd));
233155093Smarius	}
234155093Smarius
235155093Smarius	/*
236155093Smarius	 * Set up transmit ring descriptors.
237155093Smarius	 */
238155093Smarius	for (bix = 0; bix < sc->sc_ntbuf; bix++) {
239155093Smarius		a = sc->sc_addr + LE_TBUFADDR(sc, bix);
240155093Smarius		tmd.tmd0 = LE_HTOLE32(a);
241155093Smarius		tmd.tmd1 = LE_HTOLE32(LE_T1_ONES);
242155093Smarius		tmd.tmd2 = 0;
243155093Smarius		tmd.tmd3 = 0;
244155093Smarius		(*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
245155093Smarius		    sizeof(tmd));
246155093Smarius	}
247155093Smarius}
248155093Smarius
249155093Smariusstatic inline void
250155093Smariusam79900_rint(struct lance_softc *sc)
251155093Smarius{
252155093Smarius	struct ifnet *ifp = sc->sc_ifp;
253158663Smarius	struct mbuf *m;
254155093Smarius	struct lermd rmd;
255155093Smarius	uint32_t rmd1;
256155093Smarius	int bix, rp;
257158663Smarius#if defined(__i386__) && !defined(PC98)
258158663Smarius	struct ether_header *eh;
259158663Smarius#endif
260155093Smarius
261155093Smarius	bix = sc->sc_last_rd;
262155093Smarius
263155093Smarius	/* Process all buffers with valid data. */
264155093Smarius	for (;;) {
265155093Smarius		rp = LE_RMDADDR(sc, bix);
266155093Smarius		(*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
267155093Smarius
268155093Smarius		rmd1 = LE_LE32TOH(rmd.rmd1);
269155093Smarius		if (rmd1 & LE_R1_OWN)
270155093Smarius			break;
271155093Smarius
272158663Smarius		m = NULL;
273158663Smarius		if ((rmd1 & (LE_R1_ERR | LE_R1_STP | LE_R1_ENP)) !=
274158663Smarius		    (LE_R1_STP | LE_R1_ENP)){
275158663Smarius			if (rmd1 & LE_R1_ERR) {
276155093Smarius#ifdef LEDEBUG
277158663Smarius				if (rmd1 & LE_R1_ENP) {
278158663Smarius					if ((rmd1 & LE_R1_OFLO) == 0) {
279158663Smarius						if (rmd1 & LE_R1_FRAM)
280158663Smarius							if_printf(ifp,
281158663Smarius							    "framing error\n");
282158663Smarius						if (rmd1 & LE_R1_CRC)
283158663Smarius							if_printf(ifp,
284158663Smarius							    "crc mismatch\n");
285158663Smarius					}
286158663Smarius				} else
287158663Smarius					if (rmd1 & LE_R1_OFLO)
288158663Smarius						if_printf(ifp, "overflow\n");
289155093Smarius#endif
290158663Smarius				if (rmd1 & LE_R1_BUFF)
291158663Smarius					if_printf(ifp,
292158663Smarius					    "receive buffer error\n");
293158663Smarius			} else if ((rmd1 & (LE_R1_STP | LE_R1_ENP)) !=
294158663Smarius			    (LE_R1_STP | LE_R1_ENP))
295158663Smarius				if_printf(ifp, "dropping chained buffer\n");
296155093Smarius		} else {
297155093Smarius#ifdef LEDEBUG
298155093Smarius			if (sc->sc_flags & LE_DEBUG)
299158663Smarius				am79900_recv_print(sc, bix);
300155093Smarius#endif
301158663Smarius			/* Pull the packet off the interface. */
302158663Smarius			m = lance_get(sc, LE_RBUFADDR(sc, bix),
303155093Smarius			    (LE_LE32TOH(rmd.rmd2) & 0xfff) - ETHER_CRC_LEN);
304155093Smarius		}
305155093Smarius
306155093Smarius		rmd.rmd1 = LE_HTOLE32(LE_R1_OWN | LE_R1_ONES |
307155093Smarius		    (-LEBLEN & 0xfff));
308155093Smarius		rmd.rmd2 = 0;
309155093Smarius		rmd.rmd3 = 0;
310155093Smarius		(*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
311155093Smarius
312158663Smarius		if (++bix == sc->sc_nrbuf)
313158663Smarius			bix = 0;
314158663Smarius
315158663Smarius		if (m != NULL) {
316158663Smarius			ifp->if_ipackets++;
317158663Smarius
318158663Smarius#if defined(__i386__) && !defined(PC98)
319158663Smarius			/*
320158663Smarius			 * The VMware LANCE does not present IFF_SIMPLEX
321158663Smarius			 * behavior on multicast packets. Thus drop the
322158663Smarius			 * packet if it is from ourselves.
323158663Smarius			 */
324158663Smarius			eh = mtod(m, struct ether_header *);
325158663Smarius			if (!ether_cmp(eh->ether_shost, sc->sc_enaddr)) {
326158663Smarius				m_freem(m);
327158663Smarius				continue;
328158663Smarius			}
329155093Smarius#endif
330155093Smarius
331158663Smarius			/* Pass the packet up. */
332158663Smarius			LE_UNLOCK(sc);
333158663Smarius			(*ifp->if_input)(ifp, m);
334158663Smarius			LE_LOCK(sc);
335158663Smarius		} else
336158663Smarius			ifp->if_ierrors++;
337155093Smarius	}
338155093Smarius
339155093Smarius	sc->sc_last_rd = bix;
340155093Smarius}
341155093Smarius
342155093Smariusstatic inline void
343155093Smariusam79900_tint(struct lance_softc *sc)
344155093Smarius{
345155093Smarius	struct ifnet *ifp = sc->sc_ifp;
346155093Smarius	struct letmd tmd;
347155093Smarius	uint32_t tmd1, tmd2;
348155093Smarius	int bix;
349155093Smarius
350155093Smarius	bix = sc->sc_first_td;
351155093Smarius
352155093Smarius	for (;;) {
353155093Smarius		if (sc->sc_no_td <= 0)
354155093Smarius			break;
355155093Smarius
356155093Smarius		(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
357155093Smarius		    sizeof(tmd));
358155093Smarius
359158663Smarius		tmd1 = LE_LE32TOH(tmd.tmd1);
360158663Smarius
361155093Smarius#ifdef LEDEBUG
362155093Smarius		if (sc->sc_flags & LE_DEBUG)
363155093Smarius			if_printf(ifp, "trans tmd: "
364155093Smarius			    "adr %08x, flags/blen %08x\n",
365158663Smarius			    LE_LE32TOH(tmd.tmd0), tmd1);
366155093Smarius#endif
367155093Smarius
368155093Smarius		if (tmd1 & LE_T1_OWN)
369155093Smarius			break;
370155093Smarius
371155093Smarius		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
372155093Smarius
373155093Smarius		if (tmd1 & LE_T1_ERR) {
374158663Smarius			tmd2 = LE_LE32TOH(tmd.tmd2);
375155093Smarius			if (tmd2 & LE_T2_BUFF)
376155093Smarius				if_printf(ifp, "transmit buffer error\n");
377155093Smarius			else if (tmd2 & LE_T2_UFLO)
378155093Smarius				if_printf(ifp, "underflow\n");
379155093Smarius			if (tmd2 & (LE_T2_BUFF | LE_T2_UFLO)) {
380155093Smarius				lance_init_locked(sc);
381155093Smarius				return;
382155093Smarius			}
383155093Smarius			if (tmd2 & LE_T2_LCAR) {
384155093Smarius				if (sc->sc_flags & LE_CARRIER)
385155093Smarius					if_link_state_change(ifp,
386155093Smarius					    LINK_STATE_DOWN);
387155093Smarius				sc->sc_flags &= ~LE_CARRIER;
388155093Smarius				if (sc->sc_nocarrier)
389155093Smarius					(*sc->sc_nocarrier)(sc);
390155093Smarius				else
391155093Smarius					if_printf(ifp, "lost carrier\n");
392155093Smarius			}
393155093Smarius			if (tmd2 & LE_T2_LCOL)
394155093Smarius				ifp->if_collisions++;
395155093Smarius			if (tmd2 & LE_T2_RTRY) {
396155093Smarius#ifdef LEDEBUG
397155093Smarius				if_printf(ifp, "excessive collisions\n");
398155093Smarius#endif
399155093Smarius				ifp->if_collisions += 16;
400155093Smarius			}
401155093Smarius			ifp->if_oerrors++;
402155093Smarius		} else {
403155093Smarius			if (tmd1 & LE_T1_ONE)
404155093Smarius				ifp->if_collisions++;
405155093Smarius			else if (tmd1 & LE_T1_MORE)
406155093Smarius				/* Real number is unknown. */
407155093Smarius				ifp->if_collisions += 2;
408155093Smarius			ifp->if_opackets++;
409155093Smarius		}
410155093Smarius
411155093Smarius		if (++bix == sc->sc_ntbuf)
412155093Smarius			bix = 0;
413155093Smarius
414155093Smarius		--sc->sc_no_td;
415155093Smarius	}
416155093Smarius
417155093Smarius	sc->sc_first_td = bix;
418155093Smarius
419164933Smarius	sc->sc_wdog_timer = sc->sc_no_td > 0 ? 5 : 0;
420155093Smarius}
421155093Smarius
422155093Smarius/*
423155093Smarius * Controller interrupt
424155093Smarius */
425155093Smariusvoid
426155093Smariusam79900_intr(void *arg)
427155093Smarius{
428155093Smarius	struct lance_softc *sc = arg;
429155093Smarius	struct ifnet *ifp = sc->sc_ifp;
430155093Smarius	uint16_t isr;
431155093Smarius
432155093Smarius	LE_LOCK(sc);
433155093Smarius
434155093Smarius	if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) {
435155093Smarius		ifp->if_ierrors++;
436155093Smarius		lance_init_locked(sc);
437155093Smarius		LE_UNLOCK(sc);
438155093Smarius		return;
439155093Smarius	}
440155093Smarius
441155093Smarius	isr = (*sc->sc_rdcsr)(sc, LE_CSR0);
442155093Smarius#if defined(LEDEBUG) && LEDEBUG > 1
443155093Smarius	if (sc->sc_flags & LE_DEBUG)
444155093Smarius		if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr);
445155093Smarius#endif
446155093Smarius	if ((isr & LE_C0_INTR) == 0) {
447155093Smarius		LE_UNLOCK(sc);
448155093Smarius		return;
449155093Smarius	}
450155093Smarius
451155881Smarius	/*
452155881Smarius	 * Clear interrupt source flags and turn off interrupts. If we
453155881Smarius	 * don't clear these flags before processing their sources we
454158663Smarius	 * could completely miss some interrupt events as the NIC can
455174986Smarius	 * change these flags while we're in this handler. We toggle
456174986Smarius	 * the interrupt enable bit in order to keep receiving them
457174986Smarius	 * (some chips work without this, some don't).
458155881Smarius	 */
459155881Smarius	(*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD |
460155881Smarius	    LE_C0_STOP | LE_C0_STRT | LE_C0_INIT));
461155881Smarius
462155093Smarius	if (isr & LE_C0_ERR) {
463155093Smarius		if (isr & LE_C0_BABL) {
464155093Smarius#ifdef LEDEBUG
465155093Smarius			if_printf(ifp, "babble\n");
466155093Smarius#endif
467155093Smarius			ifp->if_oerrors++;
468155093Smarius		}
469155093Smarius#if 0
470155093Smarius		if (isr & LE_C0_CERR) {
471155093Smarius			if_printf(ifp, "collision error\n");
472155093Smarius			ifp->if_collisions++;
473155093Smarius		}
474155093Smarius#endif
475155093Smarius		if (isr & LE_C0_MISS) {
476155093Smarius#ifdef LEDEBUG
477155093Smarius			if_printf(ifp, "missed packet\n");
478155093Smarius#endif
479155093Smarius			ifp->if_ierrors++;
480155093Smarius		}
481155093Smarius		if (isr & LE_C0_MERR) {
482155093Smarius			if_printf(ifp, "memory error\n");
483155093Smarius			lance_init_locked(sc);
484155093Smarius			LE_UNLOCK(sc);
485155093Smarius			return;
486155093Smarius		}
487155093Smarius	}
488155093Smarius
489155093Smarius	if ((isr & LE_C0_RXON) == 0) {
490155093Smarius		if_printf(ifp, "receiver disabled\n");
491155093Smarius		ifp->if_ierrors++;
492155093Smarius		lance_init_locked(sc);
493155093Smarius		LE_UNLOCK(sc);
494155093Smarius		return;
495155093Smarius	}
496155093Smarius	if ((isr & LE_C0_TXON) == 0) {
497155093Smarius		if_printf(ifp, "transmitter disabled\n");
498155093Smarius		ifp->if_oerrors++;
499155093Smarius		lance_init_locked(sc);
500155093Smarius		LE_UNLOCK(sc);
501155093Smarius		return;
502155093Smarius	}
503155093Smarius
504155093Smarius	/*
505155093Smarius	 * Pretend we have carrier; if we don't this will be cleared shortly.
506155093Smarius	 */
507155093Smarius	if (!(sc->sc_flags & LE_CARRIER))
508155093Smarius		if_link_state_change(ifp, LINK_STATE_UP);
509155093Smarius	sc->sc_flags |= LE_CARRIER;
510155093Smarius
511155093Smarius	if (isr & LE_C0_RINT)
512155093Smarius		am79900_rint(sc);
513155093Smarius	if (isr & LE_C0_TINT)
514155093Smarius		am79900_tint(sc);
515155093Smarius
516155881Smarius	/* Enable interrupts again. */
517155881Smarius	(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA);
518155093Smarius
519155881Smarius	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
520155881Smarius		am79900_start_locked(sc);
521155881Smarius
522155093Smarius	LE_UNLOCK(sc);
523155093Smarius}
524155093Smarius
525155093Smarius/*
526155093Smarius * Set up output on interface.
527155093Smarius * Get another datagram to send off of the interface queue, and map it to the
528155093Smarius * interface before starting the output.
529155093Smarius */
530155093Smariusstatic void
531155093Smariusam79900_start_locked(struct lance_softc *sc)
532155093Smarius{
533155093Smarius	struct ifnet *ifp = sc->sc_ifp;
534155093Smarius	struct letmd tmd;
535155093Smarius	struct mbuf *m;
536155881Smarius	int bix, enq, len, rp;
537155093Smarius
538155093Smarius	LE_LOCK_ASSERT(sc, MA_OWNED);
539155093Smarius
540155093Smarius	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
541155093Smarius	    IFF_DRV_RUNNING)
542155093Smarius		return;
543155093Smarius
544155093Smarius	bix = sc->sc_last_td;
545155881Smarius	enq = 0;
546155093Smarius
547155093Smarius	for (; sc->sc_no_td < sc->sc_ntbuf &&
548155093Smarius	    !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) {
549155093Smarius		rp = LE_TMDADDR(sc, bix);
550155093Smarius		(*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
551155093Smarius
552155093Smarius		if (LE_LE32TOH(tmd.tmd1) & LE_T1_OWN) {
553155093Smarius			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
554155093Smarius			if_printf(ifp,
555155093Smarius			    "missing buffer, no_td = %d, last_td = %d\n",
556155093Smarius			    sc->sc_no_td, sc->sc_last_td);
557155093Smarius		}
558155093Smarius
559155093Smarius		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
560155093Smarius		if (m == 0)
561155093Smarius			break;
562155093Smarius
563155093Smarius		/*
564155093Smarius		 * If BPF is listening on this interface, let it see the packet
565155093Smarius		 * before we commit it to the wire.
566155093Smarius		 */
567155093Smarius		BPF_MTAP(ifp, m);
568155093Smarius
569155093Smarius		/*
570155093Smarius		 * Copy the mbuf chain into the transmit buffer.
571155093Smarius		 */
572155093Smarius		len = lance_put(sc, LE_TBUFADDR(sc, bix), m);
573155093Smarius
574155093Smarius#ifdef LEDEBUG
575155093Smarius		if (len > ETHERMTU + ETHER_HDR_LEN)
576155093Smarius			if_printf(ifp, "packet length %d\n", len);
577155093Smarius#endif
578155093Smarius
579155093Smarius		/*
580155093Smarius		 * Init transmit registers, and set transmit start flag.
581155093Smarius		 */
582155093Smarius		tmd.tmd1 = LE_HTOLE32(LE_T1_OWN | LE_T1_STP | LE_T1_ENP |
583155093Smarius		    LE_T1_ONES | (-len & 0xfff));
584155093Smarius		tmd.tmd2 = 0;
585155093Smarius		tmd.tmd3 = 0;
586155093Smarius
587155093Smarius		(*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
588155093Smarius
589155093Smarius#ifdef LEDEBUG
590155093Smarius		if (sc->sc_flags & LE_DEBUG)
591158663Smarius			am79900_xmit_print(sc, bix);
592155093Smarius#endif
593155093Smarius
594155093Smarius		(*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
595155881Smarius		enq++;
596155093Smarius
597155093Smarius		if (++bix == sc->sc_ntbuf)
598155093Smarius			bix = 0;
599155093Smarius
600155093Smarius		if (++sc->sc_no_td == sc->sc_ntbuf) {
601155093Smarius			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
602155093Smarius			break;
603155093Smarius		}
604155093Smarius	}
605155093Smarius
606155093Smarius	sc->sc_last_td = bix;
607155881Smarius
608155881Smarius	if (enq > 0)
609164933Smarius		sc->sc_wdog_timer = 5;
610155093Smarius}
611155093Smarius
612155093Smarius#ifdef LEDEBUG
613155093Smariusstatic void
614155093Smariusam79900_recv_print(struct lance_softc *sc, int no)
615155093Smarius{
616155093Smarius	struct ifnet *ifp = sc->sc_ifp;
617155093Smarius	struct ether_header eh;
618155093Smarius	struct lermd rmd;
619155093Smarius	uint16_t len;
620155093Smarius
621155093Smarius	(*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
622155093Smarius	len = LE_LE32TOH(rmd.rmd2) & 0xfff;
623155093Smarius	if_printf(ifp, "receive buffer %d, len = %d\n", no, len);
624155093Smarius	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
625155093Smarius	if_printf(ifp, "adr %08x, flags/blen %08x\n", LE_LE32TOH(rmd.rmd0),
626155093Smarius	    LE_LE32TOH(rmd.rmd1));
627155093Smarius	if (len - ETHER_CRC_LEN >= sizeof(eh)) {
628155093Smarius		(*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
629155093Smarius		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
630155093Smarius		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
631155093Smarius		    ntohs(eh.ether_type));
632155093Smarius	}
633155093Smarius}
634155093Smarius
635155093Smariusstatic void
636155093Smariusam79900_xmit_print(struct lance_softc *sc, int no)
637155093Smarius{
638155093Smarius	struct ifnet *ifp = sc->sc_ifp;
639155093Smarius	struct ether_header eh;
640155093Smarius	struct letmd tmd;
641155093Smarius	uint16_t len;
642155093Smarius
643155093Smarius	(*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
644155093Smarius	len = -(LE_LE32TOH(tmd.tmd1) & 0xfff);
645155093Smarius	if_printf(ifp, "transmit buffer %d, len = %d\n", no, len);
646155093Smarius	if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0));
647155093Smarius	if_printf(ifp, "adr %08x, flags/blen %08x\n", LE_LE32TOH(tmd.tmd0),
648155093Smarius	    LE_LE32TOH(tmd.tmd1));
649155093Smarius	if (len >= sizeof(eh)) {
650155093Smarius		(*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
651155093Smarius		if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost));
652155093Smarius		printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
653155093Smarius		    ntohs(eh.ether_type));
654155093Smarius	}
655155093Smarius}
656155093Smarius#endif /* LEDEBUG */
657