if_wb.c revision 59758
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 9193326Sed * notice, this list of conditions and the following disclaimer. 10193326Sed * 2. Redistributions in binary form must reproduce the above copyright 11193326Sed * notice, this list of conditions and the following disclaimer in the 12193326Sed * 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 17239462Sdim * may be used to endorse or promote products derived from this software 18193326Sed * without specific prior written permission. 19249423Sdim * 20202879Srdivacky * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21234353Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22234353Sdim * 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 25193326Sed * 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 28218893Sdim * 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 * 32251662Sdim * $FreeBSD: head/sys/pci/if_wb.c 59758 2000-04-29 13:41:57Z peter $ 33218893Sdim */ 34249423Sdim 35193326Sed/* 36193326Sed * Winbond fast ethernet PCI NIC driver 37193326Sed * 38193326Sed * Supports various cheap network adapters based on the Winbond W89C840F 39193326Sed * fast ethernet controller chip. This includes adapters manufactured by 40193326Sed * Winbond itself and some made by Linksys. 41193326Sed * 42193326Sed * Written by Bill Paul <wpaul@ctr.columbia.edu> 43193326Sed * Electrical Engineering Department 44193326Sed * Columbia University, New York City 45193326Sed */ 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 * 56239462Sdim * Like the tulip, the Winbond chip uses small descriptors containing 57193326Sed * a status word, a control word and 32-bit areas that can either be used 58243830Sdim * 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. 63239462Sdim * 64193326Sed * For the receive ring, this driver uses a linked list of descriptors, 65198092Srdivacky * each pointing to a single mbuf cluster buffer, which us large enough 66243830Sdim * to hold an entire packet. The link list is looped back to created a 67243830Sdim * closed ring. 68243830Sdim * 69218893Sdim * For transmission, the driver creates a linked list of 'super descriptors' 70243830Sdim * which each contain several individual descriptors linked toghether. 71239462Sdim * Each 'super descriptor' contains WB_MAXFRAGS descriptors, which we 72239462Sdim * abuse as fragment pointers. This allows us to use a buffer managment 73198092Srdivacky * scheme very similar to that used in the ThunderLAN and Etherlink XL 74198092Srdivacky * drivers. 75193326Sed * 76198092Srdivacky * Autonegotiation is performed using the external PHY via the MII bus. 77193326Sed * The sample boards I have all use a Davicom PHY. 78193326Sed * 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. 82193326Sed * I have no idea what he's on about: transmit performance with all 83193326Sed * three of my test boards seems fine. 84193326Sed */ 85193326Sed 86193326Sed#include "opt_bdg.h" 87193326Sed 88249423Sdim#include <sys/param.h> 89249423Sdim#include <sys/systm.h> 90239462Sdim#include <sys/sockio.h> 91198092Srdivacky#include <sys/mbuf.h> 92198092Srdivacky#include <sys/malloc.h> 93198092Srdivacky#include <sys/kernel.h> 94193326Sed#include <sys/socket.h> 95239462Sdim#include <sys/queue.h> 96243830Sdim 97243830Sdim#include <net/if.h> 98243830Sdim#include <net/if_arp.h> 99243830Sdim#include <net/ethernet.h> 100243830Sdim#include <net/if_dl.h> 101243830Sdim#include <net/if_media.h> 102243830Sdim 103243830Sdim#include <net/bpf.h> 104239462Sdim 105239462Sdim#ifdef BRIDGE 106193326Sed#include <net/bridge.h> 107193326Sed#endif 108193326Sed 109193326Sed#include <vm/vm.h> /* for vtophys */ 110193326Sed#include <vm/pmap.h> /* for vtophys */ 111193326Sed#include <machine/clock.h> /* for DELAY */ 112193326Sed#include <machine/bus_memio.h> 113193326Sed#include <machine/bus_pio.h> 114193326Sed#include <machine/bus.h> 115193326Sed#include <machine/resource.h> 116193326Sed#include <sys/bus.h> 117193326Sed#include <sys/rman.h> 118243830Sdim 119218893Sdim#include <pci/pcireg.h> 120243830Sdim#include <pci/pcivar.h> 121193326Sed 122208600Srdivacky#include <dev/mii/mii.h> 123208600Srdivacky#include <dev/mii/miivar.h> 124208600Srdivacky 125193326Sed/* "controller miibus0" required. See GENERIC if you get errors here. */ 126193326Sed#include "miibus_if.h" 127193326Sed 128221345Sdim#define WB_USEIOSPACE 129234353Sdim 130221345Sdim#include <pci/if_wbreg.h> 131221345Sdim 132193326SedMODULE_DEPEND(wb, miibus, 1, 1, 1); 133218893Sdim 134218893Sdim#ifndef lint 135218893Sdimstatic const char rcsid[] = 136218893Sdim "$FreeBSD: head/sys/pci/if_wb.c 59758 2000-04-29 13:41:57Z peter $"; 137218893Sdim#endif 138239462Sdim 139218893Sdim/* 140198092Srdivacky * Various supported device vendors/types and their names. 141193326Sed */ 142193326Sedstatic struct wb_type wb_devs[] = { 143193326Sed { WB_VENDORID, WB_DEVICEID_840F, 144193326Sed "Winbond W89C840F 10/100BaseTX" }, 145218893Sdim { CP_VENDORID, CP_DEVICEID_RL100, 146218893Sdim "Compex RL100-ATX 10/100baseTX" }, 147218893Sdim { 0, 0, NULL } 148218893Sdim}; 149218893Sdim 150218893Sdimstatic int wb_probe __P((device_t)); 151218893Sdimstatic int wb_attach __P((device_t)); 152243830Sdimstatic int wb_detach __P((device_t)); 153243830Sdim 154243830Sdimstatic void wb_bfree __P((caddr_t, u_int)); 155218893Sdimstatic int wb_newbuf __P((struct wb_softc *, 156218893Sdim struct wb_chain_onefrag *, 157218893Sdim struct mbuf *)); 158218893Sdimstatic int wb_encap __P((struct wb_softc *, struct wb_chain *, 159218893Sdim struct mbuf *)); 160218893Sdim 161218893Sdimstatic void wb_rxeof __P((struct wb_softc *)); 162218893Sdimstatic void wb_rxeoc __P((struct wb_softc *)); 163218893Sdimstatic void wb_txeof __P((struct wb_softc *)); 164218893Sdimstatic void wb_txeoc __P((struct wb_softc *)); 165218893Sdimstatic void wb_intr __P((void *)); 166218893Sdimstatic void wb_tick __P((void *)); 167218893Sdimstatic void wb_start __P((struct ifnet *)); 168218893Sdimstatic int wb_ioctl __P((struct ifnet *, u_long, caddr_t)); 169218893Sdimstatic void wb_init __P((void *)); 170218893Sdimstatic void wb_stop __P((struct wb_softc *)); 171218893Sdimstatic void wb_watchdog __P((struct ifnet *)); 172193326Sedstatic void wb_shutdown __P((device_t)); 173193326Sedstatic int wb_ifmedia_upd __P((struct ifnet *)); 174193326Sedstatic void wb_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 175193326Sed 176193326Sedstatic void wb_eeprom_putbyte __P((struct wb_softc *, int)); 177193326Sedstatic void wb_eeprom_getword __P((struct wb_softc *, int, u_int16_t *)); 178193326Sedstatic void wb_read_eeprom __P((struct wb_softc *, caddr_t, int, 179193326Sed int, int)); 180193326Sedstatic void wb_mii_sync __P((struct wb_softc *)); 181193326Sedstatic void wb_mii_send __P((struct wb_softc *, u_int32_t, int)); 182218893Sdimstatic int wb_mii_readreg __P((struct wb_softc *, struct wb_mii_frame *)); 183249423Sdimstatic int wb_mii_writereg __P((struct wb_softc *, struct wb_mii_frame *)); 184234353Sdim 185193326Sedstatic void wb_setcfg __P((struct wb_softc *, u_int32_t)); 186218893Sdimstatic u_int8_t wb_calchash __P((caddr_t)); 187218893Sdimstatic void wb_setmulti __P((struct wb_softc *)); 188218893Sdimstatic void wb_reset __P((struct wb_softc *)); 189249423Sdimstatic void wb_fixmedia __P((struct wb_softc *)); 190249423Sdimstatic int wb_list_rx_init __P((struct wb_softc *)); 191218893Sdimstatic int wb_list_tx_init __P((struct wb_softc *)); 192249423Sdim 193193326Sedstatic int wb_miibus_readreg __P((device_t, int, int)); 194212904Sdimstatic int wb_miibus_writereg __P((device_t, int, int, int)); 195212904Sdimstatic void wb_miibus_statchg __P((device_t)); 196202879Srdivacky 197218893Sdim#ifdef WB_USEIOSPACE 198234353Sdim#define WB_RES SYS_RES_IOPORT 199193326Sed#define WB_RID WB_PCI_LOIO 200193326Sed#else 201193326Sed#define WB_RES SYS_RES_MEMORY 202193326Sed#define WB_RID WB_PCI_LOMEM 203193326Sed#endif 204193326Sed 205193326Sedstatic device_method_t wb_methods[] = { 206218893Sdim /* Device interface */ 207218893Sdim DEVMETHOD(device_probe, wb_probe), 208234353Sdim DEVMETHOD(device_attach, wb_attach), 209249423Sdim DEVMETHOD(device_detach, wb_detach), 210249423Sdim DEVMETHOD(device_shutdown, wb_shutdown), 211249423Sdim 212249423Sdim /* bus interface, for miibus */ 213198092Srdivacky DEVMETHOD(bus_print_child, bus_generic_print_child), 214193326Sed DEVMETHOD(bus_driver_added, bus_generic_driver_added), 215193326Sed 216193326Sed /* MII interface */ 217193326Sed DEVMETHOD(miibus_readreg, wb_miibus_readreg), 218193326Sed DEVMETHOD(miibus_writereg, wb_miibus_writereg), 219193326Sed DEVMETHOD(miibus_statchg, wb_miibus_statchg), 220193326Sed { 0, 0 } 221193326Sed}; 222193326Sed 223193326Sedstatic driver_t wb_driver = { 224193326Sed "wb", 225193326Sed wb_methods, 226234353Sdim sizeof(struct wb_softc) 227234353Sdim}; 228198092Srdivacky 229193326Sedstatic devclass_t wb_devclass; 230193326Sed 231193326SedDRIVER_MODULE(if_wb, pci, wb_driver, wb_devclass, 0, 0); 232218893SdimDRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); 233212904Sdim 234249423Sdim#define WB_SETBIT(sc, reg, x) \ 235249423Sdim CSR_WRITE_4(sc, reg, \ 236218893Sdim CSR_READ_4(sc, reg) | x) 237249423Sdim 238193326Sed#define WB_CLRBIT(sc, reg, x) \ 239212904Sdim CSR_WRITE_4(sc, reg, \ 240212904Sdim CSR_READ_4(sc, reg) & ~x) 241202879Srdivacky 242212904Sdim#define SIO_SET(x) \ 243212904Sdim CSR_WRITE_4(sc, WB_SIO, \ 244218893Sdim CSR_READ_4(sc, WB_SIO) | x) 245212904Sdim 246234353Sdim#define SIO_CLR(x) \ 247249423Sdim CSR_WRITE_4(sc, WB_SIO, \ 248249423Sdim CSR_READ_4(sc, WB_SIO) & ~x) 249212904Sdim 250212904Sdim/* 251212904Sdim * Send a read command and address to the EEPROM, check for ACK. 252198092Srdivacky */ 253193326Sedstatic void wb_eeprom_putbyte(sc, addr) 254193326Sed struct wb_softc *sc; 255193326Sed int addr; 256193326Sed{ 257193326Sed register int d, i; 258198092Srdivacky 259193326Sed d = addr | WB_EECMD_READ; 260198092Srdivacky 261193326Sed /* 262193326Sed * Feed in each bit and stobe the clock. 263193326Sed */ 264218893Sdim for (i = 0x400; i; i >>= 1) { 265218893Sdim if (d & i) { 266249423Sdim SIO_SET(WB_SIO_EE_DATAIN); 267249423Sdim } else { 268218893Sdim SIO_CLR(WB_SIO_EE_DATAIN); 269249423Sdim } 270193326Sed DELAY(100); 271212904Sdim SIO_SET(WB_SIO_EE_CLK); 272212904Sdim DELAY(150); 273202879Srdivacky SIO_CLR(WB_SIO_EE_CLK); 274212904Sdim DELAY(100); 275212904Sdim } 276218893Sdim 277212904Sdim return; 278234353Sdim} 279249423Sdim 280249423Sdim/* 281234353Sdim * Read a word of data stored in the EEPROM at address 'addr.' 282212904Sdim */ 283212904Sdimstatic void wb_eeprom_getword(sc, addr, dest) 284212904Sdim struct wb_softc *sc; 285221345Sdim int addr; 286221345Sdim u_int16_t *dest; 287198092Srdivacky{ 288193326Sed register int i; 289193326Sed u_int16_t word = 0; 290193326Sed 291193326Sed /* Enter EEPROM access mode. */ 292193326Sed CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 293193326Sed 294193326Sed /* 295198092Srdivacky * Send address of word we want to read. 296193326Sed */ 297193326Sed wb_eeprom_putbyte(sc, addr); 298193326Sed 299218893Sdim CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 300218893Sdim 301234353Sdim /* 302249423Sdim * Start reading bits from EEPROM. 303249423Sdim */ 304218893Sdim for (i = 0x8000; i; i >>= 1) { 305249423Sdim SIO_SET(WB_SIO_EE_CLK); 306193326Sed DELAY(100); 307212904Sdim if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) 308212904Sdim word |= i; 309202879Srdivacky SIO_CLR(WB_SIO_EE_CLK); 310212904Sdim DELAY(100); 311212904Sdim } 312218893Sdim 313218893Sdim /* Turn off EEPROM access mode. */ 314234353Sdim CSR_WRITE_4(sc, WB_SIO, 0); 315249423Sdim 316249423Sdim *dest = word; 317212904Sdim 318212904Sdim return; 319212904Sdim} 320198092Srdivacky 321193326Sed/* 322193326Sed * Read a sequence of words from the EEPROM. 323193326Sed */ 324193326Sedstatic void wb_read_eeprom(sc, dest, off, cnt, swap) 325193326Sed struct wb_softc *sc; 326193326Sed caddr_t dest; 327198092Srdivacky int off; 328193326Sed int cnt; 329193326Sed int swap; 330193326Sed{ 331218893Sdim int i; 332234353Sdim u_int16_t word = 0, *ptr; 333249423Sdim 334234353Sdim for (i = 0; i < cnt; i++) { 335249423Sdim wb_eeprom_getword(sc, off + i, &word); 336193326Sed ptr = (u_int16_t *)(dest + (i * 2)); 337202879Srdivacky if (swap) 338212904Sdim *ptr = ntohs(word); 339202879Srdivacky else 340212904Sdim *ptr = word; 341218893Sdim } 342218893Sdim 343234353Sdim return; 344249423Sdim} 345249423Sdim 346212904Sdim/* 347212904Sdim * Sync the PHYs by setting data bit and strobing the clock 32 times. 348198092Srdivacky */ 349193326Sedstatic void wb_mii_sync(sc) 350193326Sed struct wb_softc *sc; 351193326Sed{ 352193326Sed register int i; 353234353Sdim 354234353Sdim SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN); 355234353Sdim 356234353Sdim for (i = 0; i < 32; i++) { 357234353Sdim SIO_SET(WB_SIO_MII_CLK); 358234353Sdim DELAY(1); 359234353Sdim SIO_CLR(WB_SIO_MII_CLK); 360234353Sdim DELAY(1); 361234353Sdim } 362234353Sdim 363234353Sdim return; 364234353Sdim} 365234353Sdim 366234353Sdim/* 367243830Sdim * Clock a series of bits through the MII. 368234353Sdim */ 369234353Sdimstatic void wb_mii_send(sc, bits, cnt) 370243830Sdim struct wb_softc *sc; 371243830Sdim u_int32_t bits; 372234353Sdim int cnt; 373234353Sdim{ 374234353Sdim int i; 375234353Sdim 376234353Sdim SIO_CLR(WB_SIO_MII_CLK); 377234353Sdim 378234353Sdim for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 379234353Sdim if (bits & i) { 380234353Sdim SIO_SET(WB_SIO_MII_DATAIN); 381234353Sdim } else { 382234353Sdim SIO_CLR(WB_SIO_MII_DATAIN); 383234353Sdim } 384234353Sdim DELAY(1); 385234353Sdim SIO_CLR(WB_SIO_MII_CLK); 386234353Sdim DELAY(1); 387234353Sdim SIO_SET(WB_SIO_MII_CLK); 388234353Sdim } 389234353Sdim} 390234353Sdim 391234353Sdim/* 392234353Sdim * Read an PHY register through the MII. 393234353Sdim */ 394234353Sdimstatic int wb_mii_readreg(sc, frame) 395234353Sdim struct wb_softc *sc; 396234353Sdim struct wb_mii_frame *frame; 397239462Sdim 398239462Sdim{ 399239462Sdim int i, ack, s; 400239462Sdim 401239462Sdim s = splimp(); 402239462Sdim 403239462Sdim /* 404239462Sdim * Set up frame for RX. 405234353Sdim */ 406234353Sdim frame->mii_stdelim = WB_MII_STARTDELIM; 407234353Sdim frame->mii_opcode = WB_MII_READOP; 408239462Sdim frame->mii_turnaround = 0; 409234353Sdim frame->mii_data = 0; 410234353Sdim 411234353Sdim CSR_WRITE_4(sc, WB_SIO, 0); 412234353Sdim 413234353Sdim /* 414234353Sdim * Turn on data xmit. 415234353Sdim */ 416234353Sdim SIO_SET(WB_SIO_MII_DIR); 417234353Sdim 418234353Sdim wb_mii_sync(sc); 419234353Sdim 420234353Sdim /* 421193326Sed * Send command/address info. 422198092Srdivacky */ 423193326Sed wb_mii_send(sc, frame->mii_stdelim, 2); 424193326Sed wb_mii_send(sc, frame->mii_opcode, 2); 425193326Sed wb_mii_send(sc, frame->mii_phyaddr, 5); 426193326Sed wb_mii_send(sc, frame->mii_regaddr, 5); 427198092Srdivacky 428218893Sdim /* Idle bit */ 429224145Sdim SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN)); 430218893Sdim DELAY(1); 431193326Sed SIO_SET(WB_SIO_MII_CLK); 432203955Srdivacky DELAY(1); 433203955Srdivacky 434203955Srdivacky /* Turn off xmit. */ 435193326Sed SIO_CLR(WB_SIO_MII_DIR); 436203955Srdivacky /* Check for ack */ 437193326Sed SIO_CLR(WB_SIO_MII_CLK); 438249423Sdim DELAY(1); 439249423Sdim SIO_SET(WB_SIO_MII_CLK); 440198092Srdivacky DELAY(1); 441203955Srdivacky ack = CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT; 442203955Srdivacky SIO_CLR(WB_SIO_MII_CLK); 443203955Srdivacky DELAY(1); 444198092Srdivacky SIO_SET(WB_SIO_MII_CLK); 445193326Sed DELAY(1); 446193326Sed 447198092Srdivacky /* 448193326Sed * Now try reading data bits. If the ack failed, we still 449218893Sdim * need to clock through 16 cycles to keep the PHY(s) in sync. 450193326Sed */ 451193326Sed if (ack) { 452193326Sed for(i = 0; i < 16; i++) { 453193326Sed SIO_CLR(WB_SIO_MII_CLK); 454193326Sed DELAY(1); 455193326Sed SIO_SET(WB_SIO_MII_CLK); 456193326Sed DELAY(1); 457218893Sdim } 458224145Sdim goto fail; 459218893Sdim } 460193326Sed 461203955Srdivacky for (i = 0x8000; i; i >>= 1) { 462203955Srdivacky SIO_CLR(WB_SIO_MII_CLK); 463203955Srdivacky DELAY(1); 464249423Sdim if (!ack) { 465249423Sdim if (CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT) 466193326Sed frame->mii_data |= i; 467203955Srdivacky DELAY(1); 468203955Srdivacky } 469203955Srdivacky SIO_SET(WB_SIO_MII_CLK); 470193326Sed DELAY(1); 471193326Sed } 472193326Sed 473193326Sedfail: 474218893Sdim 475193326Sed SIO_CLR(WB_SIO_MII_CLK); 476193326Sed DELAY(1); 477193326Sed SIO_SET(WB_SIO_MII_CLK); 478193326Sed DELAY(1); 479193326Sed 480193326Sed splx(s); 481193326Sed 482193326Sed if (ack) 483193326Sed return(1); 484207619Srdivacky return(0); 485193326Sed} 486193326Sed 487193326Sed/* 488207619Srdivacky * Write to a PHY register through the MII. 489218893Sdim */ 490207619Srdivackystatic int wb_mii_writereg(sc, frame) 491207619Srdivacky struct wb_softc *sc; 492207619Srdivacky struct wb_mii_frame *frame; 493218893Sdim 494224145Sdim{ 495218893Sdim int s; 496207619Srdivacky 497234353Sdim s = splimp(); 498207619Srdivacky /* 499218893Sdim * Set up frame for TX. 500193326Sed */ 501218893Sdim 502193326Sed frame->mii_stdelim = WB_MII_STARTDELIM; 503218893Sdim frame->mii_opcode = WB_MII_WRITEOP; 504224145Sdim frame->mii_turnaround = WB_MII_TURNAROUND; 505218893Sdim 506207619Srdivacky /* 507193326Sed * Turn on data output. 508208600Srdivacky */ 509208600Srdivacky SIO_SET(WB_SIO_MII_DIR); 510208600Srdivacky 511208600Srdivacky wb_mii_sync(sc); 512208600Srdivacky 513208600Srdivacky wb_mii_send(sc, frame->mii_stdelim, 2); 514208600Srdivacky wb_mii_send(sc, frame->mii_opcode, 2); 515234353Sdim wb_mii_send(sc, frame->mii_phyaddr, 5); 516239462Sdim wb_mii_send(sc, frame->mii_regaddr, 5); 517239462Sdim wb_mii_send(sc, frame->mii_turnaround, 2); 518239462Sdim wb_mii_send(sc, frame->mii_data, 16); 519239462Sdim 520207619Srdivacky /* Idle bit. */ 521234353Sdim SIO_SET(WB_SIO_MII_CLK); 522207619Srdivacky DELAY(1); 523207619Srdivacky SIO_CLR(WB_SIO_MII_CLK); 524207619Srdivacky DELAY(1); 525207619Srdivacky 526207619Srdivacky /* 527207619Srdivacky * Turn off xmit. 528193326Sed */ 529207619Srdivacky SIO_CLR(WB_SIO_MII_DIR); 530193326Sed 531208600Srdivacky splx(s); 532208600Srdivacky 533208600Srdivacky return(0); 534208600Srdivacky} 535208600Srdivacky 536234353Sdimstatic int wb_miibus_readreg(dev, phy, reg) 537208600Srdivacky device_t dev; 538193326Sed int phy, reg; 539207619Srdivacky{ 540193326Sed struct wb_softc *sc; 541234353Sdim struct wb_mii_frame frame; 542208600Srdivacky 543208600Srdivacky sc = device_get_softc(dev); 544208600Srdivacky 545193326Sed bzero((char *)&frame, sizeof(frame)); 546234353Sdim 547249423Sdim frame.mii_phyaddr = phy; 548249423Sdim frame.mii_regaddr = reg; 549234353Sdim wb_mii_readreg(sc, &frame); 550208600Srdivacky 551234353Sdim return(frame.mii_data); 552193326Sed} 553193326Sed 554193326Sedstatic int wb_miibus_writereg(dev, phy, reg, data) 555193326Sed device_t dev; 556193326Sed int phy, reg, data; 557218893Sdim{ 558218893Sdim struct wb_softc *sc; 559218893Sdim struct wb_mii_frame frame; 560218893Sdim 561218893Sdim sc = device_get_softc(dev); 562193326Sed 563193326Sed bzero((char *)&frame, sizeof(frame)); 564251662Sdim 565251662Sdim frame.mii_phyaddr = phy; 566251662Sdim frame.mii_regaddr = reg; 567251662Sdim frame.mii_data = data; 568251662Sdim 569251662Sdim wb_mii_writereg(sc, &frame); 570251662Sdim 571251662Sdim return(0); 572251662Sdim} 573251662Sdim 574251662Sdimstatic void wb_miibus_statchg(dev) 575251662Sdim device_t dev; 576251662Sdim{ 577251662Sdim struct wb_softc *sc; 578251662Sdim struct mii_data *mii; 579251662Sdim 580251662Sdim sc = device_get_softc(dev); 581251662Sdim mii = device_get_softc(sc->wb_miibus); 582251662Sdim wb_setcfg(sc, mii->mii_media_active); 583251662Sdim 584251662Sdim return; 585251662Sdim} 586251662Sdim 587251662Sdimstatic u_int8_t wb_calchash(addr) 588251662Sdim caddr_t addr; 589251662Sdim{ 590251662Sdim u_int32_t crc, carry; 591251662Sdim int i, j; 592251662Sdim u_int8_t c; 593251662Sdim 594251662Sdim /* Compute CRC for the address value. */ 595251662Sdim crc = 0xFFFFFFFF; /* initial value */ 596251662Sdim 597251662Sdim for (i = 0; i < 6; i++) { 598251662Sdim c = *(addr + i); 599251662Sdim for (j = 0; j < 8; j++) { 600251662Sdim carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 601251662Sdim crc <<= 1; 602251662Sdim c >>= 1; 603251662Sdim if (carry) 604251662Sdim crc = (crc ^ 0x04c11db6) | carry; 605251662Sdim } 606251662Sdim } 607251662Sdim 608251662Sdim /* 609251662Sdim * return the filter bit position 610251662Sdim * Note: I arrived at the following nonsense 611251662Sdim * through experimentation. It's not the usual way to 612251662Sdim * generate the bit position but it's the only thing 613251662Sdim * I could come up with that works. 614251662Sdim */ 615251662Sdim return(~(crc >> 26) & 0x0000003F); 616251662Sdim} 617251662Sdim 618251662Sdim/* 619251662Sdim * Program the 64-bit multicast hash filter. 620251662Sdim */ 621251662Sdimstatic void wb_setmulti(sc) 622218893Sdim struct wb_softc *sc; 623218893Sdim{ 624218893Sdim struct ifnet *ifp; 625218893Sdim int h = 0; 626218893Sdim u_int32_t hashes[2] = { 0, 0 }; 627218893Sdim struct ifmultiaddr *ifma; 628218893Sdim u_int32_t rxfilt; 629218893Sdim int mcnt = 0; 630218893Sdim 631218893Sdim ifp = &sc->arpcom.ac_if; 632218893Sdim 633218893Sdim rxfilt = CSR_READ_4(sc, WB_NETCFG); 634218893Sdim 635224145Sdim if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 636218893Sdim rxfilt |= WB_NETCFG_RX_MULTI; 637218893Sdim CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 638234353Sdim CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); 639218893Sdim CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); 640218893Sdim return; 641218893Sdim } 642224145Sdim 643218893Sdim /* first, zot all the existing hash bits */ 644218893Sdim CSR_WRITE_4(sc, WB_MAR0, 0); 645218893Sdim CSR_WRITE_4(sc, WB_MAR1, 0); 646218893Sdim 647218893Sdim /* now program new ones */ 648218893Sdim for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 649218893Sdim ifma = ifma->ifma_link.le_next) { 650218893Sdim if (ifma->ifma_addr->sa_family != AF_LINK) 651218893Sdim continue; 652218893Sdim h = wb_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 653234353Sdim if (h < 32) 654218893Sdim hashes[0] |= (1 << h); 655234353Sdim else 656218893Sdim hashes[1] |= (1 << (h - 32)); 657218893Sdim mcnt++; 658218893Sdim } 659218893Sdim 660218893Sdim if (mcnt) 661218893Sdim rxfilt |= WB_NETCFG_RX_MULTI; 662218893Sdim else 663218893Sdim rxfilt &= ~WB_NETCFG_RX_MULTI; 664218893Sdim 665218893Sdim CSR_WRITE_4(sc, WB_MAR0, hashes[0]); 666218893Sdim CSR_WRITE_4(sc, WB_MAR1, hashes[1]); 667218893Sdim CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 668218893Sdim 669218893Sdim return; 670234353Sdim} 671218893Sdim 672218893Sdim/* 673218893Sdim * The Winbond manual states that in order to fiddle with the 674218893Sdim * 'full-duplex' and '100Mbps' bits in the netconfig register, we 675234353Sdim * first have to put the transmit and/or receive logic in the idle state. 676218893Sdim */ 677218893Sdimstatic void wb_setcfg(sc, media) 678218893Sdim struct wb_softc *sc; 679218893Sdim u_int32_t media; 680218893Sdim{ 681249423Sdim int i, restart = 0; 682249423Sdim 683234353Sdim if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { 684218893Sdim restart = 1; 685234353Sdim WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); 686218893Sdim 687218893Sdim for (i = 0; i < WB_TIMEOUT; i++) { 688218893Sdim DELAY(10); 689218893Sdim if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && 690243830Sdim (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) 691243830Sdim break; 692243830Sdim } 693218893Sdim 694218893Sdim if (i == WB_TIMEOUT) 695218893Sdim printf("wb%d: failed to force tx and " 696218893Sdim "rx to idle state\n", sc->wb_unit); 697218893Sdim } 698218893Sdim 699218893Sdim if (IFM_SUBTYPE(media) == IFM_10_T) 700218893Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 701193326Sed else 702193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 703193326Sed 704193326Sed if ((media & IFM_GMASK) == IFM_FDX) 705193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 706193326Sed else 707193326Sed WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 708193326Sed 709193326Sed if (restart) 710193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); 711193326Sed 712193326Sed return; 713193326Sed} 714202379Srdivacky 715234353Sdimstatic void wb_reset(sc) 716193326Sed struct wb_softc *sc; 717202379Srdivacky{ 718218893Sdim register int i; 719193326Sed struct mii_data *mii; 720193326Sed 721218893Sdim CSR_WRITE_4(sc, WB_NETCFG, 0); 722224145Sdim CSR_WRITE_4(sc, WB_BUSCTL, 0); 723218893Sdim CSR_WRITE_4(sc, WB_TXADDR, 0); 724202379Srdivacky CSR_WRITE_4(sc, WB_RXADDR, 0); 725193326Sed 726208600Srdivacky WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 727208600Srdivacky WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 728208600Srdivacky 729208600Srdivacky for (i = 0; i < WB_TIMEOUT; i++) { 730208600Srdivacky DELAY(10); 731249423Sdim if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) 732249423Sdim break; 733193326Sed } 734202379Srdivacky if (i == WB_TIMEOUT) 735202379Srdivacky printf("wb%d: reset never completed!\n", sc->wb_unit); 736234353Sdim 737198092Srdivacky /* Wait a little while for the chip to get its brains in order. */ 738193326Sed DELAY(1000); 739193326Sed 740193326Sed if (sc->wb_miibus == NULL) 741193326Sed return; 742218893Sdim 743193326Sed mii = device_get_softc(sc->wb_miibus); 744193326Sed if (mii == NULL) 745193326Sed return; 746193326Sed 747193326Sed if (mii->mii_instance) { 748193326Sed struct mii_softc *miisc; 749193326Sed for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 750193326Sed miisc = LIST_NEXT(miisc, mii_list)) 751193326Sed mii_phy_reset(miisc); 752224145Sdim } 753224145Sdim 754234353Sdim return; 755224145Sdim} 756234353Sdim 757193326Sedstatic void wb_fixmedia(sc) 758193326Sed struct wb_softc *sc; 759193326Sed{ 760193326Sed struct mii_data *mii = NULL; 761224145Sdim struct ifnet *ifp; 762224145Sdim u_int32_t media; 763218893Sdim 764224145Sdim if (sc->wb_miibus == NULL) 765218893Sdim return; 766224145Sdim 767208600Srdivacky mii = device_get_softc(sc->wb_miibus); 768208600Srdivacky ifp = &sc->arpcom.ac_if; 769193326Sed 770193326Sed mii_pollstat(mii); 771193326Sed if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 772193326Sed media = mii->mii_media_active & ~IFM_10_T; 773193326Sed media |= IFM_100_TX; 774224145Sdim } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 775224145Sdim media = mii->mii_media_active & ~IFM_100_TX; 776224145Sdim media |= IFM_10_T; 777224145Sdim } else 778224145Sdim return; 779224145Sdim 780234353Sdim ifmedia_set(&mii->mii_media, media); 781249423Sdim 782249423Sdim return; 783193326Sed} 784249423Sdim 785249423Sdim/* 786193326Sed * Probe for a Winbond chip. Check the PCI vendor and device 787193326Sed * IDs against our list and return a device name if we find a match. 788193326Sed */ 789193326Sedstatic int wb_probe(dev) 790193326Sed device_t dev; 791193326Sed{ 792193326Sed struct wb_type *t; 793218893Sdim 794218893Sdim t = wb_devs; 795218893Sdim 796193326Sed while(t->wb_name != NULL) { 797193326Sed if ((pci_get_vendor(dev) == t->wb_vid) && 798193326Sed (pci_get_device(dev) == t->wb_did)) { 799193326Sed device_set_desc(dev, t->wb_name); 800193326Sed return(0); 801193326Sed } 802193326Sed t++; 803201361Srdivacky } 804201361Srdivacky 805234353Sdim return(ENXIO); 806201361Srdivacky} 807201361Srdivacky 808201361Srdivacky/* 809198092Srdivacky * Attach the interface. Allocate softc structures, do ifmedia 810201361Srdivacky * setup and ethernet/BPF attach. 811201361Srdivacky */ 812234353Sdimstatic int wb_attach(dev) 813201361Srdivacky device_t dev; 814234353Sdim{ 815201361Srdivacky int s; 816201361Srdivacky u_char eaddr[ETHER_ADDR_LEN]; 817201361Srdivacky u_int32_t command; 818218893Sdim struct wb_softc *sc; 819224145Sdim struct ifnet *ifp; 820201361Srdivacky int unit, error = 0, rid; 821198092Srdivacky 822234353Sdim s = splimp(); 823201361Srdivacky 824218893Sdim sc = device_get_softc(dev); 825218893Sdim unit = device_get_unit(dev); 826234353Sdim 827218893Sdim /* 828201361Srdivacky * Handle power management nonsense. 829201361Srdivacky */ 830234353Sdim 831193326Sed command = pci_read_config(dev, WB_PCI_CAPID, 4) & 0x000000FF; 832208600Srdivacky if (command == 0x01) { 833208600Srdivacky 834234353Sdim command = pci_read_config(dev, WB_PCI_PWRMGMTCTRL, 4); 835193326Sed if (command & WB_PSTATE_MASK) { 836193326Sed u_int32_t iobase, membase, irq; 837201361Srdivacky 838201361Srdivacky /* Save important PCI config data. */ 839201361Srdivacky iobase = pci_read_config(dev, WB_PCI_LOIO, 4); 840198092Srdivacky membase = pci_read_config(dev, WB_PCI_LOMEM, 4); 841193326Sed irq = pci_read_config(dev, WB_PCI_INTLINE, 4); 842201361Srdivacky 843201361Srdivacky /* Reset the power state. */ 844234353Sdim printf("wb%d: chip is in D%d power mode " 845201361Srdivacky "-- setting to D0\n", unit, command & WB_PSTATE_MASK); 846234353Sdim command &= 0xFFFFFFFC; 847201361Srdivacky pci_write_config(dev, WB_PCI_PWRMGMTCTRL, command, 4); 848234353Sdim 849193326Sed /* Restore PCI config data. */ 850201361Srdivacky pci_write_config(dev, WB_PCI_LOIO, iobase, 4); 851201361Srdivacky pci_write_config(dev, WB_PCI_LOMEM, membase, 4); 852234353Sdim pci_write_config(dev, WB_PCI_INTLINE, irq, 4); 853193326Sed } 854234353Sdim } 855201361Srdivacky 856201361Srdivacky /* 857234353Sdim * Map control/status registers. 858201361Srdivacky */ 859234353Sdim command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 860201361Srdivacky command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 861201361Srdivacky pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 862234353Sdim command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 863201361Srdivacky 864193326Sed#ifdef WB_USEIOSPACE 865234353Sdim if (!(command & PCIM_CMD_PORTEN)) { 866201361Srdivacky printf("wb%d: failed to enable I/O ports!\n", unit); 867201361Srdivacky error = ENXIO; 868234353Sdim goto fail; 869249423Sdim } 870249423Sdim#else 871249423Sdim if (!(command & PCIM_CMD_MEMEN)) { 872249423Sdim printf("wb%d: failed to enable memory mapping!\n", unit); 873193326Sed error = ENXIO; 874249423Sdim goto fail; 875249423Sdim } 876193326Sed#endif 877193326Sed 878193326Sed rid = WB_RID; 879193326Sed sc->wb_res = bus_alloc_resource(dev, WB_RES, &rid, 880193326Sed 0, ~0, 1, RF_ACTIVE); 881218893Sdim 882210299Sed if (sc->wb_res == NULL) { 883212904Sdim printf("wb%d: couldn't map ports/memory\n", unit); 884212904Sdim error = ENXIO; 885193326Sed goto fail; 886193326Sed } 887251662Sdim 888251662Sdim sc->wb_btag = rman_get_bustag(sc->wb_res); 889251662Sdim sc->wb_bhandle = rman_get_bushandle(sc->wb_res); 890251662Sdim 891251662Sdim /* Allocate interrupt */ 892251662Sdim rid = 0; 893251662Sdim sc->wb_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 894251662Sdim RF_SHAREABLE | RF_ACTIVE); 895251662Sdim 896251662Sdim if (sc->wb_irq == NULL) { 897251662Sdim printf("wb%d: couldn't map interrupt\n", unit); 898251662Sdim bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 899251662Sdim error = ENXIO; 900251662Sdim goto fail; 901251662Sdim } 902251662Sdim 903251662Sdim error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET, 904251662Sdim wb_intr, sc, &sc->wb_intrhand); 905251662Sdim 906251662Sdim if (error) { 907251662Sdim bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 908251662Sdim bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 909251662Sdim printf("wb%d: couldn't set up irq\n", unit); 910251662Sdim goto fail; 911251662Sdim } 912251662Sdim 913251662Sdim /* Save the cache line size. */ 914251662Sdim sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; 915251662Sdim 916251662Sdim /* Reset the adapter. */ 917251662Sdim wb_reset(sc); 918251662Sdim 919251662Sdim /* 920251662Sdim * Get station address from the EEPROM. 921251662Sdim */ 922251662Sdim wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); 923251662Sdim 924251662Sdim /* 925251662Sdim * A Winbond chip was detected. Inform the world. 926251662Sdim */ 927251662Sdim printf("wb%d: Ethernet address: %6D\n", unit, eaddr, ":"); 928251662Sdim 929251662Sdim sc->wb_unit = unit; 930251662Sdim bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 931251662Sdim 932251662Sdim sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, 933251662Sdim M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 934193326Sed 935193326Sed if (sc->wb_ldata == NULL) { 936193326Sed printf("wb%d: no memory for list buffers!\n", unit); 937193326Sed bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 938198092Srdivacky bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 939193326Sed bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 940193326Sed error = ENXIO; 941193326Sed goto fail; 942193326Sed } 943198092Srdivacky 944193326Sed bzero(sc->wb_ldata, sizeof(struct wb_list_data)); 945198092Srdivacky 946193326Sed ifp = &sc->arpcom.ac_if; 947234353Sdim ifp->if_softc = sc; 948234353Sdim ifp->if_unit = unit; 949234353Sdim ifp->if_name = "wb"; 950193326Sed ifp->if_mtu = ETHERMTU; 951193326Sed ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 952208600Srdivacky ifp->if_ioctl = wb_ioctl; 953208600Srdivacky ifp->if_output = ether_output; 954208600Srdivacky ifp->if_start = wb_start; 955208600Srdivacky ifp->if_watchdog = wb_watchdog; 956208600Srdivacky ifp->if_init = wb_init; 957208600Srdivacky ifp->if_baudrate = 10000000; 958208600Srdivacky ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; 959208600Srdivacky 960208600Srdivacky /* 961208600Srdivacky * Do MII setup. 962208600Srdivacky */ 963208600Srdivacky if (mii_phy_probe(dev, &sc->wb_miibus, 964208600Srdivacky wb_ifmedia_upd, wb_ifmedia_sts)) { 965208600Srdivacky bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 966193326Sed bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 967193326Sed bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 968198092Srdivacky free(sc->wb_ldata_ptr, M_DEVBUF); 969193326Sed error = ENXIO; 970193326Sed goto fail; 971218893Sdim } 972218893Sdim 973234353Sdim /* 974218893Sdim * Call MI attach routines. 975224145Sdim */ 976218893Sdim if_attach(ifp); 977218893Sdim ether_ifattach(ifp); 978193326Sed 979193326Sed bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 980208600Srdivacky 981208600Srdivackyfail: 982234353Sdim if (error) 983198092Srdivacky device_delete_child(dev, sc->wb_miibus); 984193326Sed splx(s); 985198092Srdivacky 986193326Sed return(error); 987193326Sed} 988208600Srdivacky 989193326Sedstatic int wb_detach(dev) 990193326Sed device_t dev; 991193326Sed{ 992193326Sed struct wb_softc *sc; 993193326Sed struct ifnet *ifp; 994249423Sdim int s; 995249423Sdim 996198092Srdivacky s = splimp(); 997249423Sdim 998193326Sed sc = device_get_softc(dev); 999193326Sed ifp = &sc->arpcom.ac_if; 1000193326Sed 1001193326Sed wb_stop(sc); 1002193326Sed if_detach(ifp); 1003193326Sed 1004193326Sed /* Delete any miibus and phy devices attached to this interface */ 1005218893Sdim bus_generic_detach(dev); 1006193326Sed device_delete_child(dev, sc->wb_miibus); 1007193326Sed 1008239462Sdim bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 1009193326Sed bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 1010207619Srdivacky bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 1011207619Srdivacky 1012207619Srdivacky free(sc->wb_ldata_ptr, M_DEVBUF); 1013207619Srdivacky 1014221345Sdim splx(s); 1015221345Sdim 1016207619Srdivacky return(0); 1017234353Sdim} 1018207619Srdivacky 1019193326Sed/* 1020193326Sed * Initialize the transmit descriptors. 1021201361Srdivacky */ 1022218893Sdimstatic int wb_list_tx_init(sc) 1023226633Sdim struct wb_softc *sc; 1024201361Srdivacky{ 1025226633Sdim struct wb_chain_data *cd; 1026234353Sdim struct wb_list_data *ld; 1027201361Srdivacky int i; 1028207619Srdivacky 1029193326Sed cd = &sc->wb_cdata; 1030193326Sed ld = sc->wb_ldata; 1031193326Sed 1032198092Srdivacky for (i = 0; i < WB_TX_LIST_CNT; i++) { 1033201361Srdivacky cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; 1034193326Sed if (i == (WB_TX_LIST_CNT - 1)) { 1035243830Sdim cd->wb_tx_chain[i].wb_nextdesc = 1036226633Sdim &cd->wb_tx_chain[0]; 1037234353Sdim } else { 1038234353Sdim cd->wb_tx_chain[i].wb_nextdesc = 1039234353Sdim &cd->wb_tx_chain[i + 1]; 1040234353Sdim } 1041193326Sed } 1042210299Sed 1043210299Sed cd->wb_tx_free = &cd->wb_tx_chain[0]; 1044234353Sdim cd->wb_tx_tail = cd->wb_tx_head = NULL; 1045234353Sdim 1046234353Sdim return(0); 1047234353Sdim} 1048210299Sed 1049193326Sed 1050210299Sed/* 1051210299Sed * Initialize the RX descriptors and allocate mbufs for them. Note that 1052210299Sed * we arrange the descriptors in a closed ring, so that the last descriptor 1053234353Sdim * points back to the first. 1054234353Sdim */ 1055234353Sdimstatic int wb_list_rx_init(sc) 1056234353Sdim struct wb_softc *sc; 1057210299Sed{ 1058193326Sed struct wb_chain_data *cd; 1059201361Srdivacky struct wb_list_data *ld; 1060198092Srdivacky int i; 1061243830Sdim 1062226633Sdim cd = &sc->wb_cdata; 1063234353Sdim ld = sc->wb_ldata; 1064234353Sdim 1065234353Sdim for (i = 0; i < WB_RX_LIST_CNT; i++) { 1066234353Sdim cd->wb_rx_chain[i].wb_ptr = 1067198092Srdivacky (struct wb_desc *)&ld->wb_rx_list[i]; 1068198092Srdivacky cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; 1069198092Srdivacky if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) 1070234353Sdim return(ENOBUFS); 1071201361Srdivacky if (i == (WB_RX_LIST_CNT - 1)) { 1072201361Srdivacky cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; 1073234353Sdim ld->wb_rx_list[i].wb_next = 1074193326Sed vtophys(&ld->wb_rx_list[0]); 1075193326Sed } else { 1076198092Srdivacky cd->wb_rx_chain[i].wb_nextdesc = 1077226633Sdim &cd->wb_rx_chain[i + 1]; 1078226633Sdim ld->wb_rx_list[i].wb_next = 1079226633Sdim vtophys(&ld->wb_rx_list[i + 1]); 1080226633Sdim } 1081226633Sdim } 1082226633Sdim 1083234353Sdim cd->wb_rx_head = &cd->wb_rx_chain[0]; 1084234353Sdim 1085234353Sdim return(0); 1086234353Sdim} 1087201361Srdivacky 1088201361Srdivackystatic void wb_bfree(buf, size) 1089201361Srdivacky caddr_t buf; 1090201361Srdivacky u_int size; 1091201361Srdivacky{ 1092201361Srdivacky return; 1093234353Sdim} 1094203955Srdivacky 1095203955Srdivacky/* 1096207619Srdivacky * Initialize an RX descriptor and attach an MBUF cluster. 1097207619Srdivacky */ 1098207619Srdivackystatic int wb_newbuf(sc, c, m) 1099234353Sdim struct wb_softc *sc; 1100207619Srdivacky struct wb_chain_onefrag *c; 1101207619Srdivacky struct mbuf *m; 1102234353Sdim{ 1103193326Sed struct mbuf *m_new = NULL; 1104193326Sed 1105198092Srdivacky if (m == NULL) { 1106193326Sed MGETHDR(m_new, M_DONTWAIT, MT_DATA); 1107193326Sed if (m_new == NULL) { 1108193326Sed printf("wb%d: no memory for rx " 1109193326Sed "list -- packet dropped!\n", sc->wb_unit); 1110193326Sed return(ENOBUFS); 1111200583Srdivacky } 1112193326Sed 1113193326Sed m_new->m_data = m_new->m_ext.ext_buf = c->wb_buf; 1114198092Srdivacky m_new->m_flags |= M_EXT; 1115198092Srdivacky m_new->m_ext.ext_size = m_new->m_pkthdr.len = 1116198092Srdivacky m_new->m_len = WB_BUFBYTES; 1117198092Srdivacky m_new->m_ext.ext_free = wb_bfree; 1118198092Srdivacky m_new->m_ext.ext_ref = wb_bfree; 1119198092Srdivacky } else { 1120198092Srdivacky m_new = m; 1121198092Srdivacky m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; 1122198092Srdivacky m_new->m_data = m_new->m_ext.ext_buf; 1123193326Sed } 1124198092Srdivacky 1125198092Srdivacky m_adj(m_new, sizeof(u_int64_t)); 1126198092Srdivacky 1127198092Srdivacky c->wb_mbuf = m_new; 1128198092Srdivacky c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); 1129198092Srdivacky c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; 1130249423Sdim c->wb_ptr->wb_status = WB_RXSTAT; 1131249423Sdim 1132218893Sdim return(0); 1133239462Sdim} 1134198092Srdivacky 1135198092Srdivacky/* 1136193326Sed * A frame has been uploaded: pass the resulting mbuf chain up to 1137193326Sed * the higher level protocols. 1138193326Sed */ 1139198092Srdivackystatic void wb_rxeof(sc) 1140193326Sed struct wb_softc *sc; 1141218893Sdim{ 1142218893Sdim struct ether_header *eh; 1143218893Sdim struct mbuf *m = NULL; 1144210299Sed struct ifnet *ifp; 1145212904Sdim struct wb_chain_onefrag *cur_rx; 1146193326Sed int total_len = 0; 1147193326Sed u_int32_t rxstat; 1148239462Sdim 1149239462Sdim ifp = &sc->arpcom.ac_if; 1150239462Sdim 1151239462Sdim while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & 1152239462Sdim WB_RXSTAT_OWN)) { 1153239462Sdim struct mbuf *m0 = NULL; 1154239462Sdim 1155193326Sed cur_rx = sc->wb_cdata.wb_rx_head; 1156193326Sed sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; 1157193326Sed 1158212904Sdim m = cur_rx->wb_mbuf; 1159218893Sdim 1160218893Sdim if ((rxstat & WB_RXSTAT_MIIERR) || 1161198092Srdivacky (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || 1162212904Sdim (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || 1163234353Sdim !(rxstat & WB_RXSTAT_LASTFRAG) || 1164218893Sdim !(rxstat & WB_RXSTAT_RXCMP)) { 1165218893Sdim ifp->if_ierrors++; 1166198092Srdivacky wb_newbuf(sc, cur_rx, m); 1167193326Sed printf("wb%x: receiver babbling: possible chip " 1168212904Sdim "bug, forcing reset\n", sc->wb_unit); 1169212904Sdim wb_fixmedia(sc); 1170202879Srdivacky wb_reset(sc); 1171212904Sdim wb_init(sc); 1172212904Sdim return; 1173218893Sdim } 1174212904Sdim 1175212904Sdim if (rxstat & WB_RXSTAT_RXERR) { 1176212904Sdim ifp->if_ierrors++; 1177212904Sdim wb_newbuf(sc, cur_rx, m); 1178212904Sdim break; 1179212904Sdim } 1180212904Sdim 1181212904Sdim /* No errors; receive the packet. */ 1182193326Sed total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); 1183202879Srdivacky 1184193326Sed /* 1185202879Srdivacky * XXX The Winbond chip includes the CRC with every 1186198092Srdivacky * received frame, and there's no way to turn this 1187249423Sdim * behavior off (at least, I can't find anything in 1188249423Sdim * the manual that explains how to do it) so we have 1189249423Sdim * to trim off the CRC manually. 1190198092Srdivacky */ 1191198092Srdivacky total_len -= ETHER_CRC_LEN; 1192193326Sed 1193193326Sed m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 1194193326Sed total_len + ETHER_ALIGN, 0, ifp, NULL); 1195193326Sed wb_newbuf(sc, cur_rx, m); 1196193326Sed if (m0 == NULL) { 1197193326Sed ifp->if_ierrors++; 1198198092Srdivacky break; 1199193326Sed } 1200234353Sdim m_adj(m0, ETHER_ALIGN); 1201210299Sed m = m0; 1202193326Sed 1203193326Sed ifp->if_ipackets++; 1204193326Sed eh = mtod(m, struct ether_header *); 1205193326Sed 1206193326Sed#ifdef BRIDGE 1207193326Sed if (do_bridge) { 1208193326Sed struct ifnet *bdg_ifp; 1209193326Sed bdg_ifp = bridge_in(m); 1210193326Sed if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_DROP) 1211218893Sdim bdg_forward(&m, bdg_ifp); 1212193326Sed if (((bdg_ifp != BDG_LOCAL) && (bdg_ifp != BDG_BCAST) && 1213193326Sed (bdg_ifp != BDG_MCAST)) || bdg_ifp == BDG_DROP) { 1214198092Srdivacky m_freem(m); 1215218893Sdim break; 1216243830Sdim } 1217218893Sdim } 1218226633Sdim#endif 1219249423Sdim 1220249423Sdim /* 1221210299Sed * Handle BPF listeners. Let the BPF user see the packet, but 1222218893Sdim * don't pass it up to the ether_input() layer unless it's 1223193326Sed * a broadcast packet, multicast packet, matches our ethernet 1224218893Sdim * address or the interface is in promiscuous mode. 1225193326Sed */ 1226249423Sdim if (ifp->if_bpf) { 1227249423Sdim bpf_mtap(ifp, m); 1228234353Sdim if (ifp->if_flags & IFF_PROMISC && 1229198092Srdivacky (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 1230193326Sed ETHER_ADDR_LEN) && 1231193326Sed (eh->ether_dhost[0] & 1) == 0)) { 1232210299Sed m_freem(m); 1233212904Sdim break; 1234193326Sed } 1235193326Sed } 1236234353Sdim 1237234353Sdim /* Remove header from mbuf and pass it on. */ 1238234353Sdim m_adj(m, sizeof(struct ether_header)); 1239234353Sdim ether_input(ifp, eh, m); 1240234353Sdim } 1241234353Sdim 1242234353Sdim return; 1243249423Sdim} 1244234353Sdim 1245234353Sdimvoid wb_rxeoc(sc) 1246234353Sdim struct wb_softc *sc; 1247234353Sdim{ 1248234353Sdim wb_rxeof(sc); 1249234353Sdim 1250234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1251234353Sdim CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 1252234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1253234353Sdim if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) 1254234353Sdim CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 1255234353Sdim 1256234353Sdim return; 1257234353Sdim} 1258234353Sdim 1259234353Sdim/* 1260234353Sdim * A frame was downloaded to the chip. It's safe for us to clean up 1261234353Sdim * the list buffers. 1262234353Sdim */ 1263234353Sdimstatic void wb_txeof(sc) 1264234353Sdim struct wb_softc *sc; 1265234353Sdim{ 1266234353Sdim struct wb_chain *cur_tx; 1267234353Sdim struct ifnet *ifp; 1268234353Sdim 1269234353Sdim ifp = &sc->arpcom.ac_if; 1270234353Sdim 1271234353Sdim /* Clear the timeout timer. */ 1272234353Sdim ifp->if_timer = 0; 1273234353Sdim 1274234353Sdim if (sc->wb_cdata.wb_tx_head == NULL) 1275234353Sdim return; 1276234353Sdim 1277234353Sdim /* 1278234353Sdim * Go through our tx list and free mbufs for those 1279234353Sdim * frames that have been transmitted. 1280234353Sdim */ 1281234353Sdim while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { 1282234353Sdim u_int32_t txstat; 1283234353Sdim 1284234353Sdim cur_tx = sc->wb_cdata.wb_tx_head; 1285234353Sdim txstat = WB_TXSTATUS(cur_tx); 1286234353Sdim 1287234353Sdim if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) 1288234353Sdim break; 1289234353Sdim 1290234353Sdim if (txstat & WB_TXSTAT_TXERR) { 1291234353Sdim ifp->if_oerrors++; 1292234353Sdim if (txstat & WB_TXSTAT_ABORT) 1293234353Sdim ifp->if_collisions++; 1294234353Sdim if (txstat & WB_TXSTAT_LATECOLL) 1295234353Sdim ifp->if_collisions++; 1296234353Sdim } 1297234353Sdim 1298234353Sdim ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; 1299234353Sdim 1300234353Sdim ifp->if_opackets++; 1301234353Sdim m_freem(cur_tx->wb_mbuf); 1302234353Sdim cur_tx->wb_mbuf = NULL; 1303234353Sdim 1304234353Sdim if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { 1305234353Sdim sc->wb_cdata.wb_tx_head = NULL; 1306234353Sdim sc->wb_cdata.wb_tx_tail = NULL; 1307234353Sdim break; 1308234353Sdim } 1309234353Sdim 1310234353Sdim sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; 1311234353Sdim } 1312234353Sdim 1313234353Sdim return; 1314234353Sdim} 1315234353Sdim 1316234353Sdim/* 1317234353Sdim * TX 'end of channel' interrupt handler. 1318234353Sdim */ 1319234353Sdimstatic void wb_txeoc(sc) 1320234353Sdim struct wb_softc *sc; 1321234353Sdim{ 1322234353Sdim struct ifnet *ifp; 1323234353Sdim 1324234353Sdim ifp = &sc->arpcom.ac_if; 1325234353Sdim 1326234353Sdim ifp->if_timer = 0; 1327234353Sdim 1328234353Sdim if (sc->wb_cdata.wb_tx_head == NULL) { 1329234353Sdim ifp->if_flags &= ~IFF_OACTIVE; 1330234353Sdim sc->wb_cdata.wb_tx_tail = NULL; 1331234353Sdim } else { 1332234353Sdim if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { 1333234353Sdim WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; 1334234353Sdim ifp->if_timer = 5; 1335234353Sdim CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1336234353Sdim } 1337234353Sdim } 1338234353Sdim 1339234353Sdim return; 1340234353Sdim} 1341234353Sdim 1342234353Sdimstatic void wb_intr(arg) 1343234353Sdim void *arg; 1344234353Sdim{ 1345234353Sdim struct wb_softc *sc; 1346234353Sdim struct ifnet *ifp; 1347234353Sdim u_int32_t status; 1348234353Sdim 1349234353Sdim sc = arg; 1350234353Sdim ifp = &sc->arpcom.ac_if; 1351234353Sdim 1352234353Sdim if (!(ifp->if_flags & IFF_UP)) 1353234353Sdim return; 1354234353Sdim 1355234353Sdim /* Disable interrupts. */ 1356234353Sdim CSR_WRITE_4(sc, WB_IMR, 0x00000000); 1357234353Sdim 1358234353Sdim for (;;) { 1359234353Sdim 1360234353Sdim status = CSR_READ_4(sc, WB_ISR); 1361234353Sdim if (status) 1362234353Sdim CSR_WRITE_4(sc, WB_ISR, status); 1363234353Sdim 1364234353Sdim if ((status & WB_INTRS) == 0) 1365234353Sdim break; 1366234353Sdim 1367234353Sdim if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { 1368234353Sdim ifp->if_ierrors++; 1369234353Sdim wb_reset(sc); 1370234353Sdim if (status & WB_ISR_RX_ERR) 1371234353Sdim wb_fixmedia(sc); 1372234353Sdim wb_init(sc); 1373234353Sdim continue; 1374234353Sdim } 1375234353Sdim 1376234353Sdim if (status & WB_ISR_RX_OK) 1377234353Sdim wb_rxeof(sc); 1378234353Sdim 1379234353Sdim if (status & WB_ISR_RX_IDLE) 1380234353Sdim wb_rxeoc(sc); 1381234353Sdim 1382234353Sdim if (status & WB_ISR_TX_OK) 1383234353Sdim wb_txeof(sc); 1384234353Sdim 1385239462Sdim if (status & WB_ISR_TX_NOBUF) 1386239462Sdim wb_txeoc(sc); 1387234353Sdim 1388234353Sdim if (status & WB_ISR_TX_IDLE) { 1389234353Sdim wb_txeof(sc); 1390234353Sdim if (sc->wb_cdata.wb_tx_head != NULL) { 1391234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1392234353Sdim CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1393234353Sdim } 1394234353Sdim } 1395234353Sdim 1396234353Sdim if (status & WB_ISR_TX_UNDERRUN) { 1397234353Sdim ifp->if_oerrors++; 1398234353Sdim wb_txeof(sc); 1399234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1400234353Sdim /* Jack up TX threshold */ 1401234353Sdim sc->wb_txthresh += WB_TXTHRESH_CHUNK; 1402234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 1403234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 1404234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1405234353Sdim } 1406234353Sdim 1407234353Sdim if (status & WB_ISR_BUS_ERR) { 1408243830Sdim wb_reset(sc); 1409243830Sdim wb_init(sc); 1410243830Sdim } 1411234353Sdim 1412243830Sdim } 1413234353Sdim 1414234353Sdim /* Re-enable interrupts. */ 1415234353Sdim CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 1416234353Sdim 1417234353Sdim if (ifp->if_snd.ifq_head != NULL) { 1418234353Sdim wb_start(ifp); 1419234353Sdim } 1420234353Sdim 1421234353Sdim return; 1422234353Sdim} 1423234353Sdim 1424234353Sdimstatic void wb_tick(xsc) 1425234353Sdim void *xsc; 1426234353Sdim{ 1427239462Sdim struct wb_softc *sc; 1428239462Sdim struct mii_data *mii; 1429234353Sdim int s; 1430234353Sdim 1431234353Sdim s = splimp(); 1432234353Sdim 1433234353Sdim sc = xsc; 1434234353Sdim mii = device_get_softc(sc->wb_miibus); 1435234353Sdim 1436234353Sdim mii_tick(mii); 1437234353Sdim 1438234353Sdim sc->wb_stat_ch = timeout(wb_tick, sc, hz); 1439234353Sdim 1440234353Sdim splx(s); 1441234353Sdim 1442234353Sdim return; 1443234353Sdim} 1444234353Sdim 1445234353Sdim/* 1446234353Sdim * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 1447234353Sdim * pointers to the fragment pointers. 1448234353Sdim */ 1449234353Sdimstatic int wb_encap(sc, c, m_head) 1450234353Sdim struct wb_softc *sc; 1451234353Sdim struct wb_chain *c; 1452234353Sdim struct mbuf *m_head; 1453234353Sdim{ 1454234353Sdim int frag = 0; 1455234353Sdim struct wb_desc *f = NULL; 1456234353Sdim int total_len; 1457234353Sdim struct mbuf *m; 1458234353Sdim 1459234353Sdim /* 1460234353Sdim * Start packing the mbufs in this chain into 1461234353Sdim * the fragment pointers. Stop when we run out 1462234353Sdim * of fragments or hit the end of the mbuf chain. 1463234353Sdim */ 1464234353Sdim m = m_head; 1465234353Sdim total_len = 0; 1466234353Sdim 1467234353Sdim for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 1468234353Sdim if (m->m_len != 0) { 1469234353Sdim if (frag == WB_MAXFRAGS) 1470234353Sdim break; 1471234353Sdim total_len += m->m_len; 1472234353Sdim f = &c->wb_ptr->wb_frag[frag]; 1473234353Sdim f->wb_ctl = WB_TXCTL_TLINK | m->m_len; 1474234353Sdim if (frag == 0) { 1475234353Sdim f->wb_ctl |= WB_TXCTL_FIRSTFRAG; 1476234353Sdim f->wb_status = 0; 1477234353Sdim } else 1478234353Sdim f->wb_status = WB_TXSTAT_OWN; 1479234353Sdim f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); 1480234353Sdim f->wb_data = vtophys(mtod(m, vm_offset_t)); 1481234353Sdim frag++; 1482234353Sdim } 1483234353Sdim } 1484234353Sdim 1485234353Sdim /* 1486234353Sdim * Handle special case: we used up all 16 fragments, 1487234353Sdim * but we have more mbufs left in the chain. Copy the 1488234353Sdim * data into an mbuf cluster. Note that we don't 1489234353Sdim * bother clearing the values in the other fragment 1490234353Sdim * pointers/counters; it wouldn't gain us anything, 1491234353Sdim * and would waste cycles. 1492234353Sdim */ 1493234353Sdim if (m != NULL) { 1494234353Sdim struct mbuf *m_new = NULL; 1495234353Sdim 1496234353Sdim MGETHDR(m_new, M_DONTWAIT, MT_DATA); 1497234353Sdim if (m_new == NULL) { 1498234353Sdim printf("wb%d: no memory for tx list", sc->wb_unit); 1499234353Sdim return(1); 1500234353Sdim } 1501234353Sdim if (m_head->m_pkthdr.len > MHLEN) { 1502234353Sdim MCLGET(m_new, M_DONTWAIT); 1503234353Sdim if (!(m_new->m_flags & M_EXT)) { 1504234353Sdim m_freem(m_new); 1505234353Sdim printf("wb%d: no memory for tx list", 1506234353Sdim sc->wb_unit); 1507234353Sdim return(1); 1508234353Sdim } 1509234353Sdim } 1510234353Sdim m_copydata(m_head, 0, m_head->m_pkthdr.len, 1511234353Sdim mtod(m_new, caddr_t)); 1512234353Sdim m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 1513234353Sdim m_freem(m_head); 1514234353Sdim m_head = m_new; 1515234353Sdim f = &c->wb_ptr->wb_frag[0]; 1516234353Sdim f->wb_status = 0; 1517234353Sdim f->wb_data = vtophys(mtod(m_new, caddr_t)); 1518234353Sdim f->wb_ctl = total_len = m_new->m_len; 1519234353Sdim f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; 1520234353Sdim frag = 1; 1521234353Sdim } 1522234353Sdim 1523234353Sdim if (total_len < WB_MIN_FRAMELEN) { 1524234353Sdim f = &c->wb_ptr->wb_frag[frag]; 1525249423Sdim f->wb_ctl = WB_MIN_FRAMELEN - total_len; 1526249423Sdim f->wb_data = vtophys(&sc->wb_cdata.wb_pad); 1527234353Sdim f->wb_ctl |= WB_TXCTL_TLINK; 1528249423Sdim f->wb_status = WB_TXSTAT_OWN; 1529234353Sdim frag++; 1530234353Sdim } 1531234353Sdim 1532234353Sdim c->wb_mbuf = m_head; 1533234353Sdim c->wb_lastdesc = frag - 1; 1534234353Sdim WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; 1535234353Sdim WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); 1536234353Sdim 1537234353Sdim return(0); 1538210299Sed} 1539193326Sed 1540210299Sed/* 1541193326Sed * Main transmit routine. To avoid having to do mbuf copies, we put pointers 1542210299Sed * to the mbuf data regions directly in the transmit lists. We also save a 1543193326Sed * copy of the pointers since the transmit list fragment pointers are 1544218893Sdim * physical addresses. 1545193326Sed */ 1546218893Sdim 1547234353Sdimstatic void wb_start(ifp) 1548193326Sed struct ifnet *ifp; 1549234353Sdim{ 1550218893Sdim struct wb_softc *sc; 1551218893Sdim struct mbuf *m_head = NULL; 1552218893Sdim struct wb_chain *cur_tx = NULL, *start_tx; 1553218893Sdim 1554218893Sdim sc = ifp->if_softc; 1555224145Sdim 1556218893Sdim /* 1557218893Sdim * Check for an available queue slot. If there are none, 1558210299Sed * punt. 1559210299Sed */ 1560198092Srdivacky if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { 1561218893Sdim ifp->if_flags |= IFF_OACTIVE; 1562218893Sdim return; 1563218893Sdim } 1564234353Sdim 1565193326Sed start_tx = sc->wb_cdata.wb_tx_free; 1566193326Sed 1567249423Sdim while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { 1568249423Sdim IF_DEQUEUE(&ifp->if_snd, m_head); 1569193326Sed if (m_head == NULL) 1570198092Srdivacky break; 1571210299Sed 1572193326Sed /* Pick a descriptor off the free list. */ 1573198092Srdivacky cur_tx = sc->wb_cdata.wb_tx_free; 1574193326Sed sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; 1575218893Sdim 1576193326Sed /* Pack the data into the descriptor. */ 1577193326Sed wb_encap(sc, cur_tx, m_head); 1578239462Sdim 1579239462Sdim if (cur_tx != start_tx) 1580193326Sed WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; 1581234353Sdim 1582234353Sdim /* 1583193326Sed * If there's a BPF listener, bounce a copy of this frame 1584239462Sdim * to him. 1585193326Sed */ 1586239462Sdim if (ifp->if_bpf) 1587239462Sdim bpf_mtap(ifp, cur_tx->wb_mbuf); 1588193326Sed } 1589193326Sed 1590218893Sdim /* 1591218893Sdim * If there are no packets queued, bail. 1592234353Sdim */ 1593234353Sdim if (cur_tx == NULL) 1594210299Sed return; 1595210299Sed 1596234353Sdim /* 1597243830Sdim * Place the request for the upload interrupt 1598243830Sdim * in the last descriptor in the chain. This way, if 1599193326Sed * we're chaining several packets at once, we'll only 1600234353Sdim * get an interupt once for the whole chain rather than 1601234353Sdim * once for each packet. 1602234353Sdim */ 1603234353Sdim WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; 1604234353Sdim cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; 1605234353Sdim sc->wb_cdata.wb_tx_tail = cur_tx; 1606234353Sdim 1607234353Sdim if (sc->wb_cdata.wb_tx_head == NULL) { 1608234353Sdim sc->wb_cdata.wb_tx_head = start_tx; 1609234353Sdim WB_TXOWN(start_tx) = WB_TXSTAT_OWN; 1610234353Sdim CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 1611234353Sdim } else { 1612234353Sdim /* 1613234353Sdim * We need to distinguish between the case where 1614234353Sdim * the own bit is clear because the chip cleared it 1615234353Sdim * and where the own bit is clear because we haven't 1616234353Sdim * set it yet. The magic value WB_UNSET is just some 1617212904Sdim * ramdomly chosen number which doesn't have the own 1618234353Sdim * bit set. When we actually transmit the frame, the 1619193326Sed * status word will have _only_ the own bit set, so 1620234353Sdim * the txeoc handler will be able to tell if it needs 1621234353Sdim * to initiate another transmission to flush out pending 1622234353Sdim * frames. 1623234353Sdim */ 1624234353Sdim WB_TXOWN(start_tx) = WB_UNSENT; 1625234353Sdim } 1626203955Srdivacky 1627234353Sdim /* 1628243830Sdim * Set a timeout in case the chip goes out to lunch. 1629234353Sdim */ 1630234353Sdim ifp->if_timer = 5; 1631218893Sdim 1632243830Sdim return; 1633208600Srdivacky} 1634208600Srdivacky 1635208600Srdivackystatic void wb_init(xsc) 1636208600Srdivacky void *xsc; 1637234353Sdim{ 1638234353Sdim struct wb_softc *sc = xsc; 1639193326Sed struct ifnet *ifp = &sc->arpcom.ac_if; 1640193326Sed int s, i; 1641198092Srdivacky struct mii_data *mii; 1642193326Sed 1643193326Sed s = splimp(); 1644218893Sdim 1645218893Sdim mii = device_get_softc(sc->wb_miibus); 1646218893Sdim 1647221345Sdim /* 1648221345Sdim * Cancel pending I/O and free all RX/TX buffers. 1649221345Sdim */ 1650221345Sdim wb_stop(sc); 1651221345Sdim wb_reset(sc); 1652221345Sdim 1653221345Sdim sc->wb_txthresh = WB_TXTHRESH_INIT; 1654221345Sdim 1655221345Sdim /* 1656221345Sdim * Set cache alignment and burst length. 1657221345Sdim */ 1658234353Sdim#ifdef foo 1659193326Sed CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); 1660208600Srdivacky WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 1661193326Sed WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 1662208600Srdivacky#endif 1663193326Sed 1664193326Sed CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); 1665193326Sed WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); 1666193326Sed switch(sc->wb_cachesize) { 1667193326Sed case 32: 1668193326Sed WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); 1669193326Sed break; 1670193326Sed case 16: 1671193326Sed WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); 1672193326Sed break; 1673234353Sdim case 8: 1674234353Sdim WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); 1675218893Sdim break; 1676234353Sdim case 0: 1677193326Sed default: 1678193326Sed WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); 1679234353Sdim break; 1680193326Sed } 1681193326Sed 1682193326Sed /* This doesn't tend to work too well at 100Mbps. */ 1683234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); 1684193326Sed 1685193326Sed /* Init our MAC address */ 1686210299Sed for (i = 0; i < ETHER_ADDR_LEN; i++) { 1687210299Sed CSR_WRITE_1(sc, WB_NODE0 + i, sc->arpcom.ac_enaddr[i]); 1688210299Sed } 1689193326Sed 1690193326Sed /* Init circular RX list. */ 1691234353Sdim if (wb_list_rx_init(sc) == ENOBUFS) { 1692234353Sdim printf("wb%d: initialization failed: no " 1693234353Sdim "memory for rx buffers\n", sc->wb_unit); 1694234353Sdim wb_stop(sc); 1695234353Sdim (void)splx(s); 1696234353Sdim return; 1697234353Sdim } 1698234353Sdim 1699234353Sdim /* Init TX descriptors. */ 1700234353Sdim wb_list_tx_init(sc); 1701234353Sdim 1702234353Sdim /* If we want promiscuous mode, set the allframes bit. */ 1703234353Sdim if (ifp->if_flags & IFF_PROMISC) { 1704234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 1705234353Sdim } else { 1706234353Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 1707234353Sdim } 1708234353Sdim 1709234353Sdim /* 1710243830Sdim * Set capture broadcast bit to capture broadcast frames. 1711234353Sdim */ 1712234353Sdim if (ifp->if_flags & IFF_BROADCAST) { 1713234353Sdim WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 1714218893Sdim } else { 1715218893Sdim WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 1716218893Sdim } 1717218893Sdim 1718218893Sdim /* 1719218893Sdim * Program the multicast filter, if necessary. 1720218893Sdim */ 1721193326Sed wb_setmulti(sc); 1722193326Sed 1723193326Sed /* 1724193326Sed * Load the address of the RX list. 1725234353Sdim */ 1726193326Sed WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1727193326Sed CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 1728234353Sdim 1729193326Sed /* 1730193326Sed * Enable interrupts. 1731234353Sdim */ 1732193326Sed CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 1733193326Sed CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); 1734234353Sdim 1735193326Sed /* Enable receiver and transmitter. */ 1736193326Sed WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 1737208600Srdivacky CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 1738208600Srdivacky 1739208600Srdivacky WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1740234353Sdim CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); 1741208600Srdivacky WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 1742208600Srdivacky 1743234353Sdim mii_mediachg(mii); 1744234353Sdim 1745234353Sdim ifp->if_flags |= IFF_RUNNING; 1746193326Sed ifp->if_flags &= ~IFF_OACTIVE; 1747243830Sdim 1748243830Sdim (void)splx(s); 1749218893Sdim 1750234353Sdim sc->wb_stat_ch = timeout(wb_tick, sc, hz); 1751218893Sdim 1752234353Sdim return; 1753243830Sdim} 1754193326Sed 1755249423Sdim/* 1756249423Sdim * Set media options. 1757193326Sed */ 1758193326Sedstatic int wb_ifmedia_upd(ifp) 1759193326Sed struct ifnet *ifp; 1760193326Sed{ 1761193326Sed struct wb_softc *sc; 1762193326Sed 1763218893Sdim sc = ifp->if_softc; 1764234353Sdim 1765218893Sdim if (ifp->if_flags & IFF_UP) 1766193326Sed wb_init(sc); 1767193326Sed 1768239462Sdim return(0); 1769239462Sdim} 1770193326Sed 1771234353Sdim/* 1772234353Sdim * Report current media status. 1773234353Sdim */ 1774234353Sdimstatic void wb_ifmedia_sts(ifp, ifmr) 1775234353Sdim struct ifnet *ifp; 1776234353Sdim struct ifmediareq *ifmr; 1777193326Sed{ 1778193326Sed struct wb_softc *sc; 1779193326Sed struct mii_data *mii; 1780193326Sed 1781218893Sdim sc = ifp->if_softc; 1782218893Sdim 1783218893Sdim mii = device_get_softc(sc->wb_miibus); 1784218893Sdim 1785218893Sdim mii_pollstat(mii); 1786218893Sdim ifmr->ifm_active = mii->mii_media_active; 1787218893Sdim ifmr->ifm_status = mii->mii_media_status; 1788193326Sed 1789193326Sed return; 1790218893Sdim} 1791193326Sed 1792218893Sdimstatic int wb_ioctl(ifp, command, data) 1793224145Sdim struct ifnet *ifp; 1794218893Sdim u_long command; 1795234353Sdim caddr_t data; 1796218893Sdim{ 1797218893Sdim struct wb_softc *sc = ifp->if_softc; 1798234353Sdim struct mii_data *mii; 1799210299Sed struct ifreq *ifr = (struct ifreq *) data; 1800210299Sed int s, error = 0; 1801193326Sed 1802193326Sed s = splimp(); 1803193326Sed 1804218893Sdim switch(command) { 1805193326Sed case SIOCSIFADDR: 1806218893Sdim case SIOCGIFADDR: 1807218893Sdim case SIOCSIFMTU: 1808218893Sdim error = ether_ioctl(ifp, command, data); 1809218893Sdim break; 1810218893Sdim case SIOCSIFFLAGS: 1811218893Sdim if (ifp->if_flags & IFF_UP) { 1812218893Sdim wb_init(sc); 1813218893Sdim } else { 1814193326Sed if (ifp->if_flags & IFF_RUNNING) 1815193326Sed wb_stop(sc); 1816193326Sed } 1817193326Sed error = 0; 1818193326Sed break; 1819218893Sdim case SIOCADDMULTI: 1820218893Sdim case SIOCDELMULTI: 1821218893Sdim wb_setmulti(sc); 1822218893Sdim error = 0; 1823234353Sdim break; 1824249423Sdim case SIOCGIFMEDIA: 1825249423Sdim case SIOCSIFMEDIA: 1826193326Sed mii = device_get_softc(sc->wb_miibus); 1827193326Sed error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1828193326Sed break; 1829193326Sed default: 1830193326Sed error = EINVAL; 1831193326Sed break; 1832218893Sdim } 1833218893Sdim 1834218893Sdim (void)splx(s); 1835193326Sed 1836193326Sed return(error); 1837239462Sdim} 1838204643Srdivacky 1839234353Sdimstatic void wb_watchdog(ifp) 1840204643Srdivacky struct ifnet *ifp; 1841204643Srdivacky{ 1842234353Sdim struct wb_softc *sc; 1843204643Srdivacky 1844204643Srdivacky sc = ifp->if_softc; 1845234353Sdim 1846204643Srdivacky ifp->if_oerrors++; 1847204643Srdivacky printf("wb%d: watchdog timeout\n", sc->wb_unit); 1848234353Sdim#ifdef foo 1849204643Srdivacky if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) 1850204643Srdivacky printf("wb%d: no carrier - transceiver cable problem?\n", 1851234353Sdim sc->wb_unit); 1852204643Srdivacky#endif 1853234353Sdim wb_stop(sc); 1854234353Sdim wb_reset(sc); 1855234353Sdim wb_init(sc); 1856204643Srdivacky 1857234353Sdim if (ifp->if_snd.ifq_head != NULL) 1858204643Srdivacky wb_start(ifp); 1859204643Srdivacky 1860204643Srdivacky return; 1861234353Sdim} 1862204643Srdivacky 1863204643Srdivacky/* 1864234353Sdim * Stop the adapter and free any mbufs allocated to the 1865198092Srdivacky * RX and TX lists. 1866198092Srdivacky */ 1867204643Srdivackystatic void wb_stop(sc) 1868234353Sdim struct wb_softc *sc; 1869204643Srdivacky{ 1870198092Srdivacky register int i; 1871198092Srdivacky struct ifnet *ifp; 1872204643Srdivacky 1873204643Srdivacky ifp = &sc->arpcom.ac_if; 1874204643Srdivacky ifp->if_timer = 0; 1875204643Srdivacky 1876204643Srdivacky untimeout(wb_tick, sc, sc->wb_stat_ch); 1877204643Srdivacky 1878204643Srdivacky WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); 1879234353Sdim CSR_WRITE_4(sc, WB_IMR, 0x00000000); 1880204643Srdivacky CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); 1881198092Srdivacky CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); 1882198092Srdivacky 1883204643Srdivacky /* 1884198092Srdivacky * Free data in the RX lists. 1885198092Srdivacky */ 1886198092Srdivacky for (i = 0; i < WB_RX_LIST_CNT; i++) { 1887204643Srdivacky if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { 1888204643Srdivacky m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); 1889198092Srdivacky sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; 1890198092Srdivacky } 1891198092Srdivacky } 1892198092Srdivacky bzero((char *)&sc->wb_ldata->wb_rx_list, 1893198092Srdivacky sizeof(sc->wb_ldata->wb_rx_list)); 1894198092Srdivacky 1895198092Srdivacky /* 1896198092Srdivacky * Free the TX list buffers. 1897198092Srdivacky */ 1898198092Srdivacky for (i = 0; i < WB_TX_LIST_CNT; i++) { 1899234353Sdim if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { 1900198092Srdivacky m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); 1901219077Sdim sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; 1902198092Srdivacky } 1903204643Srdivacky } 1904204643Srdivacky 1905204643Srdivacky bzero((char *)&sc->wb_ldata->wb_tx_list, 1906234353Sdim sizeof(sc->wb_ldata->wb_tx_list)); 1907234353Sdim 1908204643Srdivacky ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 1909204643Srdivacky 1910234353Sdim return; 1911204643Srdivacky} 1912204643Srdivacky 1913234353Sdim/* 1914234353Sdim * Stop all chip I/O so that the kernel's probe routines don't 1915204643Srdivacky * get confused by errant DMAs when rebooting. 1916204643Srdivacky */ 1917198092Srdivackystatic void wb_shutdown(dev) 1918219077Sdim device_t dev; 1919234353Sdim{ 1920198092Srdivacky struct wb_softc *sc; 1921198092Srdivacky 1922198092Srdivacky sc = device_get_softc(dev); 1923219077Sdim wb_stop(sc); 1924204643Srdivacky 1925204643Srdivacky return; 1926204643Srdivacky} 1927218893Sdim