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