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