am7990.c revision 174986
1155093Smarius/* $NetBSD: am7990.c,v 1.68 2005/12/11 12:21:25 christos Exp $ */ 2155093Smarius 3155093Smarius/*- 4155093Smarius * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5155093Smarius * All rights reserved. 6155093Smarius * 7155093Smarius * This code is derived from software contributed to The NetBSD Foundation 8155093Smarius * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace 9155093Smarius * Simulation Facility, NASA Ames Research Center. 10155093Smarius * 11155093Smarius * Redistribution and use in source and binary forms, with or without 12155093Smarius * modification, are permitted provided that the following conditions 13155093Smarius * are met: 14155093Smarius * 1. Redistributions of source code must retain the above copyright 15155093Smarius * notice, this list of conditions and the following disclaimer. 16155093Smarius * 2. Redistributions in binary form must reproduce the above copyright 17155093Smarius * notice, this list of conditions and the following disclaimer in the 18155093Smarius * documentation and/or other materials provided with the distribution. 19155093Smarius * 3. All advertising materials mentioning features or use of this software 20155093Smarius * must display the following acknowledgement: 21155093Smarius * This product includes software developed by the NetBSD 22155093Smarius * Foundation, Inc. and its contributors. 23155093Smarius * 4. Neither the name of The NetBSD Foundation nor the names of its 24155093Smarius * contributors may be used to endorse or promote products derived 25155093Smarius * from this software without specific prior written permission. 26155093Smarius * 27155093Smarius * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28155093Smarius * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29155093Smarius * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30155093Smarius * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31155093Smarius * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32155093Smarius * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33155093Smarius * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34155093Smarius * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35155093Smarius * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36155093Smarius * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37155093Smarius * POSSIBILITY OF SUCH DAMAGE. 38155093Smarius */ 39155093Smarius 40155093Smarius/*- 41155093Smarius * Copyright (c) 1992, 1993 42155093Smarius * The Regents of the University of California. All rights reserved. 43155093Smarius * 44155093Smarius * This code is derived from software contributed to Berkeley by 45155093Smarius * Ralph Campbell and Rick Macklem. 46155093Smarius * 47155093Smarius * Redistribution and use in source and binary forms, with or without 48155093Smarius * modification, are permitted provided that the following conditions 49155093Smarius * are met: 50155093Smarius * 1. Redistributions of source code must retain the above copyright 51155093Smarius * notice, this list of conditions and the following disclaimer. 52155093Smarius * 2. Redistributions in binary form must reproduce the above copyright 53155093Smarius * notice, this list of conditions and the following disclaimer in the 54155093Smarius * documentation and/or other materials provided with the distribution. 55155093Smarius * 3. Neither the name of the University nor the names of its contributors 56155093Smarius * may be used to endorse or promote products derived from this software 57155093Smarius * without specific prior written permission. 58155093Smarius * 59155093Smarius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60155093Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61155093Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62155093Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63155093Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64155093Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65155093Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66155093Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67155093Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68155093Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69155093Smarius * SUCH DAMAGE. 70155093Smarius * 71155093Smarius * @(#)if_le.c 8.2 (Berkeley) 11/16/93 72155093Smarius */ 73155093Smarius 74155093Smarius#include <sys/cdefs.h> 75155093Smarius__FBSDID("$FreeBSD: head/sys/dev/le/am7990.c 174986 2007-12-30 00:23:38Z marius $"); 76155093Smarius 77155093Smarius#include <sys/param.h> 78155093Smarius#include <sys/bus.h> 79155093Smarius#include <sys/endian.h> 80155093Smarius#include <sys/lock.h> 81155093Smarius#include <sys/mbuf.h> 82155093Smarius#include <sys/mutex.h> 83155093Smarius#include <sys/socket.h> 84155093Smarius 85155093Smarius#include <net/bpf.h> 86155093Smarius#include <net/ethernet.h> 87155093Smarius#include <net/if.h> 88155093Smarius#include <net/if_arp.h> 89155093Smarius#include <net/if_dl.h> 90155093Smarius#include <net/if_media.h> 91155093Smarius#include <net/if_var.h> 92155093Smarius 93158663Smarius#include <machine/bus.h> 94158663Smarius 95155093Smarius#include <dev/le/lancereg.h> 96155093Smarius#include <dev/le/lancevar.h> 97155093Smarius#include <dev/le/am7990reg.h> 98155093Smarius#include <dev/le/am7990var.h> 99155093Smarius 100155093Smariusstatic void am7990_meminit(struct lance_softc *); 101155093Smariusstatic void am7990_rint(struct lance_softc *); 102155093Smariusstatic void am7990_tint(struct lance_softc *); 103155093Smariusstatic void am7990_start_locked(struct lance_softc *sc); 104155093Smarius 105155093Smarius#ifdef LEDEBUG 106155093Smariusstatic void am7990_recv_print(struct lance_softc *, int); 107155093Smariusstatic void am7990_xmit_print(struct lance_softc *, int); 108155093Smarius#endif 109155093Smarius 110155093Smariusint 111155093Smariusam7990_config(struct am7990_softc *sc, const char* name, int unit) 112155093Smarius{ 113155093Smarius int error, mem; 114155093Smarius 115155093Smarius sc->lsc.sc_meminit = am7990_meminit; 116155093Smarius sc->lsc.sc_start_locked = am7990_start_locked; 117155093Smarius 118155093Smarius error = lance_config(&sc->lsc, name, unit); 119155093Smarius if (error != 0) 120155093Smarius return (error); 121155093Smarius 122155093Smarius mem = 0; 123155093Smarius sc->lsc.sc_initaddr = mem; 124155093Smarius mem += sizeof(struct leinit); 125155093Smarius sc->lsc.sc_rmdaddr = mem; 126155093Smarius mem += sizeof(struct lermd) * sc->lsc.sc_nrbuf; 127155093Smarius sc->lsc.sc_tmdaddr = mem; 128155093Smarius mem += sizeof(struct letmd) * sc->lsc.sc_ntbuf; 129155093Smarius sc->lsc.sc_rbufaddr = mem; 130155093Smarius mem += LEBLEN * sc->lsc.sc_nrbuf; 131155093Smarius sc->lsc.sc_tbufaddr = mem; 132155093Smarius mem += LEBLEN * sc->lsc.sc_ntbuf; 133155093Smarius 134155093Smarius if (mem > sc->lsc.sc_memsize) 135155093Smarius panic("%s: memsize", __func__); 136155093Smarius 137155093Smarius lance_attach(&sc->lsc); 138155093Smarius 139155093Smarius return (0); 140155093Smarius} 141155093Smarius 142155093Smariusvoid 143155093Smariusam7990_detach(struct am7990_softc *sc) 144155093Smarius{ 145155093Smarius 146155093Smarius lance_detach(&sc->lsc); 147155093Smarius} 148155093Smarius 149155093Smarius/* 150155093Smarius * Set up the initialization block and the descriptor rings. 151155093Smarius */ 152155093Smariusstatic void 153155093Smariusam7990_meminit(struct lance_softc *sc) 154155093Smarius{ 155155093Smarius struct ifnet *ifp = sc->sc_ifp; 156155093Smarius struct leinit init; 157155093Smarius struct lermd rmd; 158155093Smarius struct letmd tmd; 159155093Smarius u_long a; 160155093Smarius int bix; 161155093Smarius 162155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 163155093Smarius 164155093Smarius if (ifp->if_flags & IFF_PROMISC) 165155093Smarius init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM; 166155093Smarius else 167155093Smarius init.init_mode = LE_MODE_NORMAL; 168155093Smarius 169155093Smarius init.init_padr[0] = (sc->sc_enaddr[1] << 8) | sc->sc_enaddr[0]; 170155093Smarius init.init_padr[1] = (sc->sc_enaddr[3] << 8) | sc->sc_enaddr[2]; 171155093Smarius init.init_padr[2] = (sc->sc_enaddr[5] << 8) | sc->sc_enaddr[4]; 172155093Smarius lance_setladrf(sc, init.init_ladrf); 173155093Smarius 174155093Smarius sc->sc_last_rd = 0; 175155093Smarius sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0; 176155093Smarius 177155093Smarius a = sc->sc_addr + LE_RMDADDR(sc, 0); 178155093Smarius init.init_rdra = a; 179155093Smarius init.init_rlen = (a >> 16) | ((ffs(sc->sc_nrbuf) - 1) << 13); 180155093Smarius 181155093Smarius a = sc->sc_addr + LE_TMDADDR(sc, 0); 182155093Smarius init.init_tdra = a; 183155093Smarius init.init_tlen = (a >> 16) | ((ffs(sc->sc_ntbuf) - 1) << 13); 184155093Smarius 185155093Smarius (*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init)); 186155093Smarius 187155093Smarius /* 188155093Smarius * Set up receive ring descriptors. 189155093Smarius */ 190155093Smarius for (bix = 0; bix < sc->sc_nrbuf; bix++) { 191155093Smarius a = sc->sc_addr + LE_RBUFADDR(sc, bix); 192155093Smarius rmd.rmd0 = a; 193155093Smarius rmd.rmd1_hadr = a >> 16; 194155093Smarius rmd.rmd1_bits = LE_R1_OWN; 195155093Smarius rmd.rmd2 = -LEBLEN | LE_XMD2_ONES; 196155093Smarius rmd.rmd3 = 0; 197155093Smarius (*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix), 198155093Smarius sizeof(rmd)); 199155093Smarius } 200155093Smarius 201155093Smarius /* 202155093Smarius * Set up transmit ring descriptors. 203155093Smarius */ 204155093Smarius for (bix = 0; bix < sc->sc_ntbuf; bix++) { 205155093Smarius a = sc->sc_addr + LE_TBUFADDR(sc, bix); 206155093Smarius tmd.tmd0 = a; 207155093Smarius tmd.tmd1_hadr = a >> 16; 208155093Smarius tmd.tmd1_bits = 0; 209155093Smarius tmd.tmd2 = LE_XMD2_ONES; 210155093Smarius tmd.tmd3 = 0; 211155093Smarius (*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix), 212155093Smarius sizeof(tmd)); 213155093Smarius } 214155093Smarius} 215155093Smarius 216155093Smariusstatic void 217155093Smariusam7990_rint(struct lance_softc *sc) 218155093Smarius{ 219155093Smarius struct ifnet *ifp = sc->sc_ifp; 220158663Smarius struct mbuf *m; 221155093Smarius struct lermd rmd; 222155093Smarius int bix, rp; 223158663Smarius#if defined(LANCE_REVC_BUG) 224158663Smarius struct ether_header *eh; 225158663Smarius /* Make sure this is short-aligned, for ether_cmp(). */ 226158663Smarius static uint16_t bcast_enaddr[3] = { ~0, ~0, ~0 }; 227158663Smarius#endif 228155093Smarius 229155093Smarius bix = sc->sc_last_rd; 230155093Smarius 231155093Smarius /* Process all buffers with valid data. */ 232155093Smarius for (;;) { 233155093Smarius rp = LE_RMDADDR(sc, bix); 234155093Smarius (*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd)); 235155093Smarius 236155093Smarius if (rmd.rmd1_bits & LE_R1_OWN) 237155093Smarius break; 238155093Smarius 239158663Smarius m = NULL; 240158663Smarius if ((rmd.rmd1_bits & (LE_R1_ERR | LE_R1_STP | LE_R1_ENP)) != 241158663Smarius (LE_R1_STP | LE_R1_ENP)) { 242158663Smarius if (rmd.rmd1_bits & LE_R1_ERR) { 243155093Smarius#ifdef LEDEBUG 244158663Smarius if (rmd.rmd1_bits & LE_R1_ENP) { 245158663Smarius if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) { 246158663Smarius if (rmd.rmd1_bits & LE_R1_FRAM) 247158663Smarius if_printf(ifp, 248158663Smarius "framing error\n"); 249158663Smarius if (rmd.rmd1_bits & LE_R1_CRC) 250158663Smarius if_printf(ifp, 251158663Smarius "crc mismatch\n"); 252158663Smarius } 253158663Smarius } else 254158663Smarius if (rmd.rmd1_bits & LE_R1_OFLO) 255158663Smarius if_printf(ifp, "overflow\n"); 256155093Smarius#endif 257158663Smarius if (rmd.rmd1_bits & LE_R1_BUFF) 258158663Smarius if_printf(ifp, 259158663Smarius "receive buffer error\n"); 260158663Smarius } else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) != 261158663Smarius (LE_R1_STP | LE_R1_ENP)) 262158663Smarius if_printf(ifp, "dropping chained buffer\n"); 263155093Smarius } else { 264155093Smarius#ifdef LEDEBUG 265155093Smarius if (sc->sc_flags & LE_DEBUG) 266158663Smarius am7990_recv_print(sc, bix); 267155093Smarius#endif 268158663Smarius /* Pull the packet off the interface. */ 269158663Smarius m = lance_get(sc, LE_RBUFADDR(sc, bix), 270155093Smarius (int)rmd.rmd3 - ETHER_CRC_LEN); 271155093Smarius } 272155093Smarius 273155093Smarius rmd.rmd1_bits = LE_R1_OWN; 274155093Smarius rmd.rmd2 = -LEBLEN | LE_XMD2_ONES; 275155093Smarius rmd.rmd3 = 0; 276155093Smarius (*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd)); 277155093Smarius 278158663Smarius if (++bix == sc->sc_nrbuf) 279158663Smarius bix = 0; 280158663Smarius 281158663Smarius if (m != NULL) { 282158663Smarius ifp->if_ipackets++; 283158663Smarius 284158663Smarius#ifdef LANCE_REVC_BUG 285158663Smarius /* 286158663Smarius * The old LANCE (Rev. C) chips have a bug which 287158663Smarius * causes garbage to be inserted in front of the 288158663Smarius * received packet. The workaround is to ignore 289158663Smarius * packets with an invalid destination address 290158663Smarius * (garbage will usually not match). 291158663Smarius * Of course, this precludes multicast support... 292158663Smarius */ 293158663Smarius eh = mtod(m, struct ether_header *); 294158663Smarius if (ether_cmp(eh->ether_dhost, sc->sc_enaddr) && 295158663Smarius ether_cmp(eh->ether_dhost, bcast_enaddr)) { 296158663Smarius m_freem(m); 297158663Smarius continue; 298158663Smarius } 299155093Smarius#endif 300155093Smarius 301158663Smarius /* Pass the packet up. */ 302158663Smarius LE_UNLOCK(sc); 303158663Smarius (*ifp->if_input)(ifp, m); 304158663Smarius LE_LOCK(sc); 305158663Smarius } else 306158663Smarius ifp->if_ierrors++; 307155093Smarius } 308155093Smarius 309155093Smarius sc->sc_last_rd = bix; 310155093Smarius} 311155093Smarius 312155093Smariusstatic void 313155093Smariusam7990_tint(struct lance_softc *sc) 314155093Smarius{ 315155093Smarius struct ifnet *ifp = sc->sc_ifp; 316155093Smarius struct letmd tmd; 317155093Smarius int bix; 318155093Smarius 319155093Smarius bix = sc->sc_first_td; 320155093Smarius 321155093Smarius for (;;) { 322155093Smarius if (sc->sc_no_td <= 0) 323155093Smarius break; 324155093Smarius 325155093Smarius (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix), 326155093Smarius sizeof(tmd)); 327155093Smarius 328155093Smarius#ifdef LEDEBUG 329155093Smarius if (sc->sc_flags & LE_DEBUG) 330155093Smarius if_printf(ifp, "trans tmd: " 331155093Smarius "ladr %04x, hadr %02x, flags %02x, " 332155093Smarius "bcnt %04x, mcnt %04x\n", 333155093Smarius tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, 334155093Smarius tmd.tmd2, tmd.tmd3); 335155093Smarius#endif 336155093Smarius 337155093Smarius if (tmd.tmd1_bits & LE_T1_OWN) 338155093Smarius break; 339155093Smarius 340155093Smarius ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 341155093Smarius 342155093Smarius if (tmd.tmd1_bits & LE_T1_ERR) { 343155093Smarius if (tmd.tmd3 & LE_T3_BUFF) 344155093Smarius if_printf(ifp, "transmit buffer error\n"); 345155093Smarius else if (tmd.tmd3 & LE_T3_UFLO) 346155093Smarius if_printf(ifp, "underflow\n"); 347155093Smarius if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) { 348155093Smarius lance_init_locked(sc); 349155093Smarius return; 350155093Smarius } 351155093Smarius if (tmd.tmd3 & LE_T3_LCAR) { 352155093Smarius if (sc->sc_flags & LE_CARRIER) 353155093Smarius if_link_state_change(ifp, 354155093Smarius LINK_STATE_DOWN); 355155093Smarius sc->sc_flags &= ~LE_CARRIER; 356155093Smarius if (sc->sc_nocarrier) 357155093Smarius (*sc->sc_nocarrier)(sc); 358155093Smarius else 359155093Smarius if_printf(ifp, "lost carrier\n"); 360155093Smarius } 361155093Smarius if (tmd.tmd3 & LE_T3_LCOL) 362155093Smarius ifp->if_collisions++; 363155093Smarius if (tmd.tmd3 & LE_T3_RTRY) { 364155093Smarius#ifdef LEDEBUG 365155093Smarius if_printf(ifp, "excessive collisions, tdr %d\n", 366155093Smarius tmd.tmd3 & LE_T3_TDR_MASK); 367155093Smarius#endif 368155093Smarius ifp->if_collisions += 16; 369155093Smarius } 370155093Smarius ifp->if_oerrors++; 371155093Smarius } else { 372155093Smarius if (tmd.tmd1_bits & LE_T1_ONE) 373155093Smarius ifp->if_collisions++; 374155093Smarius else if (tmd.tmd1_bits & LE_T1_MORE) 375155093Smarius /* Real number is unknown. */ 376155093Smarius ifp->if_collisions += 2; 377155093Smarius ifp->if_opackets++; 378155093Smarius } 379155093Smarius 380155093Smarius if (++bix == sc->sc_ntbuf) 381155093Smarius bix = 0; 382155093Smarius 383155093Smarius --sc->sc_no_td; 384155093Smarius } 385155093Smarius 386155093Smarius sc->sc_first_td = bix; 387155093Smarius 388164933Smarius sc->sc_wdog_timer = sc->sc_no_td > 0 ? 5 : 0; 389155093Smarius} 390155093Smarius 391155093Smarius/* 392155093Smarius * Controller interrupt 393155093Smarius */ 394155093Smariusvoid 395155093Smariusam7990_intr(void *arg) 396155093Smarius{ 397155093Smarius struct lance_softc *sc = arg; 398155093Smarius struct ifnet *ifp = sc->sc_ifp; 399155093Smarius uint16_t isr; 400155093Smarius 401155093Smarius LE_LOCK(sc); 402155093Smarius 403155093Smarius if (sc->sc_hwintr && (*sc->sc_hwintr)(sc) == -1) { 404155093Smarius ifp->if_ierrors++; 405155093Smarius lance_init_locked(sc); 406155093Smarius LE_UNLOCK(sc); 407155093Smarius return; 408155093Smarius } 409155093Smarius 410155093Smarius isr = (*sc->sc_rdcsr)(sc, LE_CSR0); 411155093Smarius#if defined(LEDEBUG) && LEDEBUG > 1 412155093Smarius if (sc->sc_flags & LE_DEBUG) 413155093Smarius if_printf(ifp, "%s: entering with isr=%04x\n", __func__, isr); 414155093Smarius#endif 415155093Smarius if ((isr & LE_C0_INTR) == 0) { 416155093Smarius LE_UNLOCK(sc); 417155093Smarius return; 418155093Smarius } 419155093Smarius 420155881Smarius /* 421155881Smarius * Clear interrupt source flags and turn off interrupts. If we 422155881Smarius * don't clear these flags before processing their sources we 423158663Smarius * could completely miss some interrupt events as the NIC can 424174986Smarius * change these flags while we're in this handler. We toggle 425174986Smarius * the interrupt enable bit in order to keep receiving them 426174986Smarius * (some chips work without this, some don't). 427155881Smarius */ 428155881Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, isr & ~(LE_C0_INEA | LE_C0_TDMD | 429155881Smarius LE_C0_STOP | LE_C0_STRT | LE_C0_INIT)); 430155881Smarius 431155093Smarius if (isr & LE_C0_ERR) { 432155093Smarius if (isr & LE_C0_BABL) { 433155093Smarius#ifdef LEDEBUG 434155093Smarius if_printf(ifp, "babble\n"); 435155093Smarius#endif 436155093Smarius ifp->if_oerrors++; 437155093Smarius } 438155093Smarius#if 0 439155093Smarius if (isr & LE_C0_CERR) { 440155093Smarius if_printf(ifp, "collision error\n"); 441155093Smarius ifp->if_collisions++; 442155093Smarius } 443155093Smarius#endif 444155093Smarius if (isr & LE_C0_MISS) { 445155093Smarius#ifdef LEDEBUG 446155093Smarius if_printf(ifp, "missed packet\n"); 447155093Smarius#endif 448155093Smarius ifp->if_ierrors++; 449155093Smarius } 450155093Smarius if (isr & LE_C0_MERR) { 451155093Smarius if_printf(ifp, "memory error\n"); 452155093Smarius lance_init_locked(sc); 453155093Smarius LE_UNLOCK(sc); 454155093Smarius return; 455155093Smarius } 456155093Smarius } 457155093Smarius 458155093Smarius if ((isr & LE_C0_RXON) == 0) { 459155093Smarius if_printf(ifp, "receiver disabled\n"); 460155093Smarius ifp->if_ierrors++; 461155093Smarius lance_init_locked(sc); 462155093Smarius LE_UNLOCK(sc); 463155093Smarius return; 464155093Smarius } 465155093Smarius if ((isr & LE_C0_TXON) == 0) { 466155093Smarius if_printf(ifp, "transmitter disabled\n"); 467155093Smarius ifp->if_oerrors++; 468155093Smarius lance_init_locked(sc); 469155093Smarius LE_UNLOCK(sc); 470155093Smarius return; 471155093Smarius } 472155093Smarius 473155093Smarius /* 474155093Smarius * Pretend we have carrier; if we don't this will be cleared shortly. 475155093Smarius */ 476155093Smarius if (!(sc->sc_flags & LE_CARRIER)) 477155093Smarius if_link_state_change(ifp, LINK_STATE_UP); 478155093Smarius sc->sc_flags |= LE_CARRIER; 479155093Smarius 480155093Smarius if (isr & LE_C0_RINT) 481155093Smarius am7990_rint(sc); 482155093Smarius if (isr & LE_C0_TINT) 483155093Smarius am7990_tint(sc); 484155093Smarius 485155881Smarius /* Enable interrupts again. */ 486155881Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA); 487155093Smarius 488155881Smarius if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 489155881Smarius am7990_start_locked(sc); 490155881Smarius 491155093Smarius LE_UNLOCK(sc); 492155093Smarius} 493155093Smarius 494155093Smarius/* 495155093Smarius * Set up output on interface. 496155093Smarius * Get another datagram to send off of the interface queue, and map it to the 497155093Smarius * interface before starting the output. 498155093Smarius */ 499155093Smariusstatic void 500155093Smariusam7990_start_locked(struct lance_softc *sc) 501155093Smarius{ 502155093Smarius struct ifnet *ifp = sc->sc_ifp; 503155093Smarius struct letmd tmd; 504155093Smarius struct mbuf *m; 505155881Smarius int bix, enq, len, rp; 506155093Smarius 507155093Smarius LE_LOCK_ASSERT(sc, MA_OWNED); 508155093Smarius 509155093Smarius if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 510155093Smarius IFF_DRV_RUNNING) 511155093Smarius return; 512155093Smarius 513155093Smarius bix = sc->sc_last_td; 514155881Smarius enq = 0; 515155093Smarius 516155093Smarius for (; sc->sc_no_td < sc->sc_ntbuf && 517155093Smarius !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { 518155093Smarius rp = LE_TMDADDR(sc, bix); 519155093Smarius (*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd)); 520155093Smarius 521155093Smarius if (tmd.tmd1_bits & LE_T1_OWN) { 522155093Smarius ifp->if_drv_flags |= IFF_DRV_OACTIVE; 523155093Smarius if_printf(ifp, 524155093Smarius "missing buffer, no_td = %d, last_td = %d\n", 525155093Smarius sc->sc_no_td, sc->sc_last_td); 526155093Smarius } 527155093Smarius 528155093Smarius IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 529155093Smarius if (m == 0) 530155093Smarius break; 531155093Smarius 532155093Smarius /* 533155093Smarius * If BPF is listening on this interface, let it see the packet 534155093Smarius * before we commit it to the wire. 535155093Smarius */ 536155093Smarius BPF_MTAP(ifp, m); 537155093Smarius 538155093Smarius /* 539155093Smarius * Copy the mbuf chain into the transmit buffer. 540155093Smarius */ 541155093Smarius len = lance_put(sc, LE_TBUFADDR(sc, bix), m); 542155093Smarius 543155093Smarius#ifdef LEDEBUG 544155093Smarius if (len > ETHERMTU + ETHER_HDR_LEN) 545155093Smarius if_printf(ifp, "packet length %d\n", len); 546155093Smarius#endif 547155093Smarius 548155093Smarius /* 549155093Smarius * Init transmit registers, and set transmit start flag. 550155093Smarius */ 551155093Smarius tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP; 552155093Smarius tmd.tmd2 = -len | LE_XMD2_ONES; 553155093Smarius tmd.tmd3 = 0; 554155093Smarius 555155093Smarius (*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd)); 556155093Smarius 557155093Smarius#ifdef LEDEBUG 558155093Smarius if (sc->sc_flags & LE_DEBUG) 559158663Smarius am7990_xmit_print(sc, bix); 560155093Smarius#endif 561155093Smarius 562155093Smarius (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD); 563155881Smarius enq++; 564155093Smarius 565155093Smarius if (++bix == sc->sc_ntbuf) 566155093Smarius bix = 0; 567155093Smarius 568155093Smarius if (++sc->sc_no_td == sc->sc_ntbuf) { 569155093Smarius ifp->if_drv_flags |= IFF_DRV_OACTIVE; 570155093Smarius break; 571155093Smarius } 572155093Smarius } 573155093Smarius 574155093Smarius sc->sc_last_td = bix; 575155881Smarius 576155881Smarius if (enq > 0) 577164933Smarius sc->sc_wdog_timer = 5; 578155093Smarius} 579155093Smarius 580155093Smarius#ifdef LEDEBUG 581155093Smariusstatic void 582155093Smariusam7990_recv_print(struct lance_softc *sc, int no) 583155093Smarius{ 584155093Smarius struct ifnet *ifp = sc->sc_ifp; 585155093Smarius struct ether_header eh; 586155093Smarius struct lermd rmd; 587155093Smarius uint16_t len; 588155093Smarius 589155093Smarius (*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd)); 590155093Smarius len = rmd.rmd3; 591155093Smarius if_printf(ifp, "receive buffer %d, len = %d\n", no, len); 592155093Smarius if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); 593155093Smarius if_printf(ifp, 594155093Smarius "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n", 595155093Smarius rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3); 596155093Smarius if (len - ETHER_CRC_LEN >= sizeof(eh)) { 597155093Smarius (*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh)); 598155093Smarius if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); 599155093Smarius printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), 600155093Smarius ntohs(eh.ether_type)); 601155093Smarius } 602155093Smarius} 603155093Smarius 604155093Smariusstatic void 605155093Smariusam7990_xmit_print(struct lance_softc *sc, int no) 606155093Smarius{ 607155093Smarius struct ifnet *ifp = sc->sc_ifp; 608155093Smarius struct ether_header eh; 609155093Smarius struct letmd tmd; 610155093Smarius uint16_t len; 611155093Smarius 612155093Smarius (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd)); 613155093Smarius len = -tmd.tmd2; 614155093Smarius if_printf(ifp, "transmit buffer %d, len = %d\n", no, len); 615155093Smarius if_printf(ifp, "status %04x\n", (*sc->sc_rdcsr)(sc, LE_CSR0)); 616155093Smarius if_printf(ifp, 617155093Smarius "ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n", 618155093Smarius tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3); 619155093Smarius if (len >= sizeof(eh)) { 620155093Smarius (*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh)); 621155093Smarius if_printf(ifp, "dst %s", ether_sprintf(eh.ether_dhost)); 622155093Smarius printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost), 623155093Smarius ntohs(eh.ether_type)); 624155093Smarius } 625155093Smarius} 626155093Smarius#endif /* LEDEBUG */ 627