if_wb.c revision 254842
1193326Sed/*- 2193326Sed * Copyright (c) 1997, 1998 3193326Sed * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4193326Sed * 5193326Sed * Redistribution and use in source and binary forms, with or without 6193326Sed * modification, are permitted provided that the following conditions 7193326Sed * are met: 8193326Sed * 1. Redistributions of source code must retain the above copyright 9261991Sdim * notice, this list of conditions and the following disclaimer. 10261991Sdim * 2. Redistributions in binary form must reproduce the above copyright 11261991Sdim * notice, this list of conditions and the following disclaimer in the 12261991Sdim * documentation and/or other materials provided with the distribution. 13193326Sed * 3. All advertising materials mentioning features or use of this software 14193326Sed * must display the following acknowledgement: 15193326Sed * This product includes software developed by Bill Paul. 16193326Sed * 4. Neither the name of the author nor the names of any co-contributors 17193326Sed * may be used to endorse or promote products derived from this software 18239462Sdim * without specific prior written permission. 19193326Sed * 20280031Sdim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22202879Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23234353Sdim * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24234353Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25234353Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26193326Sed * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27193326Sed * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28193326Sed * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29218893Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30218893Sdim * THE POSSIBILITY OF SUCH DAMAGE. 31218893Sdim */ 32218893Sdim 33251662Sdim#include <sys/cdefs.h> 34218893Sdim__FBSDID("$FreeBSD: head/sys/dev/wb/if_wb.c 254842 2013-08-25 10:57:09Z andre $"); 35249423Sdim 36193326Sed/* 37193326Sed * Winbond fast ethernet PCI NIC driver 38193326Sed * 39193326Sed * Supports various cheap network adapters based on the Winbond W89C840F 40193326Sed * fast ethernet controller chip. This includes adapters manufactured by 41193326Sed * Winbond itself and some made by Linksys. 42193326Sed * 43193326Sed * Written by Bill Paul <wpaul@ctr.columbia.edu> 44193326Sed * Electrical Engineering Department 45193326Sed * Columbia University, New York City 46193326Sed */ 47193326Sed/* 48193326Sed * The Winbond W89C840F chip is a bus master; in some ways it resembles 49193326Sed * a DEC 'tulip' chip, only not as complicated. Unfortunately, it has 50193326Sed * one major difference which is that while the registers do many of 51193326Sed * the same things as a tulip adapter, the offsets are different: where 52193326Sed * tulip registers are typically spaced 8 bytes apart, the Winbond 53193326Sed * registers are spaced 4 bytes apart. The receiver filter is also 54193326Sed * programmed differently. 55193326Sed * 56193326Sed * Like the tulip, the Winbond chip uses small descriptors containing 57239462Sdim * a status word, a control word and 32-bit areas that can either be used 58193326Sed * to point to two external data blocks, or to point to a single block 59243830Sdim * and another descriptor in a linked list. Descriptors can be grouped 60243830Sdim * together in blocks to form fixed length rings or can be chained 61243830Sdim * together in linked lists. A single packet may be spread out over 62243830Sdim * several descriptors if necessary. 63243830Sdim * 64239462Sdim * For the receive ring, this driver uses a linked list of descriptors, 65193326Sed * each pointing to a single mbuf cluster buffer, which us large enough 66198092Srdivacky * to hold an entire packet. The link list is looped back to created a 67243830Sdim * closed ring. 68243830Sdim * 69243830Sdim * For transmission, the driver creates a linked list of 'super descriptors' 70218893Sdim * which each contain several individual descriptors linked toghether. 71243830Sdim * Each 'super descriptor' contains WB_MAXFRAGS descriptors, which we 72239462Sdim * abuse as fragment pointers. This allows us to use a buffer managment 73239462Sdim * scheme very similar to that used in the ThunderLAN and Etherlink XL 74198092Srdivacky * drivers. 75198092Srdivacky * 76193326Sed * Autonegotiation is performed using the external PHY via the MII bus. 77198092Srdivacky * The sample boards I have all use a Davicom PHY. 78261991Sdim * 79193326Sed * Note: the author of the Linux driver for the Winbond chip alludes 80193326Sed * to some sort of flaw in the chip's design that seems to mandate some 81193326Sed * drastic workaround which signigicantly impairs transmit performance. 82261991Sdim * I have no idea what he's on about: transmit performance with all 83261991Sdim * three of my test boards seems fine. 84261991Sdim */ 85261991Sdim 86261991Sdim#include <sys/param.h> 87193326Sed#include <sys/systm.h> 88193326Sed#include <sys/sockio.h> 89288943Sdim#include <sys/mbuf.h> 90288943Sdim#include <sys/malloc.h> 91288943Sdim#include <sys/module.h> 92288943Sdim#include <sys/kernel.h> 93288943Sdim#include <sys/socket.h> 94288943Sdim#include <sys/queue.h> 95288943Sdim 96249423Sdim#include <net/if.h> 97249423Sdim#include <net/if_arp.h> 98239462Sdim#include <net/ethernet.h> 99198092Srdivacky#include <net/if_dl.h> 100198092Srdivacky#include <net/if_media.h> 101198092Srdivacky#include <net/if_types.h> 102193326Sed 103239462Sdim#include <net/bpf.h> 104243830Sdim 105243830Sdim#include <vm/vm.h> /* for vtophys */ 106243830Sdim#include <vm/pmap.h> /* for vtophys */ 107243830Sdim#include <machine/bus.h> 108243830Sdim#include <machine/resource.h> 109243830Sdim#include <sys/bus.h> 110243830Sdim#include <sys/rman.h> 111243830Sdim 112239462Sdim#include <dev/pci/pcireg.h> 113239462Sdim#include <dev/pci/pcivar.h> 114193326Sed 115193326Sed#include <dev/mii/mii.h> 116261991Sdim#include <dev/mii/mii_bitbang.h> 117193326Sed#include <dev/mii/miivar.h> 118193326Sed 119193326Sed/* "device miibus" required. See GENERIC if you get errors here. */ 120193326Sed#include "miibus_if.h" 121193326Sed 122193326Sed#define WB_USEIOSPACE 123193326Sed 124193326Sed#include <dev/wb/if_wbreg.h> 125193326Sed 126243830SdimMODULE_DEPEND(wb, pci, 1, 1, 1); 127218893SdimMODULE_DEPEND(wb, ether, 1, 1, 1); 128243830SdimMODULE_DEPEND(wb, miibus, 1, 1, 1); 129193326Sed 130208600Srdivacky/* 131208600Srdivacky * Various supported device vendors/types and their names. 132208600Srdivacky */ 133261991Sdimstatic const struct wb_type wb_devs[] = { 134261991Sdim { WB_VENDORID, WB_DEVICEID_840F, 135261991Sdim "Winbond W89C840F 10/100BaseTX" }, 136221345Sdim { CP_VENDORID, CP_DEVICEID_RL100, 137234353Sdim "Compex RL100-ATX 10/100baseTX" }, 138261991Sdim { 0, 0, NULL } 139221345Sdim}; 140193326Sed 141261991Sdimstatic int wb_probe(device_t); 142261991Sdimstatic int wb_attach(device_t); 143261991Sdimstatic int wb_detach(device_t); 144261991Sdim 145261991Sdimstatic int wb_bfree(struct mbuf *, void *addr, void *args); 146218893Sdimstatic int wb_newbuf(struct wb_softc *, struct wb_chain_onefrag *, 147239462Sdim struct mbuf *); 148218893Sdimstatic int wb_encap(struct wb_softc *, struct wb_chain *, struct mbuf *); 149198092Srdivacky 150193326Sedstatic void wb_rxeof(struct wb_softc *); 151193326Sedstatic void wb_rxeoc(struct wb_softc *); 152193326Sedstatic void wb_txeof(struct wb_softc *); 153193326Sedstatic void wb_txeoc(struct wb_softc *); 154261991Sdimstatic void wb_intr(void *); 155218893Sdimstatic void wb_tick(void *); 156218893Sdimstatic void wb_start(struct ifnet *); 157218893Sdimstatic void wb_start_locked(struct ifnet *); 158218893Sdimstatic int wb_ioctl(struct ifnet *, u_long, caddr_t); 159218893Sdimstatic void wb_init(void *); 160218893Sdimstatic void wb_init_locked(struct wb_softc *); 161243830Sdimstatic void wb_stop(struct wb_softc *); 162243830Sdimstatic void wb_watchdog(struct wb_softc *); 163243830Sdimstatic int wb_shutdown(device_t); 164218893Sdimstatic int wb_ifmedia_upd(struct ifnet *); 165218893Sdimstatic void wb_ifmedia_sts(struct ifnet *, struct ifmediareq *); 166218893Sdim 167218893Sdimstatic void wb_eeprom_putbyte(struct wb_softc *, int); 168218893Sdimstatic void wb_eeprom_getword(struct wb_softc *, int, u_int16_t *); 169218893Sdimstatic void wb_read_eeprom(struct wb_softc *, caddr_t, int, int, int); 170218893Sdim 171218893Sdimstatic void wb_setcfg(struct wb_softc *, u_int32_t); 172218893Sdimstatic void wb_setmulti(struct wb_softc *); 173218893Sdimstatic void wb_reset(struct wb_softc *); 174218893Sdimstatic void wb_fixmedia(struct wb_softc *); 175218893Sdimstatic int wb_list_rx_init(struct wb_softc *); 176218893Sdimstatic int wb_list_tx_init(struct wb_softc *); 177218893Sdim 178218893Sdimstatic int wb_miibus_readreg(device_t, int, int); 179218893Sdimstatic int wb_miibus_writereg(device_t, int, int, int); 180218893Sdimstatic void wb_miibus_statchg(device_t); 181261991Sdim 182193326Sed/* 183193326Sed * MII bit-bang glue 184261991Sdim */ 185261991Sdimstatic uint32_t wb_mii_bitbang_read(device_t); 186261991Sdimstatic void wb_mii_bitbang_write(device_t, uint32_t); 187193326Sed 188193326Sedstatic const struct mii_bitbang_ops wb_mii_bitbang_ops = { 189193326Sed wb_mii_bitbang_read, 190218893Sdim wb_mii_bitbang_write, 191249423Sdim { 192234353Sdim WB_SIO_MII_DATAOUT, /* MII_BIT_MDO */ 193193326Sed WB_SIO_MII_DATAIN, /* MII_BIT_MDI */ 194218893Sdim WB_SIO_MII_CLK, /* MII_BIT_MDC */ 195218893Sdim WB_SIO_MII_DIR, /* MII_BIT_DIR_HOST_PHY */ 196218893Sdim 0, /* MII_BIT_DIR_PHY_HOST */ 197249423Sdim } 198249423Sdim}; 199218893Sdim 200249423Sdim#ifdef WB_USEIOSPACE 201193326Sed#define WB_RES SYS_RES_IOPORT 202212904Sdim#define WB_RID WB_PCI_LOIO 203212904Sdim#else 204202879Srdivacky#define WB_RES SYS_RES_MEMORY 205218893Sdim#define WB_RID WB_PCI_LOMEM 206234353Sdim#endif 207193326Sed 208193326Sedstatic device_method_t wb_methods[] = { 209193326Sed /* Device interface */ 210193326Sed DEVMETHOD(device_probe, wb_probe), 211261991Sdim DEVMETHOD(device_attach, wb_attach), 212193326Sed DEVMETHOD(device_detach, wb_detach), 213193326Sed DEVMETHOD(device_shutdown, wb_shutdown), 214218893Sdim 215218893Sdim /* MII interface */ 216234353Sdim DEVMETHOD(miibus_readreg, wb_miibus_readreg), 217249423Sdim DEVMETHOD(miibus_writereg, wb_miibus_writereg), 218249423Sdim DEVMETHOD(miibus_statchg, wb_miibus_statchg), 219249423Sdim 220249423Sdim DEVMETHOD_END 221198092Srdivacky}; 222193326Sed 223193326Sedstatic driver_t wb_driver = { 224193326Sed "wb", 225193326Sed wb_methods, 226193326Sed sizeof(struct wb_softc) 227193326Sed}; 228193326Sed 229193326Sedstatic devclass_t wb_devclass; 230193326Sed 231193326SedDRIVER_MODULE(wb, pci, wb_driver, wb_devclass, 0, 0); 232193326SedDRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); 233193326Sed 234261991Sdim#define WB_SETBIT(sc, reg, x) \ 235198092Srdivacky CSR_WRITE_4(sc, reg, \ 236193326Sed CSR_READ_4(sc, reg) | (x)) 237261991Sdim 238296417Sdim#define WB_CLRBIT(sc, reg, x) \ 239296417Sdim CSR_WRITE_4(sc, reg, \ 240296417Sdim CSR_READ_4(sc, reg) & ~(x)) 241218893Sdim 242212904Sdim#define SIO_SET(x) \ 243249423Sdim CSR_WRITE_4(sc, WB_SIO, \ 244249423Sdim CSR_READ_4(sc, WB_SIO) | (x)) 245218893Sdim 246249423Sdim#define SIO_CLR(x) \ 247193326Sed CSR_WRITE_4(sc, WB_SIO, \ 248212904Sdim CSR_READ_4(sc, WB_SIO) & ~(x)) 249212904Sdim 250202879Srdivacky/* 251212904Sdim * Send a read command and address to the EEPROM, check for ACK. 252261991Sdim */ 253218893Sdimstatic void 254212904Sdimwb_eeprom_putbyte(sc, addr) 255234353Sdim struct wb_softc *sc; 256249423Sdim int addr; 257249423Sdim{ 258261991Sdim register int d, i; 259212904Sdim 260212904Sdim d = addr | WB_EECMD_READ; 261198092Srdivacky 262193326Sed /* 263193326Sed * Feed in each bit and stobe the clock. 264296417Sdim */ 265296417Sdim for (i = 0x400; i; i >>= 1) { 266296417Sdim if (d & i) { 267193326Sed SIO_SET(WB_SIO_EE_DATAIN); 268193326Sed } else { 269261991Sdim SIO_CLR(WB_SIO_EE_DATAIN); 270198092Srdivacky } 271193326Sed DELAY(100); 272261991Sdim SIO_SET(WB_SIO_EE_CLK); 273261991Sdim DELAY(150); 274296417Sdim SIO_CLR(WB_SIO_EE_CLK); 275296417Sdim DELAY(100); 276296417Sdim } 277218893Sdim} 278218893Sdim 279249423Sdim/* 280249423Sdim * Read a word of data stored in the EEPROM at address 'addr.' 281218893Sdim */ 282249423Sdimstatic void 283193326Sedwb_eeprom_getword(sc, addr, dest) 284212904Sdim struct wb_softc *sc; 285212904Sdim int addr; 286202879Srdivacky u_int16_t *dest; 287212904Sdim{ 288261991Sdim register int i; 289218893Sdim u_int16_t word = 0; 290212904Sdim 291234353Sdim /* Enter EEPROM access mode. */ 292249423Sdim CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 293249423Sdim 294234353Sdim /* 295261991Sdim * Send address of word we want to read. 296212904Sdim */ 297212904Sdim wb_eeprom_putbyte(sc, addr); 298221345Sdim 299221345Sdim CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 300198092Srdivacky 301193326Sed /* 302193326Sed * Start reading bits from EEPROM. 303296417Sdim */ 304296417Sdim for (i = 0x8000; i; i >>= 1) { 305296417Sdim SIO_SET(WB_SIO_EE_CLK); 306193326Sed DELAY(100); 307193326Sed if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) 308261991Sdim word |= i; 309198092Srdivacky SIO_CLR(WB_SIO_EE_CLK); 310193326Sed DELAY(100); 311193326Sed } 312261991Sdim 313261991Sdim /* Turn off EEPROM access mode. */ 314261991Sdim CSR_WRITE_4(sc, WB_SIO, 0); 315261991Sdim 316296417Sdim *dest = word; 317296417Sdim} 318296417Sdim 319296417Sdim/* 320218893Sdim * Read a sequence of words from the EEPROM. 321218893Sdim */ 322234353Sdimstatic void 323249423Sdimwb_read_eeprom(sc, dest, off, cnt, swap) 324249423Sdim struct wb_softc *sc; 325218893Sdim caddr_t dest; 326249423Sdim int off; 327193326Sed int cnt; 328212904Sdim int swap; 329212904Sdim{ 330202879Srdivacky int i; 331212904Sdim u_int16_t word = 0, *ptr; 332261991Sdim 333218893Sdim for (i = 0; i < cnt; i++) { 334218893Sdim wb_eeprom_getword(sc, off + i, &word); 335234353Sdim ptr = (u_int16_t *)(dest + (i * 2)); 336249423Sdim if (swap) 337249423Sdim *ptr = ntohs(word); 338261991Sdim else 339212904Sdim *ptr = word; 340212904Sdim } 341198092Srdivacky} 342193326Sed 343193326Sed/* 344296417Sdim * Read the MII serial port for the MII bit-bang module. 345296417Sdim */ 346296417Sdimstatic uint32_t 347193326Sedwb_mii_bitbang_read(device_t dev) 348193326Sed{ 349261991Sdim struct wb_softc *sc; 350198092Srdivacky uint32_t val; 351193326Sed 352261991Sdim sc = device_get_softc(dev); 353261991Sdim 354261991Sdim val = CSR_READ_4(sc, WB_SIO); 355261991Sdim CSR_BARRIER(sc, WB_SIO, 4, 356296417Sdim BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 357296417Sdim 358296417Sdim return (val); 359218893Sdim} 360234353Sdim 361249423Sdim/* 362234353Sdim * Write the MII serial port for the MII bit-bang module. 363249423Sdim */ 364193326Sedstatic void 365202879Srdivackywb_mii_bitbang_write(device_t dev, uint32_t val) 366212904Sdim{ 367202879Srdivacky struct wb_softc *sc; 368212904Sdim 369261991Sdim sc = device_get_softc(dev); 370218893Sdim 371234353Sdim CSR_WRITE_4(sc, WB_SIO, val); 372249423Sdim CSR_BARRIER(sc, WB_SIO, 4, 373249423Sdim BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 374261991Sdim} 375212904Sdim 376198092Srdivackystatic int 377193326Sedwb_miibus_readreg(dev, phy, reg) 378193326Sed device_t dev; 379296417Sdim int phy, reg; 380296417Sdim{ 381296417Sdim 382193326Sed return (mii_bitbang_readreg(dev, &wb_mii_bitbang_ops, phy, reg)); 383193326Sed} 384261991Sdim 385234353Sdimstatic int 386234353Sdimwb_miibus_writereg(dev, phy, reg, data) 387234353Sdim device_t dev; 388234353Sdim int phy, reg, data; 389234353Sdim{ 390234353Sdim 391234353Sdim mii_bitbang_writereg(dev, &wb_mii_bitbang_ops, phy, reg, data); 392234353Sdim 393234353Sdim return(0); 394234353Sdim} 395234353Sdim 396234353Sdimstatic void 397234353Sdimwb_miibus_statchg(dev) 398261991Sdim device_t dev; 399234353Sdim{ 400234353Sdim struct wb_softc *sc; 401243830Sdim struct mii_data *mii; 402243830Sdim 403261991Sdim sc = device_get_softc(dev); 404234353Sdim mii = device_get_softc(sc->wb_miibus); 405234353Sdim wb_setcfg(sc, mii->mii_media_active); 406234353Sdim} 407234353Sdim 408234353Sdim/* 409234353Sdim * Program the 64-bit multicast hash filter. 410234353Sdim */ 411234353Sdimstatic void 412234353Sdimwb_setmulti(sc) 413234353Sdim struct wb_softc *sc; 414234353Sdim{ 415234353Sdim struct ifnet *ifp; 416261991Sdim int h = 0; 417234353Sdim u_int32_t hashes[2] = { 0, 0 }; 418234353Sdim struct ifmultiaddr *ifma; 419234353Sdim u_int32_t rxfilt; 420261991Sdim int mcnt = 0; 421234353Sdim 422234353Sdim ifp = sc->wb_ifp; 423234353Sdim 424234353Sdim rxfilt = CSR_READ_4(sc, WB_NETCFG); 425234353Sdim 426234353Sdim if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 427234353Sdim rxfilt |= WB_NETCFG_RX_MULTI; 428239462Sdim CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 429239462Sdim CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); 430239462Sdim CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); 431239462Sdim return; 432239462Sdim } 433239462Sdim 434239462Sdim /* first, zot all the existing hash bits */ 435239462Sdim CSR_WRITE_4(sc, WB_MAR0, 0); 436261991Sdim CSR_WRITE_4(sc, WB_MAR1, 0); 437261991Sdim 438234353Sdim /* now program new ones */ 439234353Sdim if_maddr_rlock(ifp); 440239462Sdim TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 441234353Sdim if (ifma->ifma_addr->sa_family != AF_LINK) 442261991Sdim continue; 443234353Sdim h = ~ether_crc32_be(LLADDR((struct sockaddr_dl *) 444234353Sdim ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; 445234353Sdim if (h < 32) 446234353Sdim hashes[0] |= (1 << h); 447234353Sdim else 448234353Sdim hashes[1] |= (1 << (h - 32)); 449234353Sdim mcnt++; 450234353Sdim } 451234353Sdim if_maddr_runlock(ifp); 452234353Sdim 453261991Sdim if (mcnt) 454198092Srdivacky rxfilt |= WB_NETCFG_RX_MULTI; 455193326Sed else 456193326Sed rxfilt &= ~WB_NETCFG_RX_MULTI; 457193326Sed 458193326Sed CSR_WRITE_4(sc, WB_MAR0, hashes[0]); 459198092Srdivacky CSR_WRITE_4(sc, WB_MAR1, hashes[1]); 460218893Sdim CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 461224145Sdim} 462218893Sdim 463193326Sed/* 464203955Srdivacky * The Winbond manual states that in order to fiddle with the 465203955Srdivacky * 'full-duplex' and '100Mbps' bits in the netconfig register, we 466203955Srdivacky * first have to put the transmit and/or receive logic in the idle state. 467193326Sed */ 468203955Srdivackystatic void 469193326Sedwb_setcfg(sc, media) 470249423Sdim struct wb_softc *sc; 471249423Sdim u_int32_t media; 472198092Srdivacky{ 473203955Srdivacky int i, restart = 0; 474203955Srdivacky 475203955Srdivacky if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { 476198092Srdivacky restart = 1; 477193326Sed WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); 478193326Sed 479198092Srdivacky for (i = 0; i < WB_TIMEOUT; i++) { 480193326Sed DELAY(10); 481296417Sdim if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && 482296417Sdim (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) 483296417Sdim break; 484193326Sed } 485193326Sed 486261991Sdim if (i == WB_TIMEOUT) 487261991Sdim device_printf(sc->wb_dev, 488261991Sdim "failed to force tx and rx to idle state\n"); 489193326Sed } 490193326Sed 491193326Sed if (IFM_SUBTYPE(media) == IFM_10_T) 492193326Sed WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 493218893Sdim else 494224145Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 495218893Sdim 496193326Sed if ((media & IFM_GMASK) == IFM_FDX) 497203955Srdivacky WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 498203955Srdivacky else 499203955Srdivacky WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 500249423Sdim 501249423Sdim if (restart) 502193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); 503203955Srdivacky} 504203955Srdivacky 505203955Srdivackystatic void 506193326Sedwb_reset(sc) 507193326Sed struct wb_softc *sc; 508193326Sed{ 509193326Sed register int i; 510296417Sdim struct mii_data *mii; 511296417Sdim struct mii_softc *miisc; 512296417Sdim 513193326Sed CSR_WRITE_4(sc, WB_NETCFG, 0); 514193326Sed CSR_WRITE_4(sc, WB_BUSCTL, 0); 515261991Sdim CSR_WRITE_4(sc, WB_TXADDR, 0); 516261991Sdim CSR_WRITE_4(sc, WB_RXADDR, 0); 517261991Sdim 518261991Sdim WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 519261991Sdim WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 520261991Sdim 521276479Sdim for (i = 0; i < WB_TIMEOUT; i++) { 522261991Sdim DELAY(10); 523261991Sdim if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) 524261991Sdim break; 525261991Sdim } 526261991Sdim if (i == WB_TIMEOUT) 527261991Sdim device_printf(sc->wb_dev, "reset never completed!\n"); 528261991Sdim 529261991Sdim /* Wait a little while for the chip to get its brains in order. */ 530261991Sdim DELAY(1000); 531261991Sdim 532261991Sdim if (sc->wb_miibus == NULL) 533261991Sdim return; 534261991Sdim 535261991Sdim mii = device_get_softc(sc->wb_miibus); 536261991Sdim LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 537261991Sdim PHY_RESET(miisc); 538261991Sdim} 539261991Sdim 540261991Sdimstatic void 541261991Sdimwb_fixmedia(sc) 542261991Sdim struct wb_softc *sc; 543261991Sdim{ 544261991Sdim struct mii_data *mii = NULL; 545261991Sdim struct ifnet *ifp; 546261991Sdim u_int32_t media; 547261991Sdim 548261991Sdim mii = device_get_softc(sc->wb_miibus); 549261991Sdim ifp = sc->wb_ifp; 550261991Sdim 551261991Sdim mii_pollstat(mii); 552261991Sdim if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 553261991Sdim media = mii->mii_media_active & ~IFM_10_T; 554261991Sdim media |= IFM_100_TX; 555261991Sdim } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 556193326Sed media = mii->mii_media_active & ~IFM_100_TX; 557193326Sed media |= IFM_10_T; 558261991Sdim } else 559193326Sed return; 560193326Sed 561207619Srdivacky ifmedia_set(&mii->mii_media, media); 562193326Sed} 563193326Sed 564193326Sed/* 565207619Srdivacky * Probe for a Winbond chip. Check the PCI vendor and device 566218893Sdim * IDs against our list and return a device name if we find a match. 567207619Srdivacky */ 568207619Srdivackystatic int 569207619Srdivackywb_probe(dev) 570218893Sdim device_t dev; 571224145Sdim{ 572218893Sdim const struct wb_type *t; 573207619Srdivacky 574234353Sdim t = wb_devs; 575207619Srdivacky 576218893Sdim while(t->wb_name != NULL) { 577193326Sed if ((pci_get_vendor(dev) == t->wb_vid) && 578218893Sdim (pci_get_device(dev) == t->wb_did)) { 579193326Sed device_set_desc(dev, t->wb_name); 580218893Sdim return (BUS_PROBE_DEFAULT); 581224145Sdim } 582218893Sdim t++; 583207619Srdivacky } 584193326Sed 585208600Srdivacky return(ENXIO); 586208600Srdivacky} 587208600Srdivacky 588276479Sdim/* 589208600Srdivacky * Attach the interface. Allocate softc structures, do ifmedia 590276479Sdim * setup and ethernet/BPF attach. 591208600Srdivacky */ 592234353Sdimstatic int 593239462Sdimwb_attach(dev) 594239462Sdim device_t dev; 595239462Sdim{ 596239462Sdim u_char eaddr[ETHER_ADDR_LEN]; 597207619Srdivacky struct wb_softc *sc; 598234353Sdim struct ifnet *ifp; 599207619Srdivacky int error = 0, rid; 600207619Srdivacky 601261991Sdim sc = device_get_softc(dev); 602207619Srdivacky sc->wb_dev = dev; 603207619Srdivacky 604207619Srdivacky mtx_init(&sc->wb_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 605193326Sed MTX_DEF); 606207619Srdivacky callout_init_mtx(&sc->wb_stat_callout, &sc->wb_mtx, 0); 607193326Sed 608208600Srdivacky /* 609208600Srdivacky * Map control/status registers. 610208600Srdivacky */ 611208600Srdivacky pci_enable_busmaster(dev); 612208600Srdivacky 613234353Sdim rid = WB_RID; 614208600Srdivacky sc->wb_res = bus_alloc_resource_any(dev, WB_RES, &rid, RF_ACTIVE); 615193326Sed 616207619Srdivacky if (sc->wb_res == NULL) { 617193326Sed device_printf(dev, "couldn't map ports/memory\n"); 618234353Sdim error = ENXIO; 619208600Srdivacky goto fail; 620208600Srdivacky } 621208600Srdivacky 622193326Sed /* Allocate interrupt */ 623234353Sdim rid = 0; 624249423Sdim sc->wb_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 625249423Sdim RF_SHAREABLE | RF_ACTIVE); 626234353Sdim 627208600Srdivacky if (sc->wb_irq == NULL) { 628234353Sdim device_printf(dev, "couldn't map interrupt\n"); 629193326Sed error = ENXIO; 630193326Sed goto fail; 631193326Sed } 632193326Sed 633193326Sed /* Save the cache line size. */ 634218893Sdim sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; 635296417Sdim 636296417Sdim /* Reset the adapter. */ 637218893Sdim wb_reset(sc); 638218893Sdim 639218893Sdim /* 640193326Sed * Get station address from the EEPROM. 641193326Sed */ 642261991Sdim wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); 643261991Sdim 644261991Sdim sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, 645261991Sdim M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 646261991Sdim 647251662Sdim if (sc->wb_ldata == NULL) { 648251662Sdim device_printf(dev, "no memory for list buffers!\n"); 649251662Sdim error = ENXIO; 650251662Sdim goto fail; 651251662Sdim } 652251662Sdim 653251662Sdim bzero(sc->wb_ldata, sizeof(struct wb_list_data)); 654251662Sdim 655251662Sdim ifp = sc->wb_ifp = if_alloc(IFT_ETHER); 656251662Sdim if (ifp == NULL) { 657251662Sdim device_printf(dev, "can not if_alloc()\n"); 658251662Sdim error = ENOSPC; 659251662Sdim goto fail; 660251662Sdim } 661251662Sdim ifp->if_softc = sc; 662251662Sdim if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 663251662Sdim ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 664251662Sdim ifp->if_ioctl = wb_ioctl; 665251662Sdim ifp->if_start = wb_start; 666251662Sdim ifp->if_init = wb_init; 667251662Sdim ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; 668251662Sdim 669251662Sdim /* 670251662Sdim * Do MII setup. 671251662Sdim */ 672251662Sdim error = mii_attach(dev, &sc->wb_miibus, ifp, wb_ifmedia_upd, 673251662Sdim wb_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); 674251662Sdim if (error != 0) { 675251662Sdim device_printf(dev, "attaching PHYs failed\n"); 676251662Sdim goto fail; 677251662Sdim } 678251662Sdim 679251662Sdim /* 680251662Sdim * Call MI attach routine. 681251662Sdim */ 682251662Sdim ether_ifattach(ifp, eaddr); 683251662Sdim 684251662Sdim /* Hook interrupt last to avoid having to lock softc */ 685251662Sdim error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET | INTR_MPSAFE, 686251662Sdim NULL, wb_intr, sc, &sc->wb_intrhand); 687251662Sdim 688251662Sdim if (error) { 689251662Sdim device_printf(dev, "couldn't set up irq\n"); 690251662Sdim ether_ifdetach(ifp); 691251662Sdim goto fail; 692251662Sdim } 693251662Sdim 694251662Sdimfail: 695251662Sdim if (error) 696251662Sdim wb_detach(dev); 697251662Sdim 698251662Sdim return(error); 699251662Sdim} 700251662Sdim 701296417Sdim/* 702296417Sdim * Shutdown hardware and free up resources. This can be called any 703296417Sdim * time after the mutex has been initialized. It is called in both 704296417Sdim * the error case in attach and the normal detach case so it needs 705296417Sdim * to be careful about only freeing resources that have actually been 706296417Sdim * allocated. 707296417Sdim */ 708296417Sdimstatic int 709296417Sdimwb_detach(dev) 710296417Sdim device_t dev; 711296417Sdim{ 712296417Sdim struct wb_softc *sc; 713296417Sdim struct ifnet *ifp; 714296417Sdim 715296417Sdim sc = device_get_softc(dev); 716296417Sdim KASSERT(mtx_initialized(&sc->wb_mtx), ("wb mutex not initialized")); 717296417Sdim ifp = sc->wb_ifp; 718296417Sdim 719296417Sdim /* 720296417Sdim * Delete any miibus and phy devices attached to this interface. 721296417Sdim * This should only be done if attach succeeded. 722296417Sdim */ 723296417Sdim if (device_is_attached(dev)) { 724296417Sdim ether_ifdetach(ifp); 725296417Sdim WB_LOCK(sc); 726296417Sdim wb_stop(sc); 727296417Sdim WB_UNLOCK(sc); 728296417Sdim callout_drain(&sc->wb_stat_callout); 729296417Sdim } 730296417Sdim if (sc->wb_miibus) 731296417Sdim device_delete_child(dev, sc->wb_miibus); 732296417Sdim bus_generic_detach(dev); 733296417Sdim 734296417Sdim if (sc->wb_intrhand) 735296417Sdim bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 736296417Sdim if (sc->wb_irq) 737296417Sdim bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 738296417Sdim if (sc->wb_res) 739296417Sdim bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 740296417Sdim 741296417Sdim if (ifp) 742296417Sdim if_free(ifp); 743296417Sdim 744296417Sdim if (sc->wb_ldata) { 745296417Sdim contigfree(sc->wb_ldata, sizeof(struct wb_list_data) + 8, 746296417Sdim M_DEVBUF); 747296417Sdim } 748296417Sdim 749296417Sdim mtx_destroy(&sc->wb_mtx); 750296417Sdim 751296417Sdim return(0); 752296417Sdim} 753296417Sdim 754296417Sdim/* 755296417Sdim * Initialize the transmit descriptors. 756296417Sdim */ 757296417Sdimstatic int 758296417Sdimwb_list_tx_init(sc) 759296417Sdim struct wb_softc *sc; 760296417Sdim{ 761296417Sdim struct wb_chain_data *cd; 762296417Sdim struct wb_list_data *ld; 763296417Sdim int i; 764261991Sdim 765218893Sdim cd = &sc->wb_cdata; 766218893Sdim ld = sc->wb_ldata; 767218893Sdim 768218893Sdim for (i = 0; i < WB_TX_LIST_CNT; i++) { 769218893Sdim cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; 770218893Sdim if (i == (WB_TX_LIST_CNT - 1)) { 771218893Sdim cd->wb_tx_chain[i].wb_nextdesc = 772218893Sdim &cd->wb_tx_chain[0]; 773218893Sdim } else { 774218893Sdim cd->wb_tx_chain[i].wb_nextdesc = 775218893Sdim &cd->wb_tx_chain[i + 1]; 776218893Sdim } 777224145Sdim } 778218893Sdim 779218893Sdim cd->wb_tx_free = &cd->wb_tx_chain[0]; 780234353Sdim cd->wb_tx_tail = cd->wb_tx_head = NULL; 781218893Sdim 782218893Sdim return(0); 783218893Sdim} 784224145Sdim 785218893Sdim 786218893Sdim/* 787218893Sdim * Initialize the RX descriptors and allocate mbufs for them. Note that 788218893Sdim * we arrange the descriptors in a closed ring, so that the last descriptor 789218893Sdim * points back to the first. 790218893Sdim */ 791276479Sdimstatic int 792218893Sdimwb_list_rx_init(sc) 793276479Sdim struct wb_softc *sc; 794218893Sdim{ 795234353Sdim struct wb_chain_data *cd; 796218893Sdim struct wb_list_data *ld; 797234353Sdim int i; 798218893Sdim 799218893Sdim cd = &sc->wb_cdata; 800261991Sdim ld = sc->wb_ldata; 801218893Sdim 802218893Sdim for (i = 0; i < WB_RX_LIST_CNT; i++) { 803218893Sdim cd->wb_rx_chain[i].wb_ptr = 804218893Sdim (struct wb_desc *)&ld->wb_rx_list[i]; 805218893Sdim cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; 806218893Sdim if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) 807218893Sdim return(ENOBUFS); 808218893Sdim if (i == (WB_RX_LIST_CNT - 1)) { 809218893Sdim cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; 810218893Sdim ld->wb_rx_list[i].wb_next = 811218893Sdim vtophys(&ld->wb_rx_list[0]); 812234353Sdim } else { 813218893Sdim cd->wb_rx_chain[i].wb_nextdesc = 814218893Sdim &cd->wb_rx_chain[i + 1]; 815218893Sdim ld->wb_rx_list[i].wb_next = 816218893Sdim vtophys(&ld->wb_rx_list[i + 1]); 817234353Sdim } 818218893Sdim } 819218893Sdim 820218893Sdim cd->wb_rx_head = &cd->wb_rx_chain[0]; 821218893Sdim 822218893Sdim return(0); 823261991Sdim} 824261991Sdim 825249423Sdimstatic int 826249423Sdimwb_bfree(struct mbuf *m, void *buf, void *args) 827234353Sdim{ 828218893Sdim 829234353Sdim return (EXT_FREE_OK); 830218893Sdim} 831218893Sdim 832218893Sdim/* 833218893Sdim * Initialize an RX descriptor and attach an MBUF cluster. 834261991Sdim */ 835261991Sdimstatic int 836276479Sdimwb_newbuf(sc, c, m) 837276479Sdim struct wb_softc *sc; 838243830Sdim struct wb_chain_onefrag *c; 839218893Sdim struct mbuf *m; 840218893Sdim{ 841296417Sdim struct mbuf *m_new = NULL; 842296417Sdim 843218893Sdim if (m == NULL) { 844218893Sdim MGETHDR(m_new, M_NOWAIT, MT_DATA); 845218893Sdim if (m_new == NULL) 846218893Sdim return(ENOBUFS); 847218893Sdim m_new->m_data = c->wb_buf; 848261991Sdim m_new->m_pkthdr.len = m_new->m_len = WB_BUFBYTES; 849261991Sdim MEXTADD(m_new, c->wb_buf, WB_BUFBYTES, wb_bfree, c->wb_buf, 850261991Sdim NULL, 0, EXT_NET_DRV); 851193326Sed } else { 852193326Sed m_new = m; 853261991Sdim m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; 854193326Sed m_new->m_data = m_new->m_ext.ext_buf; 855193326Sed } 856193326Sed 857193326Sed m_adj(m_new, sizeof(u_int64_t)); 858193326Sed 859261991Sdim c->wb_mbuf = m_new; 860193326Sed c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); 861193326Sed c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; 862202379Srdivacky c->wb_ptr->wb_status = WB_RXSTAT; 863234353Sdim 864193326Sed return(0); 865202379Srdivacky} 866218893Sdim 867193326Sed/* 868193326Sed * A frame has been uploaded: pass the resulting mbuf chain up to 869218893Sdim * the higher level protocols. 870224145Sdim */ 871218893Sdimstatic void 872202379Srdivackywb_rxeof(sc) 873193326Sed struct wb_softc *sc; 874208600Srdivacky{ 875208600Srdivacky struct mbuf *m = NULL; 876208600Srdivacky struct ifnet *ifp; 877208600Srdivacky struct wb_chain_onefrag *cur_rx; 878208600Srdivacky int total_len = 0; 879249423Sdim u_int32_t rxstat; 880249423Sdim 881193326Sed WB_LOCK_ASSERT(sc); 882202379Srdivacky 883202379Srdivacky ifp = sc->wb_ifp; 884234353Sdim 885198092Srdivacky while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & 886193326Sed WB_RXSTAT_OWN)) { 887193326Sed struct mbuf *m0 = NULL; 888193326Sed 889193326Sed cur_rx = sc->wb_cdata.wb_rx_head; 890296417Sdim sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; 891296417Sdim 892296417Sdim m = cur_rx->wb_mbuf; 893193326Sed 894193326Sed if ((rxstat & WB_RXSTAT_MIIERR) || 895261991Sdim (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || 896193326Sed (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || 897261991Sdim !(rxstat & WB_RXSTAT_LASTFRAG) || 898261991Sdim !(rxstat & WB_RXSTAT_RXCMP)) { 899261991Sdim ifp->if_ierrors++; 900193326Sed wb_newbuf(sc, cur_rx, m); 901193326Sed device_printf(sc->wb_dev, 902193326Sed "receiver babbling: possible chip bug," 903224145Sdim " forcing reset\n"); 904224145Sdim wb_fixmedia(sc); 905234353Sdim wb_reset(sc); 906224145Sdim wb_init_locked(sc); 907234353Sdim return; 908193326Sed } 909261991Sdim 910261991Sdim if (rxstat & WB_RXSTAT_RXERR) { 911193326Sed ifp->if_ierrors++; 912224145Sdim wb_newbuf(sc, cur_rx, m); 913224145Sdim break; 914218893Sdim } 915224145Sdim 916218893Sdim /* No errors; receive the packet. */ 917224145Sdim total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); 918208600Srdivacky 919208600Srdivacky /* 920193326Sed * XXX The Winbond chip includes the CRC with every 921193326Sed * received frame, and there's no way to turn this 922193326Sed * behavior off (at least, I can't find anything in 923193326Sed * the manual that explains how to do it) so we have 924193326Sed * to trim off the CRC manually. 925224145Sdim */ 926224145Sdim total_len -= ETHER_CRC_LEN; 927224145Sdim 928224145Sdim m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, 929224145Sdim NULL); 930224145Sdim wb_newbuf(sc, cur_rx, m); 931234353Sdim if (m0 == NULL) { 932249423Sdim ifp->if_ierrors++; 933249423Sdim break; 934276479Sdim } 935249423Sdim m = m0; 936249423Sdim 937193326Sed ifp->if_ipackets++; 938193326Sed WB_UNLOCK(sc); 939193326Sed (*ifp->if_input)(ifp, m); 940193326Sed WB_LOCK(sc); 941193326Sed } 942193326Sed} 943193326Sed 944218893Sdimstatic void 945218893Sdimwb_rxeoc(sc) 946218893Sdim struct wb_softc *sc; 947193326Sed{ 948193326Sed wb_rxeof(sc); 949261991Sdim 950261991Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 951261991Sdim CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 952261991Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 953261991Sdim if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) 954296417Sdim CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 955201361Srdivacky} 956296417Sdim 957198092Srdivacky/* 958201361Srdivacky * A frame was downloaded to the chip. It's safe for us to clean up 959201361Srdivacky * the list buffers. 960234353Sdim */ 961201361Srdivackystatic void 962234353Sdimwb_txeof(sc) 963201361Srdivacky struct wb_softc *sc; 964201361Srdivacky{ 965201361Srdivacky struct wb_chain *cur_tx; 966218893Sdim struct ifnet *ifp; 967224145Sdim 968296417Sdim ifp = sc->wb_ifp; 969198092Srdivacky 970193326Sed /* Clear the timeout timer. */ 971208600Srdivacky sc->wb_timer = 0; 972208600Srdivacky 973261991Sdim if (sc->wb_cdata.wb_tx_head == NULL) 974193326Sed return; 975261991Sdim 976201361Srdivacky /* 977201361Srdivacky * Go through our tx list and free mbufs for those 978198092Srdivacky * frames that have been transmitted. 979193326Sed */ 980193326Sed while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { 981296417Sdim u_int32_t txstat; 982296417Sdim 983234353Sdim cur_tx = sc->wb_cdata.wb_tx_head; 984193326Sed txstat = WB_TXSTATUS(cur_tx); 985234353Sdim 986234353Sdim if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) 987201361Srdivacky break; 988234353Sdim 989234353Sdim if (txstat & WB_TXSTAT_TXERR) { 990201361Srdivacky ifp->if_oerrors++; 991193326Sed if (txstat & WB_TXSTAT_ABORT) 992234353Sdim ifp->if_collisions++; 993201361Srdivacky if (txstat & WB_TXSTAT_LATECOLL) 994201361Srdivacky ifp->if_collisions++; 995234353Sdim } 996261991Sdim 997261991Sdim ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; 998249423Sdim 999249423Sdim ifp->if_opackets++; 1000193326Sed m_freem(cur_tx->wb_mbuf); 1001249423Sdim cur_tx->wb_mbuf = NULL; 1002249423Sdim 1003193326Sed if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { 1004193326Sed sc->wb_cdata.wb_tx_head = NULL; 1005193326Sed sc->wb_cdata.wb_tx_tail = NULL; 1006193326Sed break; 1007193326Sed } 1008296417Sdim 1009296417Sdim sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; 1010296417Sdim } 1011210299Sed} 1012212904Sdim 1013212904Sdim/* 1014193326Sed * TX 'end of channel' interrupt handler. 1015193326Sed */ 1016261991Sdimstatic void 1017261991Sdimwb_txeoc(sc) 1018261991Sdim struct wb_softc *sc; 1019261991Sdim{ 1020251662Sdim struct ifnet *ifp; 1021251662Sdim 1022251662Sdim ifp = sc->wb_ifp; 1023251662Sdim 1024251662Sdim sc->wb_timer = 0; 1025251662Sdim 1026251662Sdim if (sc->wb_cdata.wb_tx_head == NULL) { 1027251662Sdim ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1028251662Sdim sc->wb_cdata.wb_tx_tail = NULL; 1029251662Sdim } else { 1030251662Sdim if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { 1031261991Sdim WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; 1032251662Sdim sc->wb_timer = 5; 1033251662Sdim CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1034251662Sdim } 1035251662Sdim } 1036251662Sdim} 1037261991Sdim 1038261991Sdimstatic void 1039261991Sdimwb_intr(arg) 1040251662Sdim void *arg; 1041251662Sdim{ 1042251662Sdim struct wb_softc *sc; 1043251662Sdim struct ifnet *ifp; 1044261991Sdim u_int32_t status; 1045251662Sdim 1046251662Sdim sc = arg; 1047251662Sdim WB_LOCK(sc); 1048261991Sdim ifp = sc->wb_ifp; 1049280031Sdim 1050280031Sdim if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1051280031Sdim WB_UNLOCK(sc); 1052280031Sdim return; 1053280031Sdim } 1054280031Sdim 1055280031Sdim /* Disable interrupts. */ 1056280031Sdim CSR_WRITE_4(sc, WB_IMR, 0x00000000); 1057251662Sdim 1058251662Sdim for (;;) { 1059251662Sdim 1060251662Sdim status = CSR_READ_4(sc, WB_ISR); 1061251662Sdim if (status) 1062251662Sdim CSR_WRITE_4(sc, WB_ISR, status); 1063251662Sdim 1064251662Sdim if ((status & WB_INTRS) == 0) 1065251662Sdim break; 1066296417Sdim 1067296417Sdim if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { 1068296417Sdim ifp->if_ierrors++; 1069251662Sdim wb_reset(sc); 1070251662Sdim if (status & WB_ISR_RX_ERR) 1071251662Sdim wb_fixmedia(sc); 1072251662Sdim wb_init_locked(sc); 1073251662Sdim continue; 1074261991Sdim } 1075193326Sed 1076261991Sdim if (status & WB_ISR_RX_OK) 1077193326Sed wb_rxeof(sc); 1078198092Srdivacky 1079261991Sdim if (status & WB_ISR_RX_IDLE) 1080193326Sed wb_rxeoc(sc); 1081193326Sed 1082193326Sed if (status & WB_ISR_TX_OK) 1083261991Sdim wb_txeof(sc); 1084193326Sed 1085198092Srdivacky if (status & WB_ISR_TX_NOBUF) 1086193326Sed wb_txeoc(sc); 1087234353Sdim 1088234353Sdim if (status & WB_ISR_TX_IDLE) { 1089234353Sdim wb_txeof(sc); 1090193326Sed if (sc->wb_cdata.wb_tx_head != NULL) { 1091193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1092208600Srdivacky CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1093208600Srdivacky } 1094208600Srdivacky } 1095208600Srdivacky 1096208600Srdivacky if (status & WB_ISR_TX_UNDERRUN) { 1097208600Srdivacky ifp->if_oerrors++; 1098208600Srdivacky wb_txeof(sc); 1099208600Srdivacky WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1100208600Srdivacky /* Jack up TX threshold */ 1101208600Srdivacky sc->wb_txthresh += WB_TXTHRESH_CHUNK; 1102208600Srdivacky WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 1103208600Srdivacky WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 1104208600Srdivacky WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1105208600Srdivacky } 1106193326Sed 1107193326Sed if (status & WB_ISR_BUS_ERR) { 1108198092Srdivacky wb_reset(sc); 1109193326Sed wb_init_locked(sc); 1110193326Sed } 1111218893Sdim 1112218893Sdim } 1113234353Sdim 1114218893Sdim /* Re-enable interrupts. */ 1115224145Sdim CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 1116218893Sdim 1117218893Sdim if (ifp->if_snd.ifq_head != NULL) { 1118193326Sed wb_start_locked(ifp); 1119193326Sed } 1120208600Srdivacky 1121276479Sdim WB_UNLOCK(sc); 1122234353Sdim} 1123261991Sdim 1124193326Sedstatic void 1125198092Srdivackywb_tick(xsc) 1126193326Sed void *xsc; 1127193326Sed{ 1128208600Srdivacky struct wb_softc *sc; 1129193326Sed struct mii_data *mii; 1130193326Sed 1131193326Sed sc = xsc; 1132193326Sed WB_LOCK_ASSERT(sc); 1133193326Sed mii = device_get_softc(sc->wb_miibus); 1134249423Sdim 1135249423Sdim mii_tick(mii); 1136198092Srdivacky 1137249423Sdim if (sc->wb_timer > 0 && --sc->wb_timer == 0) 1138193326Sed wb_watchdog(sc); 1139193326Sed callout_reset(&sc->wb_stat_callout, hz, wb_tick, sc); 1140193326Sed} 1141193326Sed 1142193326Sed/* 1143193326Sed * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 1144193326Sed * pointers to the fragment pointers. 1145218893Sdim */ 1146193326Sedstatic int 1147193326Sedwb_encap(sc, c, m_head) 1148239462Sdim struct wb_softc *sc; 1149193326Sed struct wb_chain *c; 1150207619Srdivacky struct mbuf *m_head; 1151207619Srdivacky{ 1152207619Srdivacky int frag = 0; 1153207619Srdivacky struct wb_desc *f = NULL; 1154221345Sdim int total_len; 1155221345Sdim struct mbuf *m; 1156207619Srdivacky 1157234353Sdim /* 1158207619Srdivacky * Start packing the mbufs in this chain into 1159193326Sed * the fragment pointers. Stop when we run out 1160193326Sed * of fragments or hit the end of the mbuf chain. 1161201361Srdivacky */ 1162261991Sdim m = m_head; 1163226633Sdim total_len = 0; 1164201361Srdivacky 1165226633Sdim for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 1166234353Sdim if (m->m_len != 0) { 1167276479Sdim if (frag == WB_MAXFRAGS) 1168201361Srdivacky break; 1169207619Srdivacky total_len += m->m_len; 1170193326Sed f = &c->wb_ptr->wb_frag[frag]; 1171193326Sed f->wb_ctl = WB_TXCTL_TLINK | m->m_len; 1172193326Sed if (frag == 0) { 1173261991Sdim f->wb_ctl |= WB_TXCTL_FIRSTFRAG; 1174201361Srdivacky f->wb_status = 0; 1175193326Sed } else 1176243830Sdim f->wb_status = WB_TXSTAT_OWN; 1177226633Sdim f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); 1178234353Sdim f->wb_data = vtophys(mtod(m, vm_offset_t)); 1179276479Sdim frag++; 1180234353Sdim } 1181234353Sdim } 1182261991Sdim 1183193326Sed /* 1184210299Sed * Handle special case: we used up all 16 fragments, 1185210299Sed * but we have more mbufs left in the chain. Copy the 1186276479Sdim * data into an mbuf cluster. Note that we don't 1187234353Sdim * bother clearing the values in the other fragment 1188276479Sdim * pointers/counters; it wouldn't gain us anything, 1189234353Sdim * and would waste cycles. 1190210299Sed */ 1191193326Sed if (m != NULL) { 1192210299Sed struct mbuf *m_new = NULL; 1193210299Sed 1194276479Sdim MGETHDR(m_new, M_NOWAIT, MT_DATA); 1195234353Sdim if (m_new == NULL) 1196234353Sdim return(1); 1197276479Sdim if (m_head->m_pkthdr.len > MHLEN) { 1198234353Sdim MCLGET(m_new, M_NOWAIT); 1199210299Sed if (!(m_new->m_flags & M_EXT)) { 1200261991Sdim m_freem(m_new); 1201201361Srdivacky return(1); 1202198092Srdivacky } 1203243830Sdim } 1204226633Sdim m_copydata(m_head, 0, m_head->m_pkthdr.len, 1205234353Sdim mtod(m_new, caddr_t)); 1206276479Sdim m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 1207234353Sdim m_freem(m_head); 1208234353Sdim m_head = m_new; 1209261991Sdim f = &c->wb_ptr->wb_frag[0]; 1210198092Srdivacky f->wb_status = 0; 1211288943Sdim f->wb_data = vtophys(mtod(m_new, caddr_t)); 1212198092Srdivacky f->wb_ctl = total_len = m_new->m_len; 1213234353Sdim f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; 1214201361Srdivacky frag = 1; 1215201361Srdivacky } 1216234353Sdim 1217193326Sed if (total_len < WB_MIN_FRAMELEN) { 1218193326Sed f = &c->wb_ptr->wb_frag[frag]; 1219198092Srdivacky f->wb_ctl = WB_MIN_FRAMELEN - total_len; 1220226633Sdim f->wb_data = vtophys(&sc->wb_cdata.wb_pad); 1221226633Sdim f->wb_ctl |= WB_TXCTL_TLINK; 1222226633Sdim f->wb_status = WB_TXSTAT_OWN; 1223226633Sdim frag++; 1224226633Sdim } 1225226633Sdim 1226234353Sdim c->wb_mbuf = m_head; 1227234353Sdim c->wb_lastdesc = frag - 1; 1228234353Sdim WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; 1229234353Sdim WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); 1230276479Sdim 1231276479Sdim return(0); 1232276479Sdim} 1233276479Sdim 1234276479Sdim/* 1235276479Sdim * Main transmit routine. To avoid having to do mbuf copies, we put pointers 1236276479Sdim * to the mbuf data regions directly in the transmit lists. We also save a 1237201361Srdivacky * copy of the pointers since the transmit list fragment pointers are 1238201361Srdivacky * physical addresses. 1239201361Srdivacky */ 1240201361Srdivacky 1241201361Srdivackystatic void 1242201361Srdivackywb_start(ifp) 1243234353Sdim struct ifnet *ifp; 1244261991Sdim{ 1245203955Srdivacky struct wb_softc *sc; 1246207619Srdivacky 1247207619Srdivacky sc = ifp->if_softc; 1248207619Srdivacky WB_LOCK(sc); 1249234353Sdim wb_start_locked(ifp); 1250207619Srdivacky WB_UNLOCK(sc); 1251207619Srdivacky} 1252234353Sdim 1253193326Sedstatic void 1254193326Sedwb_start_locked(ifp) 1255280031Sdim struct ifnet *ifp; 1256280031Sdim{ 1257198092Srdivacky struct wb_softc *sc; 1258280031Sdim struct mbuf *m_head = NULL; 1259280031Sdim struct wb_chain *cur_tx = NULL, *start_tx; 1260280031Sdim 1261280031Sdim sc = ifp->if_softc; 1262280031Sdim WB_LOCK_ASSERT(sc); 1263193326Sed 1264193326Sed /* 1265193326Sed * Check for an available queue slot. If there are none, 1266193326Sed * punt. 1267193326Sed */ 1268276479Sdim if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { 1269276479Sdim ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1270276479Sdim return; 1271276479Sdim } 1272193326Sed 1273193326Sed start_tx = sc->wb_cdata.wb_tx_free; 1274261991Sdim 1275198092Srdivacky while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { 1276198092Srdivacky IF_DEQUEUE(&ifp->if_snd, m_head); 1277198092Srdivacky if (m_head == NULL) 1278198092Srdivacky break; 1279198092Srdivacky 1280198092Srdivacky /* Pick a descriptor off the free list. */ 1281198092Srdivacky cur_tx = sc->wb_cdata.wb_tx_free; 1282198092Srdivacky sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; 1283193326Sed 1284261991Sdim /* Pack the data into the descriptor. */ 1285198092Srdivacky wb_encap(sc, cur_tx, m_head); 1286198092Srdivacky 1287198092Srdivacky if (cur_tx != start_tx) 1288198092Srdivacky WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; 1289198092Srdivacky 1290249423Sdim /* 1291249423Sdim * If there's a BPF listener, bounce a copy of this frame 1292261991Sdim * to him. 1293261991Sdim */ 1294198092Srdivacky BPF_MTAP(ifp, cur_tx->wb_mbuf); 1295198092Srdivacky } 1296193326Sed 1297193326Sed /* 1298193326Sed * If there are no packets queued, bail. 1299198092Srdivacky */ 1300193326Sed if (cur_tx == NULL) 1301218893Sdim return; 1302218893Sdim 1303218893Sdim /* 1304210299Sed * Place the request for the upload interrupt 1305212904Sdim * in the last descriptor in the chain. This way, if 1306193326Sed * we're chaining several packets at once, we'll only 1307193326Sed * get an interrupt once for the whole chain rather than 1308239462Sdim * once for each packet. 1309239462Sdim */ 1310239462Sdim WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; 1311239462Sdim cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; 1312261991Sdim sc->wb_cdata.wb_tx_tail = cur_tx; 1313239462Sdim 1314261991Sdim if (sc->wb_cdata.wb_tx_head == NULL) { 1315296417Sdim sc->wb_cdata.wb_tx_head = start_tx; 1316296417Sdim WB_TXOWN(start_tx) = WB_TXSTAT_OWN; 1317296417Sdim CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1318261991Sdim } else { 1319193326Sed /* 1320212904Sdim * We need to distinguish between the case where 1321218893Sdim * the own bit is clear because the chip cleared it 1322218893Sdim * and where the own bit is clear because we haven't 1323261991Sdim * set it yet. The magic value WB_UNSET is just some 1324261991Sdim * ramdomly chosen number which doesn't have the own 1325218893Sdim * bit set. When we actually transmit the frame, the 1326218893Sdim * status word will have _only_ the own bit set, so 1327261991Sdim * the txeoc handler will be able to tell if it needs 1328193326Sed * to initiate another transmission to flush out pending 1329212904Sdim * frames. 1330212904Sdim */ 1331202879Srdivacky WB_TXOWN(start_tx) = WB_UNSENT; 1332212904Sdim } 1333261991Sdim 1334218893Sdim /* 1335212904Sdim * Set a timeout in case the chip goes out to lunch. 1336212904Sdim */ 1337212904Sdim sc->wb_timer = 5; 1338261991Sdim} 1339212904Sdim 1340261991Sdimstatic void 1341212904Sdimwb_init(xsc) 1342212904Sdim void *xsc; 1343261991Sdim{ 1344261991Sdim struct wb_softc *sc = xsc; 1345193326Sed 1346202879Srdivacky WB_LOCK(sc); 1347198092Srdivacky wb_init_locked(sc); 1348261991Sdim WB_UNLOCK(sc); 1349261991Sdim} 1350249423Sdim 1351198092Srdivackystatic void 1352198092Srdivackywb_init_locked(sc) 1353193326Sed struct wb_softc *sc; 1354296417Sdim{ 1355296417Sdim struct ifnet *ifp = sc->wb_ifp; 1356296417Sdim int i; 1357193326Sed struct mii_data *mii; 1358193326Sed 1359193326Sed WB_LOCK_ASSERT(sc); 1360193326Sed mii = device_get_softc(sc->wb_miibus); 1361193326Sed 1362198092Srdivacky /* 1363193326Sed * Cancel pending I/O and free all RX/TX buffers. 1364234353Sdim */ 1365210299Sed wb_stop(sc); 1366193326Sed wb_reset(sc); 1367261991Sdim 1368193326Sed sc->wb_txthresh = WB_TXTHRESH_INIT; 1369193326Sed 1370193326Sed /* 1371193326Sed * Set cache alignment and burst length. 1372193326Sed */ 1373261991Sdim#ifdef foo 1374193326Sed CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); 1375218893Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 1376193326Sed WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 1377193326Sed#endif 1378261991Sdim 1379218893Sdim CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); 1380243830Sdim WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); 1381261991Sdim switch(sc->wb_cachesize) { 1382226633Sdim case 32: 1383249423Sdim WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); 1384276479Sdim break; 1385249423Sdim case 16: 1386210299Sed WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); 1387218893Sdim break; 1388193326Sed case 8: 1389218893Sdim WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); 1390193326Sed break; 1391249423Sdim case 0: 1392249423Sdim default: 1393234353Sdim WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); 1394198092Srdivacky break; 1395193326Sed } 1396193326Sed 1397210299Sed /* This doesn't tend to work too well at 100Mbps. */ 1398212904Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); 1399193326Sed 1400193326Sed /* Init our MAC address */ 1401234353Sdim for (i = 0; i < ETHER_ADDR_LEN; i++) { 1402234353Sdim CSR_WRITE_1(sc, WB_NODE0 + i, IF_LLADDR(sc->wb_ifp)[i]); 1403234353Sdim } 1404234353Sdim 1405234353Sdim /* Init circular RX list. */ 1406234353Sdim if (wb_list_rx_init(sc) == ENOBUFS) { 1407234353Sdim device_printf(sc->wb_dev, 1408249423Sdim "initialization failed: no memory for rx buffers\n"); 1409234353Sdim wb_stop(sc); 1410234353Sdim return; 1411234353Sdim } 1412261991Sdim 1413234353Sdim /* Init TX descriptors. */ 1414234353Sdim wb_list_tx_init(sc); 1415234353Sdim 1416234353Sdim /* If we want promiscuous mode, set the allframes bit. */ 1417234353Sdim if (ifp->if_flags & IFF_PROMISC) { 1418261991Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 1419261991Sdim } else { 1420261991Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 1421261991Sdim } 1422296417Sdim 1423296417Sdim /* 1424296417Sdim * Set capture broadcast bit to capture broadcast frames. 1425234353Sdim */ 1426234353Sdim if (ifp->if_flags & IFF_BROADCAST) { 1427234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 1428261991Sdim } else { 1429261991Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 1430261991Sdim } 1431234353Sdim 1432234353Sdim /* 1433234353Sdim * Program the multicast filter, if necessary. 1434234353Sdim */ 1435234353Sdim wb_setmulti(sc); 1436234353Sdim 1437234353Sdim /* 1438234353Sdim * Load the address of the RX list. 1439234353Sdim */ 1440234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1441234353Sdim CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 1442234353Sdim 1443234353Sdim /* 1444234353Sdim * Enable interrupts. 1445234353Sdim */ 1446234353Sdim CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 1447234353Sdim CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); 1448234353Sdim 1449234353Sdim /* Enable receiver and transmitter. */ 1450234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1451234353Sdim CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 1452234353Sdim 1453234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1454234353Sdim CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); 1455234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1456234353Sdim 1457234353Sdim mii_mediachg(mii); 1458234353Sdim 1459296417Sdim ifp->if_drv_flags |= IFF_DRV_RUNNING; 1460296417Sdim ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1461296417Sdim 1462234353Sdim callout_reset(&sc->wb_stat_callout, hz, wb_tick, sc); 1463296417Sdim} 1464296417Sdim 1465296417Sdim/* 1466234353Sdim * Set media options. 1467234353Sdim */ 1468234353Sdimstatic int 1469234353Sdimwb_ifmedia_upd(ifp) 1470296417Sdim struct ifnet *ifp; 1471296417Sdim{ 1472296417Sdim struct wb_softc *sc; 1473296417Sdim 1474239462Sdim sc = ifp->if_softc; 1475234353Sdim 1476234353Sdim WB_LOCK(sc); 1477234353Sdim if (ifp->if_flags & IFF_UP) 1478234353Sdim wb_init_locked(sc); 1479234353Sdim WB_UNLOCK(sc); 1480234353Sdim 1481276479Sdim return(0); 1482234353Sdim} 1483296417Sdim 1484296417Sdim/* 1485296417Sdim * Report current media status. 1486296417Sdim */ 1487296417Sdimstatic void 1488234353Sdimwb_ifmedia_sts(ifp, ifmr) 1489234353Sdim struct ifnet *ifp; 1490296417Sdim struct ifmediareq *ifmr; 1491296417Sdim{ 1492296417Sdim struct wb_softc *sc; 1493296417Sdim struct mii_data *mii; 1494234353Sdim 1495288943Sdim sc = ifp->if_softc; 1496234353Sdim 1497296417Sdim WB_LOCK(sc); 1498296417Sdim mii = device_get_softc(sc->wb_miibus); 1499296417Sdim 1500296417Sdim mii_pollstat(mii); 1501234353Sdim ifmr->ifm_active = mii->mii_media_active; 1502234353Sdim ifmr->ifm_status = mii->mii_media_status; 1503234353Sdim WB_UNLOCK(sc); 1504234353Sdim} 1505296417Sdim 1506296417Sdimstatic int 1507296417Sdimwb_ioctl(ifp, command, data) 1508296417Sdim struct ifnet *ifp; 1509296417Sdim u_long command; 1510296417Sdim caddr_t data; 1511296417Sdim{ 1512296417Sdim struct wb_softc *sc = ifp->if_softc; 1513234353Sdim struct mii_data *mii; 1514234353Sdim struct ifreq *ifr = (struct ifreq *) data; 1515234353Sdim int error = 0; 1516261991Sdim 1517261991Sdim switch(command) { 1518234353Sdim case SIOCSIFFLAGS: 1519261991Sdim WB_LOCK(sc); 1520234353Sdim if (ifp->if_flags & IFF_UP) { 1521234353Sdim wb_init_locked(sc); 1522234353Sdim } else { 1523234353Sdim if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1524234353Sdim wb_stop(sc); 1525261991Sdim } 1526261991Sdim WB_UNLOCK(sc); 1527261991Sdim error = 0; 1528261991Sdim break; 1529261991Sdim case SIOCADDMULTI: 1530288943Sdim case SIOCDELMULTI: 1531288943Sdim WB_LOCK(sc); 1532288943Sdim wb_setmulti(sc); 1533234353Sdim WB_UNLOCK(sc); 1534234353Sdim error = 0; 1535296417Sdim break; 1536234353Sdim case SIOCGIFMEDIA: 1537276479Sdim case SIOCSIFMEDIA: 1538276479Sdim mii = device_get_softc(sc->wb_miibus); 1539276479Sdim error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1540276479Sdim break; 1541276479Sdim default: 1542276479Sdim error = ether_ioctl(ifp, command, data); 1543234353Sdim break; 1544234353Sdim } 1545234353Sdim 1546234353Sdim return(error); 1547234353Sdim} 1548234353Sdim 1549234353Sdimstatic void 1550234353Sdimwb_watchdog(sc) 1551234353Sdim struct wb_softc *sc; 1552276479Sdim{ 1553276479Sdim struct ifnet *ifp; 1554276479Sdim 1555234353Sdim WB_LOCK_ASSERT(sc); 1556234353Sdim ifp = sc->wb_ifp; 1557234353Sdim ifp->if_oerrors++; 1558234353Sdim if_printf(ifp, "watchdog timeout\n"); 1559234353Sdim#ifdef foo 1560234353Sdim if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) 1561234353Sdim if_printf(ifp, "no carrier - transceiver cable problem?\n"); 1562234353Sdim#endif 1563234353Sdim wb_stop(sc); 1564276479Sdim wb_reset(sc); 1565276479Sdim wb_init_locked(sc); 1566276479Sdim 1567234353Sdim if (ifp->if_snd.ifq_head != NULL) 1568234353Sdim wb_start_locked(ifp); 1569234353Sdim} 1570234353Sdim 1571234353Sdim/* 1572234353Sdim * Stop the adapter and free any mbufs allocated to the 1573234353Sdim * RX and TX lists. 1574234353Sdim */ 1575234353Sdimstatic void 1576234353Sdimwb_stop(sc) 1577234353Sdim struct wb_softc *sc; 1578234353Sdim{ 1579296417Sdim register int i; 1580296417Sdim struct ifnet *ifp; 1581296417Sdim 1582296417Sdim WB_LOCK_ASSERT(sc); 1583276479Sdim ifp = sc->wb_ifp; 1584296417Sdim sc->wb_timer = 0; 1585296417Sdim 1586276479Sdim callout_stop(&sc->wb_stat_callout); 1587276479Sdim 1588296417Sdim WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); 1589296417Sdim CSR_WRITE_4(sc, WB_IMR, 0x00000000); 1590296417Sdim CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); 1591296417Sdim CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); 1592296417Sdim 1593234353Sdim /* 1594234353Sdim * Free data in the RX lists. 1595296417Sdim */ 1596234353Sdim for (i = 0; i < WB_RX_LIST_CNT; i++) { 1597234353Sdim if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { 1598234353Sdim m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); 1599296417Sdim sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; 1600296417Sdim } 1601296417Sdim } 1602296417Sdim bzero((char *)&sc->wb_ldata->wb_rx_list, 1603296417Sdim sizeof(sc->wb_ldata->wb_rx_list)); 1604296417Sdim 1605234353Sdim /* 1606234353Sdim * Free the TX list buffers. 1607296417Sdim */ 1608296417Sdim for (i = 0; i < WB_TX_LIST_CNT; i++) { 1609234353Sdim if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { 1610234353Sdim m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); 1611296417Sdim sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; 1612296417Sdim } 1613296417Sdim } 1614296417Sdim 1615296417Sdim bzero((char *)&sc->wb_ldata->wb_tx_list, 1616296417Sdim sizeof(sc->wb_ldata->wb_tx_list)); 1617296417Sdim 1618234353Sdim ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 1619234353Sdim} 1620296417Sdim 1621234353Sdim/* 1622296417Sdim * Stop all chip I/O so that the kernel's probe routines don't 1623296417Sdim * get confused by errant DMAs when rebooting. 1624296417Sdim */ 1625234353Sdimstatic int 1626234353Sdimwb_shutdown(dev) 1627234353Sdim device_t dev; 1628234353Sdim{ 1629234353Sdim struct wb_softc *sc; 1630261991Sdim 1631261991Sdim sc = device_get_softc(dev); 1632261991Sdim 1633261991Sdim WB_LOCK(sc); 1634261991Sdim wb_stop(sc); 1635234353Sdim WB_UNLOCK(sc); 1636234353Sdim 1637234353Sdim return (0); 1638234353Sdim} 1639234353Sdim