if_wb.c revision 51533
141502Swpaul/* 241502Swpaul * Copyright (c) 1997, 1998 341502Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 441502Swpaul * 541502Swpaul * Redistribution and use in source and binary forms, with or without 641502Swpaul * modification, are permitted provided that the following conditions 741502Swpaul * are met: 841502Swpaul * 1. Redistributions of source code must retain the above copyright 941502Swpaul * notice, this list of conditions and the following disclaimer. 1041502Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1141502Swpaul * notice, this list of conditions and the following disclaimer in the 1241502Swpaul * documentation and/or other materials provided with the distribution. 1341502Swpaul * 3. All advertising materials mentioning features or use of this software 1441502Swpaul * must display the following acknowledgement: 1541502Swpaul * This product includes software developed by Bill Paul. 1641502Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1741502Swpaul * may be used to endorse or promote products derived from this software 1841502Swpaul * without specific prior written permission. 1941502Swpaul * 2041502Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2141502Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2241502Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2341502Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2441502Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2541502Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2641502Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2741502Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2841502Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2941502Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3041502Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3141502Swpaul * 3250477Speter * $FreeBSD: head/sys/pci/if_wb.c 51533 1999-09-22 06:08:11Z wpaul $ 3341502Swpaul */ 3441502Swpaul 3541502Swpaul/* 3641502Swpaul * Winbond fast ethernet PCI NIC driver 3741502Swpaul * 3841502Swpaul * Supports various cheap network adapters based on the Winbond W89C840F 3941502Swpaul * fast ethernet controller chip. This includes adapters manufactured by 4041502Swpaul * Winbond itself and some made by Linksys. 4141502Swpaul * 4241502Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4341502Swpaul * Electrical Engineering Department 4441502Swpaul * Columbia University, New York City 4541502Swpaul */ 4641502Swpaul 4741502Swpaul/* 4841502Swpaul * The Winbond W89C840F chip is a bus master; in some ways it resembles 4941502Swpaul * a DEC 'tulip' chip, only not as complicated. Unfortunately, it has 5041502Swpaul * one major difference which is that while the registers do many of 5141502Swpaul * the same things as a tulip adapter, the offsets are different: where 5241502Swpaul * tulip registers are typically spaced 8 bytes apart, the Winbond 5341502Swpaul * registers are spaced 4 bytes apart. The receiver filter is also 5441502Swpaul * programmed differently. 5541502Swpaul * 5641502Swpaul * Like the tulip, the Winbond chip uses small descriptors containing 5741502Swpaul * a status word, a control word and 32-bit areas that can either be used 5841502Swpaul * to point to two external data blocks, or to point to a single block 5941502Swpaul * and another descriptor in a linked list. Descriptors can be grouped 6041502Swpaul * together in blocks to form fixed length rings or can be chained 6141502Swpaul * together in linked lists. A single packet may be spread out over 6241502Swpaul * several descriptors if necessary. 6341502Swpaul * 6441502Swpaul * For the receive ring, this driver uses a linked list of descriptors, 6541502Swpaul * each pointing to a single mbuf cluster buffer, which us large enough 6641502Swpaul * to hold an entire packet. The link list is looped back to created a 6741502Swpaul * closed ring. 6841502Swpaul * 6941502Swpaul * For transmission, the driver creates a linked list of 'super descriptors' 7041502Swpaul * which each contain several individual descriptors linked toghether. 7141502Swpaul * Each 'super descriptor' contains WB_MAXFRAGS descriptors, which we 7241502Swpaul * abuse as fragment pointers. This allows us to use a buffer managment 7341502Swpaul * scheme very similar to that used in the ThunderLAN and Etherlink XL 7441502Swpaul * drivers. 7541502Swpaul * 7641502Swpaul * Autonegotiation is performed using the external PHY via the MII bus. 7741502Swpaul * The sample boards I have all use a Davicom PHY. 7841502Swpaul * 7941502Swpaul * Note: the author of the Linux driver for the Winbond chip alludes 8041502Swpaul * to some sort of flaw in the chip's design that seems to mandate some 8141502Swpaul * drastic workaround which signigicantly impairs transmit performance. 8241502Swpaul * I have no idea what he's on about: transmit performance with all 8341502Swpaul * three of my test boards seems fine. 8441502Swpaul */ 8541502Swpaul 8648645Sdes#include "bpf.h" 8748745Swpaul#include "opt_bdg.h" 8841502Swpaul 8941502Swpaul#include <sys/param.h> 9041502Swpaul#include <sys/systm.h> 9141502Swpaul#include <sys/sockio.h> 9241502Swpaul#include <sys/mbuf.h> 9341502Swpaul#include <sys/malloc.h> 9441502Swpaul#include <sys/kernel.h> 9541502Swpaul#include <sys/socket.h> 9650675Swpaul#include <sys/queue.h> 9741502Swpaul 9841502Swpaul#include <net/if.h> 9941502Swpaul#include <net/if_arp.h> 10041502Swpaul#include <net/ethernet.h> 10141502Swpaul#include <net/if_dl.h> 10241502Swpaul#include <net/if_media.h> 10341502Swpaul 10448645Sdes#if NBPF > 0 10541502Swpaul#include <net/bpf.h> 10641502Swpaul#endif 10741502Swpaul 10848745Swpaul#ifdef BRIDGE 10948745Swpaul#include <net/bridge.h> 11048745Swpaul#endif 11148745Swpaul 11241502Swpaul#include <vm/vm.h> /* for vtophys */ 11341502Swpaul#include <vm/pmap.h> /* for vtophys */ 11441502Swpaul#include <machine/clock.h> /* for DELAY */ 11541502Swpaul#include <machine/bus_memio.h> 11641502Swpaul#include <machine/bus_pio.h> 11741502Swpaul#include <machine/bus.h> 11849611Swpaul#include <machine/resource.h> 11949611Swpaul#include <sys/bus.h> 12049611Swpaul#include <sys/rman.h> 12141502Swpaul 12241502Swpaul#include <pci/pcireg.h> 12341502Swpaul#include <pci/pcivar.h> 12441502Swpaul 12550675Swpaul#include <dev/mii/mii.h> 12650675Swpaul#include <dev/mii/miivar.h> 12750675Swpaul 12851089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 12950675Swpaul#include "miibus_if.h" 13050675Swpaul 13141502Swpaul#define WB_USEIOSPACE 13241502Swpaul 13341502Swpaul#include <pci/if_wbreg.h> 13441502Swpaul 13541502Swpaul#ifndef lint 13641633Sarchiestatic const char rcsid[] = 13750477Speter "$FreeBSD: head/sys/pci/if_wb.c 51533 1999-09-22 06:08:11Z wpaul $"; 13841502Swpaul#endif 13941502Swpaul 14041502Swpaul/* 14141502Swpaul * Various supported device vendors/types and their names. 14241502Swpaul */ 14341502Swpaulstatic struct wb_type wb_devs[] = { 14441502Swpaul { WB_VENDORID, WB_DEVICEID_840F, 14541502Swpaul "Winbond W89C840F 10/100BaseTX" }, 14641502Swpaul { CP_VENDORID, CP_DEVICEID_RL100, 14741502Swpaul "Compex RL100-ATX 10/100baseTX" }, 14841502Swpaul { 0, 0, NULL } 14941502Swpaul}; 15041502Swpaul 15149611Swpaulstatic int wb_probe __P((device_t)); 15249611Swpaulstatic int wb_attach __P((device_t)); 15349611Swpaulstatic int wb_detach __P((device_t)); 15441502Swpaul 15550675Swpaulstatic void wb_bfree __P((caddr_t, u_int)); 15641502Swpaulstatic int wb_newbuf __P((struct wb_softc *, 15748745Swpaul struct wb_chain_onefrag *, 15848745Swpaul struct mbuf *)); 15941502Swpaulstatic int wb_encap __P((struct wb_softc *, struct wb_chain *, 16050675Swpaul struct mbuf *)); 16141502Swpaul 16241502Swpaulstatic void wb_rxeof __P((struct wb_softc *)); 16341502Swpaulstatic void wb_rxeoc __P((struct wb_softc *)); 16441502Swpaulstatic void wb_txeof __P((struct wb_softc *)); 16541502Swpaulstatic void wb_txeoc __P((struct wb_softc *)); 16641502Swpaulstatic void wb_intr __P((void *)); 16750675Swpaulstatic void wb_tick __P((void *)); 16841502Swpaulstatic void wb_start __P((struct ifnet *)); 16941502Swpaulstatic int wb_ioctl __P((struct ifnet *, u_long, caddr_t)); 17041502Swpaulstatic void wb_init __P((void *)); 17141502Swpaulstatic void wb_stop __P((struct wb_softc *)); 17241502Swpaulstatic void wb_watchdog __P((struct ifnet *)); 17349611Swpaulstatic void wb_shutdown __P((device_t)); 17441502Swpaulstatic int wb_ifmedia_upd __P((struct ifnet *)); 17541502Swpaulstatic void wb_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 17641502Swpaul 17742718Swpaulstatic void wb_eeprom_putbyte __P((struct wb_softc *, int)); 17842718Swpaulstatic void wb_eeprom_getword __P((struct wb_softc *, int, u_int16_t *)); 17941502Swpaulstatic void wb_read_eeprom __P((struct wb_softc *, caddr_t, int, 18041502Swpaul int, int)); 18141502Swpaulstatic void wb_mii_sync __P((struct wb_softc *)); 18241502Swpaulstatic void wb_mii_send __P((struct wb_softc *, u_int32_t, int)); 18341502Swpaulstatic int wb_mii_readreg __P((struct wb_softc *, struct wb_mii_frame *)); 18441502Swpaulstatic int wb_mii_writereg __P((struct wb_softc *, struct wb_mii_frame *)); 18541502Swpaul 18650675Swpaulstatic void wb_setcfg __P((struct wb_softc *, u_int32_t)); 18742718Swpaulstatic u_int8_t wb_calchash __P((caddr_t)); 18841502Swpaulstatic void wb_setmulti __P((struct wb_softc *)); 18941502Swpaulstatic void wb_reset __P((struct wb_softc *)); 19050675Swpaulstatic void wb_fixmedia __P((struct wb_softc *)); 19141502Swpaulstatic int wb_list_rx_init __P((struct wb_softc *)); 19241502Swpaulstatic int wb_list_tx_init __P((struct wb_softc *)); 19341502Swpaul 19450675Swpaulstatic int wb_miibus_readreg __P((device_t, int, int)); 19550675Swpaulstatic int wb_miibus_writereg __P((device_t, int, int, int)); 19650675Swpaulstatic void wb_miibus_statchg __P((device_t)); 19750675Swpaul 19849611Swpaul#ifdef WB_USEIOSPACE 19949611Swpaul#define WB_RES SYS_RES_IOPORT 20049611Swpaul#define WB_RID WB_PCI_LOIO 20149611Swpaul#else 20249611Swpaul#define WB_RES SYS_RES_MEMORY 20349611Swpaul#define WB_RID WB_PCI_LOMEM 20449611Swpaul#endif 20549611Swpaul 20649611Swpaulstatic device_method_t wb_methods[] = { 20749611Swpaul /* Device interface */ 20849611Swpaul DEVMETHOD(device_probe, wb_probe), 20949611Swpaul DEVMETHOD(device_attach, wb_attach), 21049611Swpaul DEVMETHOD(device_detach, wb_detach), 21149611Swpaul DEVMETHOD(device_shutdown, wb_shutdown), 21250675Swpaul 21350675Swpaul /* bus interface, for miibus */ 21450675Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 21550675Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 21650675Swpaul 21750675Swpaul /* MII interface */ 21850675Swpaul DEVMETHOD(miibus_readreg, wb_miibus_readreg), 21950675Swpaul DEVMETHOD(miibus_writereg, wb_miibus_writereg), 22050675Swpaul DEVMETHOD(miibus_statchg, wb_miibus_statchg), 22149611Swpaul { 0, 0 } 22249611Swpaul}; 22349611Swpaul 22449611Swpaulstatic driver_t wb_driver = { 22551455Swpaul "wb", 22649611Swpaul wb_methods, 22749611Swpaul sizeof(struct wb_softc) 22849611Swpaul}; 22949611Swpaul 23049611Swpaulstatic devclass_t wb_devclass; 23149611Swpaul 23251533SwpaulDRIVER_MODULE(if_wb, pci, wb_driver, wb_devclass, 0, 0); 23351473SwpaulDRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); 23449611Swpaul 23541502Swpaul#define WB_SETBIT(sc, reg, x) \ 23641502Swpaul CSR_WRITE_4(sc, reg, \ 23741502Swpaul CSR_READ_4(sc, reg) | x) 23841502Swpaul 23941502Swpaul#define WB_CLRBIT(sc, reg, x) \ 24041502Swpaul CSR_WRITE_4(sc, reg, \ 24141502Swpaul CSR_READ_4(sc, reg) & ~x) 24241502Swpaul 24341502Swpaul#define SIO_SET(x) \ 24441502Swpaul CSR_WRITE_4(sc, WB_SIO, \ 24541502Swpaul CSR_READ_4(sc, WB_SIO) | x) 24641502Swpaul 24741502Swpaul#define SIO_CLR(x) \ 24841502Swpaul CSR_WRITE_4(sc, WB_SIO, \ 24941502Swpaul CSR_READ_4(sc, WB_SIO) & ~x) 25041502Swpaul 25141502Swpaul/* 25241502Swpaul * Send a read command and address to the EEPROM, check for ACK. 25341502Swpaul */ 25441502Swpaulstatic void wb_eeprom_putbyte(sc, addr) 25541502Swpaul struct wb_softc *sc; 25642718Swpaul int addr; 25741502Swpaul{ 25841502Swpaul register int d, i; 25941502Swpaul 26041502Swpaul d = addr | WB_EECMD_READ; 26141502Swpaul 26241502Swpaul /* 26341502Swpaul * Feed in each bit and stobe the clock. 26441502Swpaul */ 26541502Swpaul for (i = 0x400; i; i >>= 1) { 26641502Swpaul if (d & i) { 26741502Swpaul SIO_SET(WB_SIO_EE_DATAIN); 26841502Swpaul } else { 26941502Swpaul SIO_CLR(WB_SIO_EE_DATAIN); 27041502Swpaul } 27141502Swpaul DELAY(100); 27241502Swpaul SIO_SET(WB_SIO_EE_CLK); 27341502Swpaul DELAY(150); 27441502Swpaul SIO_CLR(WB_SIO_EE_CLK); 27541502Swpaul DELAY(100); 27641502Swpaul } 27741502Swpaul 27841502Swpaul return; 27941502Swpaul} 28041502Swpaul 28141502Swpaul/* 28241502Swpaul * Read a word of data stored in the EEPROM at address 'addr.' 28341502Swpaul */ 28441502Swpaulstatic void wb_eeprom_getword(sc, addr, dest) 28541502Swpaul struct wb_softc *sc; 28642718Swpaul int addr; 28741502Swpaul u_int16_t *dest; 28841502Swpaul{ 28941502Swpaul register int i; 29041502Swpaul u_int16_t word = 0; 29141502Swpaul 29241502Swpaul /* Enter EEPROM access mode. */ 29341502Swpaul CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 29441502Swpaul 29541502Swpaul /* 29641502Swpaul * Send address of word we want to read. 29741502Swpaul */ 29841502Swpaul wb_eeprom_putbyte(sc, addr); 29941502Swpaul 30041502Swpaul CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 30141502Swpaul 30241502Swpaul /* 30341502Swpaul * Start reading bits from EEPROM. 30441502Swpaul */ 30541502Swpaul for (i = 0x8000; i; i >>= 1) { 30641502Swpaul SIO_SET(WB_SIO_EE_CLK); 30741502Swpaul DELAY(100); 30841502Swpaul if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) 30941502Swpaul word |= i; 31041502Swpaul SIO_CLR(WB_SIO_EE_CLK); 31141502Swpaul DELAY(100); 31241502Swpaul } 31341502Swpaul 31441502Swpaul /* Turn off EEPROM access mode. */ 31541502Swpaul CSR_WRITE_4(sc, WB_SIO, 0); 31641502Swpaul 31741502Swpaul *dest = word; 31841502Swpaul 31941502Swpaul return; 32041502Swpaul} 32141502Swpaul 32241502Swpaul/* 32341502Swpaul * Read a sequence of words from the EEPROM. 32441502Swpaul */ 32541502Swpaulstatic void wb_read_eeprom(sc, dest, off, cnt, swap) 32641502Swpaul struct wb_softc *sc; 32741502Swpaul caddr_t dest; 32841502Swpaul int off; 32941502Swpaul int cnt; 33041502Swpaul int swap; 33141502Swpaul{ 33241502Swpaul int i; 33341502Swpaul u_int16_t word = 0, *ptr; 33441502Swpaul 33541502Swpaul for (i = 0; i < cnt; i++) { 33641502Swpaul wb_eeprom_getword(sc, off + i, &word); 33741502Swpaul ptr = (u_int16_t *)(dest + (i * 2)); 33841502Swpaul if (swap) 33941502Swpaul *ptr = ntohs(word); 34041502Swpaul else 34141502Swpaul *ptr = word; 34241502Swpaul } 34341502Swpaul 34441502Swpaul return; 34541502Swpaul} 34641502Swpaul 34741502Swpaul/* 34841502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 34941502Swpaul */ 35041502Swpaulstatic void wb_mii_sync(sc) 35141502Swpaul struct wb_softc *sc; 35241502Swpaul{ 35341502Swpaul register int i; 35441502Swpaul 35541502Swpaul SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN); 35641502Swpaul 35741502Swpaul for (i = 0; i < 32; i++) { 35841502Swpaul SIO_SET(WB_SIO_MII_CLK); 35941502Swpaul DELAY(1); 36041502Swpaul SIO_CLR(WB_SIO_MII_CLK); 36141502Swpaul DELAY(1); 36241502Swpaul } 36341502Swpaul 36441502Swpaul return; 36541502Swpaul} 36641502Swpaul 36741502Swpaul/* 36841502Swpaul * Clock a series of bits through the MII. 36941502Swpaul */ 37041502Swpaulstatic void wb_mii_send(sc, bits, cnt) 37141502Swpaul struct wb_softc *sc; 37241502Swpaul u_int32_t bits; 37341502Swpaul int cnt; 37441502Swpaul{ 37541502Swpaul int i; 37641502Swpaul 37741502Swpaul SIO_CLR(WB_SIO_MII_CLK); 37841502Swpaul 37941502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 38041502Swpaul if (bits & i) { 38141502Swpaul SIO_SET(WB_SIO_MII_DATAIN); 38241502Swpaul } else { 38341502Swpaul SIO_CLR(WB_SIO_MII_DATAIN); 38441502Swpaul } 38541502Swpaul DELAY(1); 38641502Swpaul SIO_CLR(WB_SIO_MII_CLK); 38741502Swpaul DELAY(1); 38841502Swpaul SIO_SET(WB_SIO_MII_CLK); 38941502Swpaul } 39041502Swpaul} 39141502Swpaul 39241502Swpaul/* 39341502Swpaul * Read an PHY register through the MII. 39441502Swpaul */ 39541502Swpaulstatic int wb_mii_readreg(sc, frame) 39641502Swpaul struct wb_softc *sc; 39741502Swpaul struct wb_mii_frame *frame; 39841502Swpaul 39941502Swpaul{ 40041502Swpaul int i, ack, s; 40141502Swpaul 40241502Swpaul s = splimp(); 40341502Swpaul 40441502Swpaul /* 40541502Swpaul * Set up frame for RX. 40641502Swpaul */ 40741502Swpaul frame->mii_stdelim = WB_MII_STARTDELIM; 40841502Swpaul frame->mii_opcode = WB_MII_READOP; 40941502Swpaul frame->mii_turnaround = 0; 41041502Swpaul frame->mii_data = 0; 41141502Swpaul 41241502Swpaul CSR_WRITE_4(sc, WB_SIO, 0); 41341502Swpaul 41441502Swpaul /* 41541502Swpaul * Turn on data xmit. 41641502Swpaul */ 41741502Swpaul SIO_SET(WB_SIO_MII_DIR); 41841502Swpaul 41941502Swpaul wb_mii_sync(sc); 42041502Swpaul 42141502Swpaul /* 42241502Swpaul * Send command/address info. 42341502Swpaul */ 42441502Swpaul wb_mii_send(sc, frame->mii_stdelim, 2); 42541502Swpaul wb_mii_send(sc, frame->mii_opcode, 2); 42641502Swpaul wb_mii_send(sc, frame->mii_phyaddr, 5); 42741502Swpaul wb_mii_send(sc, frame->mii_regaddr, 5); 42841502Swpaul 42941502Swpaul /* Idle bit */ 43041502Swpaul SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN)); 43141502Swpaul DELAY(1); 43241502Swpaul SIO_SET(WB_SIO_MII_CLK); 43341502Swpaul DELAY(1); 43441502Swpaul 43541502Swpaul /* Turn off xmit. */ 43641502Swpaul SIO_CLR(WB_SIO_MII_DIR); 43741502Swpaul /* Check for ack */ 43841502Swpaul SIO_CLR(WB_SIO_MII_CLK); 43941502Swpaul DELAY(1); 44041502Swpaul SIO_SET(WB_SIO_MII_CLK); 44141502Swpaul DELAY(1); 44241502Swpaul ack = CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT; 44341502Swpaul SIO_CLR(WB_SIO_MII_CLK); 44441502Swpaul DELAY(1); 44541502Swpaul SIO_SET(WB_SIO_MII_CLK); 44641502Swpaul DELAY(1); 44741502Swpaul 44841502Swpaul /* 44941502Swpaul * Now try reading data bits. If the ack failed, we still 45041502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 45141502Swpaul */ 45241502Swpaul if (ack) { 45341502Swpaul for(i = 0; i < 16; i++) { 45441502Swpaul SIO_CLR(WB_SIO_MII_CLK); 45541502Swpaul DELAY(1); 45641502Swpaul SIO_SET(WB_SIO_MII_CLK); 45741502Swpaul DELAY(1); 45841502Swpaul } 45941502Swpaul goto fail; 46041502Swpaul } 46141502Swpaul 46241502Swpaul for (i = 0x8000; i; i >>= 1) { 46341502Swpaul SIO_CLR(WB_SIO_MII_CLK); 46441502Swpaul DELAY(1); 46541502Swpaul if (!ack) { 46641502Swpaul if (CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT) 46741502Swpaul frame->mii_data |= i; 46841502Swpaul DELAY(1); 46941502Swpaul } 47041502Swpaul SIO_SET(WB_SIO_MII_CLK); 47141502Swpaul DELAY(1); 47241502Swpaul } 47341502Swpaul 47441502Swpaulfail: 47541502Swpaul 47641502Swpaul SIO_CLR(WB_SIO_MII_CLK); 47741502Swpaul DELAY(1); 47841502Swpaul SIO_SET(WB_SIO_MII_CLK); 47941502Swpaul DELAY(1); 48041502Swpaul 48141502Swpaul splx(s); 48241502Swpaul 48341502Swpaul if (ack) 48441502Swpaul return(1); 48541502Swpaul return(0); 48641502Swpaul} 48741502Swpaul 48841502Swpaul/* 48941502Swpaul * Write to a PHY register through the MII. 49041502Swpaul */ 49141502Swpaulstatic int wb_mii_writereg(sc, frame) 49241502Swpaul struct wb_softc *sc; 49341502Swpaul struct wb_mii_frame *frame; 49441502Swpaul 49541502Swpaul{ 49641502Swpaul int s; 49741502Swpaul 49841502Swpaul s = splimp(); 49941502Swpaul /* 50041502Swpaul * Set up frame for TX. 50141502Swpaul */ 50241502Swpaul 50341502Swpaul frame->mii_stdelim = WB_MII_STARTDELIM; 50441502Swpaul frame->mii_opcode = WB_MII_WRITEOP; 50541502Swpaul frame->mii_turnaround = WB_MII_TURNAROUND; 50641502Swpaul 50741502Swpaul /* 50841502Swpaul * Turn on data output. 50941502Swpaul */ 51041502Swpaul SIO_SET(WB_SIO_MII_DIR); 51141502Swpaul 51241502Swpaul wb_mii_sync(sc); 51341502Swpaul 51441502Swpaul wb_mii_send(sc, frame->mii_stdelim, 2); 51541502Swpaul wb_mii_send(sc, frame->mii_opcode, 2); 51641502Swpaul wb_mii_send(sc, frame->mii_phyaddr, 5); 51741502Swpaul wb_mii_send(sc, frame->mii_regaddr, 5); 51841502Swpaul wb_mii_send(sc, frame->mii_turnaround, 2); 51941502Swpaul wb_mii_send(sc, frame->mii_data, 16); 52041502Swpaul 52141502Swpaul /* Idle bit. */ 52241502Swpaul SIO_SET(WB_SIO_MII_CLK); 52341502Swpaul DELAY(1); 52441502Swpaul SIO_CLR(WB_SIO_MII_CLK); 52541502Swpaul DELAY(1); 52641502Swpaul 52741502Swpaul /* 52841502Swpaul * Turn off xmit. 52941502Swpaul */ 53041502Swpaul SIO_CLR(WB_SIO_MII_DIR); 53141502Swpaul 53241502Swpaul splx(s); 53341502Swpaul 53441502Swpaul return(0); 53541502Swpaul} 53641502Swpaul 53750675Swpaulstatic int wb_miibus_readreg(dev, phy, reg) 53850675Swpaul device_t dev; 53950675Swpaul int phy, reg; 54050675Swpaul{ 54141502Swpaul struct wb_softc *sc; 54241502Swpaul struct wb_mii_frame frame; 54341502Swpaul 54450675Swpaul sc = device_get_softc(dev); 54550675Swpaul 54641502Swpaul bzero((char *)&frame, sizeof(frame)); 54741502Swpaul 54850675Swpaul frame.mii_phyaddr = phy; 54941502Swpaul frame.mii_regaddr = reg; 55041502Swpaul wb_mii_readreg(sc, &frame); 55141502Swpaul 55241502Swpaul return(frame.mii_data); 55341502Swpaul} 55441502Swpaul 55550675Swpaulstatic int wb_miibus_writereg(dev, phy, reg, data) 55650675Swpaul device_t dev; 55750675Swpaul int phy, reg, data; 55850675Swpaul{ 55941502Swpaul struct wb_softc *sc; 56041502Swpaul struct wb_mii_frame frame; 56141502Swpaul 56250675Swpaul sc = device_get_softc(dev); 56350675Swpaul 56441502Swpaul bzero((char *)&frame, sizeof(frame)); 56541502Swpaul 56650675Swpaul frame.mii_phyaddr = phy; 56741502Swpaul frame.mii_regaddr = reg; 56841502Swpaul frame.mii_data = data; 56941502Swpaul 57041502Swpaul wb_mii_writereg(sc, &frame); 57141502Swpaul 57250675Swpaul return(0); 57350675Swpaul} 57450675Swpaul 57550675Swpaulstatic void wb_miibus_statchg(dev) 57650675Swpaul device_t dev; 57750675Swpaul{ 57850675Swpaul struct wb_softc *sc; 57950675Swpaul struct mii_data *mii; 58050675Swpaul 58150675Swpaul sc = device_get_softc(dev); 58250675Swpaul mii = device_get_softc(sc->wb_miibus); 58350675Swpaul wb_setcfg(sc, mii->mii_media_active); 58450675Swpaul 58541502Swpaul return; 58641502Swpaul} 58741502Swpaul 58841502Swpaulstatic u_int8_t wb_calchash(addr) 58942718Swpaul caddr_t addr; 59041502Swpaul{ 59141502Swpaul u_int32_t crc, carry; 59241502Swpaul int i, j; 59341502Swpaul u_int8_t c; 59441502Swpaul 59541502Swpaul /* Compute CRC for the address value. */ 59641502Swpaul crc = 0xFFFFFFFF; /* initial value */ 59741502Swpaul 59841502Swpaul for (i = 0; i < 6; i++) { 59941502Swpaul c = *(addr + i); 60041502Swpaul for (j = 0; j < 8; j++) { 60141502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 60241502Swpaul crc <<= 1; 60341502Swpaul c >>= 1; 60441502Swpaul if (carry) 60541502Swpaul crc = (crc ^ 0x04c11db6) | carry; 60641502Swpaul } 60741502Swpaul } 60841502Swpaul 60941502Swpaul /* 61041502Swpaul * return the filter bit position 61141502Swpaul * Note: I arrived at the following nonsense 61241502Swpaul * through experimentation. It's not the usual way to 61341502Swpaul * generate the bit position but it's the only thing 61441502Swpaul * I could come up with that works. 61541502Swpaul */ 61641502Swpaul return(~(crc >> 26) & 0x0000003F); 61741502Swpaul} 61841502Swpaul 61941502Swpaul/* 62041502Swpaul * Program the 64-bit multicast hash filter. 62141502Swpaul */ 62241502Swpaulstatic void wb_setmulti(sc) 62341502Swpaul struct wb_softc *sc; 62441502Swpaul{ 62541502Swpaul struct ifnet *ifp; 62641502Swpaul int h = 0; 62741502Swpaul u_int32_t hashes[2] = { 0, 0 }; 62841502Swpaul struct ifmultiaddr *ifma; 62941502Swpaul u_int32_t rxfilt; 63041502Swpaul int mcnt = 0; 63141502Swpaul 63241502Swpaul ifp = &sc->arpcom.ac_if; 63341502Swpaul 63441502Swpaul rxfilt = CSR_READ_4(sc, WB_NETCFG); 63541502Swpaul 63641502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 63741502Swpaul rxfilt |= WB_NETCFG_RX_MULTI; 63841502Swpaul CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 63941502Swpaul CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); 64041502Swpaul CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); 64141502Swpaul return; 64241502Swpaul } 64341502Swpaul 64441502Swpaul /* first, zot all the existing hash bits */ 64541502Swpaul CSR_WRITE_4(sc, WB_MAR0, 0); 64641502Swpaul CSR_WRITE_4(sc, WB_MAR1, 0); 64741502Swpaul 64841502Swpaul /* now program new ones */ 64941502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 65041502Swpaul ifma = ifma->ifma_link.le_next) { 65141502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 65241502Swpaul continue; 65341502Swpaul h = wb_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 65441502Swpaul if (h < 32) 65541502Swpaul hashes[0] |= (1 << h); 65641502Swpaul else 65741502Swpaul hashes[1] |= (1 << (h - 32)); 65841502Swpaul mcnt++; 65941502Swpaul } 66041502Swpaul 66141502Swpaul if (mcnt) 66241502Swpaul rxfilt |= WB_NETCFG_RX_MULTI; 66341502Swpaul else 66441502Swpaul rxfilt &= ~WB_NETCFG_RX_MULTI; 66541502Swpaul 66641502Swpaul CSR_WRITE_4(sc, WB_MAR0, hashes[0]); 66741502Swpaul CSR_WRITE_4(sc, WB_MAR1, hashes[1]); 66841502Swpaul CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 66941502Swpaul 67041502Swpaul return; 67141502Swpaul} 67241502Swpaul 67341502Swpaul/* 67441502Swpaul * The Winbond manual states that in order to fiddle with the 67541502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 67641502Swpaul * first have to put the transmit and/or receive logic in the idle state. 67741502Swpaul */ 67850675Swpaulstatic void wb_setcfg(sc, media) 67941502Swpaul struct wb_softc *sc; 68050675Swpaul u_int32_t media; 68141502Swpaul{ 68241502Swpaul int i, restart = 0; 68341502Swpaul 68441502Swpaul if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { 68541502Swpaul restart = 1; 68641502Swpaul WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); 68741502Swpaul 68841502Swpaul for (i = 0; i < WB_TIMEOUT; i++) { 68941502Swpaul DELAY(10); 69041502Swpaul if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && 69141502Swpaul (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) 69241502Swpaul break; 69341502Swpaul } 69441502Swpaul 69541502Swpaul if (i == WB_TIMEOUT) 69641502Swpaul printf("wb%d: failed to force tx and " 69741502Swpaul "rx to idle state\n", sc->wb_unit); 69841502Swpaul } 69941502Swpaul 70050675Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) 70150675Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 70250675Swpaul else 70341502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 70441502Swpaul 70550675Swpaul if ((media & IFM_GMASK) == IFM_FDX) 70641502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 70741502Swpaul else 70841502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 70941502Swpaul 71041502Swpaul if (restart) 71141502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); 71241502Swpaul 71341502Swpaul return; 71441502Swpaul} 71541502Swpaul 71641502Swpaulstatic void wb_reset(sc) 71741502Swpaul struct wb_softc *sc; 71841502Swpaul{ 71941502Swpaul register int i; 72050675Swpaul struct mii_data *mii; 72141502Swpaul 72250675Swpaul CSR_WRITE_4(sc, WB_NETCFG, 0); 72350675Swpaul CSR_WRITE_4(sc, WB_BUSCTL, 0); 72450675Swpaul CSR_WRITE_4(sc, WB_TXADDR, 0); 72550675Swpaul CSR_WRITE_4(sc, WB_RXADDR, 0); 72650675Swpaul 72741502Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 72850675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 72941502Swpaul 73041502Swpaul for (i = 0; i < WB_TIMEOUT; i++) { 73141502Swpaul DELAY(10); 73241502Swpaul if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) 73341502Swpaul break; 73441502Swpaul } 73541502Swpaul if (i == WB_TIMEOUT) 73641502Swpaul printf("wb%d: reset never completed!\n", sc->wb_unit); 73741502Swpaul 73841502Swpaul /* Wait a little while for the chip to get its brains in order. */ 73941502Swpaul DELAY(1000); 74041502Swpaul 74150675Swpaul if (sc->wb_miibus == NULL) 74250675Swpaul return; 74341502Swpaul 74450675Swpaul mii = device_get_softc(sc->wb_miibus); 74550675Swpaul if (mii == NULL) 74650675Swpaul return; 74750675Swpaul 74850675Swpaul if (mii->mii_instance) { 74950675Swpaul struct mii_softc *miisc; 75050675Swpaul for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 75150675Swpaul miisc = LIST_NEXT(miisc, mii_list)) 75250675Swpaul mii_phy_reset(miisc); 75350675Swpaul } 75450675Swpaul 75541502Swpaul return; 75641502Swpaul} 75741502Swpaul 75850675Swpaulstatic void wb_fixmedia(sc) 75950675Swpaul struct wb_softc *sc; 76050675Swpaul{ 76150675Swpaul struct mii_data *mii = NULL; 76250675Swpaul struct ifnet *ifp; 76350675Swpaul u_int32_t media; 76450675Swpaul 76550675Swpaul if (sc->wb_miibus == NULL) 76650675Swpaul return; 76750675Swpaul 76850675Swpaul mii = device_get_softc(sc->wb_miibus); 76950675Swpaul ifp = &sc->arpcom.ac_if; 77050675Swpaul 77150675Swpaul mii_pollstat(mii); 77250675Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 77350675Swpaul media = mii->mii_media_active & ~IFM_10_T; 77450675Swpaul media |= IFM_100_TX; 77550675Swpaul } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 77650675Swpaul media = mii->mii_media_active & ~IFM_100_TX; 77750675Swpaul media |= IFM_10_T; 77850675Swpaul } else 77950675Swpaul return; 78050675Swpaul 78150675Swpaul ifmedia_set(&mii->mii_media, media); 78250675Swpaul 78350675Swpaul return; 78450675Swpaul} 78550675Swpaul 78641502Swpaul/* 78741502Swpaul * Probe for a Winbond chip. Check the PCI vendor and device 78841502Swpaul * IDs against our list and return a device name if we find a match. 78941502Swpaul */ 79049611Swpaulstatic int wb_probe(dev) 79149611Swpaul device_t dev; 79241502Swpaul{ 79341502Swpaul struct wb_type *t; 79441502Swpaul 79541502Swpaul t = wb_devs; 79641502Swpaul 79741502Swpaul while(t->wb_name != NULL) { 79849611Swpaul if ((pci_get_vendor(dev) == t->wb_vid) && 79949611Swpaul (pci_get_device(dev) == t->wb_did)) { 80049611Swpaul device_set_desc(dev, t->wb_name); 80149611Swpaul return(0); 80241502Swpaul } 80341502Swpaul t++; 80441502Swpaul } 80541502Swpaul 80649611Swpaul return(ENXIO); 80741502Swpaul} 80841502Swpaul 80941502Swpaul/* 81041502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 81141502Swpaul * setup and ethernet/BPF attach. 81241502Swpaul */ 81349611Swpaulstatic int wb_attach(dev) 81449611Swpaul device_t dev; 81541502Swpaul{ 81650675Swpaul int s; 81741502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 81841502Swpaul u_int32_t command; 81941502Swpaul struct wb_softc *sc; 82041502Swpaul struct ifnet *ifp; 82149611Swpaul int unit, error = 0, rid; 82241502Swpaul 82341502Swpaul s = splimp(); 82441502Swpaul 82549611Swpaul sc = device_get_softc(dev); 82649611Swpaul unit = device_get_unit(dev); 82741502Swpaul 82841502Swpaul /* 82941502Swpaul * Handle power management nonsense. 83041502Swpaul */ 83141502Swpaul 83249611Swpaul command = pci_read_config(dev, WB_PCI_CAPID, 4) & 0x000000FF; 83341502Swpaul if (command == 0x01) { 83441502Swpaul 83549611Swpaul command = pci_read_config(dev, WB_PCI_PWRMGMTCTRL, 4); 83641502Swpaul if (command & WB_PSTATE_MASK) { 83741502Swpaul u_int32_t iobase, membase, irq; 83841502Swpaul 83941502Swpaul /* Save important PCI config data. */ 84049611Swpaul iobase = pci_read_config(dev, WB_PCI_LOIO, 4); 84149611Swpaul membase = pci_read_config(dev, WB_PCI_LOMEM, 4); 84249611Swpaul irq = pci_read_config(dev, WB_PCI_INTLINE, 4); 84341502Swpaul 84441502Swpaul /* Reset the power state. */ 84541502Swpaul printf("wb%d: chip is in D%d power mode " 84641502Swpaul "-- setting to D0\n", unit, command & WB_PSTATE_MASK); 84741502Swpaul command &= 0xFFFFFFFC; 84849611Swpaul pci_write_config(dev, WB_PCI_PWRMGMTCTRL, command, 4); 84941502Swpaul 85041502Swpaul /* Restore PCI config data. */ 85149611Swpaul pci_write_config(dev, WB_PCI_LOIO, iobase, 4); 85249611Swpaul pci_write_config(dev, WB_PCI_LOMEM, membase, 4); 85349611Swpaul pci_write_config(dev, WB_PCI_INTLINE, irq, 4); 85441502Swpaul } 85541502Swpaul } 85641502Swpaul 85741502Swpaul /* 85841502Swpaul * Map control/status registers. 85941502Swpaul */ 86049611Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 86141502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 86249611Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 86349611Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 86441502Swpaul 86541502Swpaul#ifdef WB_USEIOSPACE 86641502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 86741502Swpaul printf("wb%d: failed to enable I/O ports!\n", unit); 86849611Swpaul error = ENXIO; 86941502Swpaul goto fail; 87041502Swpaul } 87141502Swpaul#else 87241502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 87341502Swpaul printf("wb%d: failed to enable memory mapping!\n", unit); 87449611Swpaul error = ENXIO; 87541502Swpaul goto fail; 87641502Swpaul } 87749611Swpaul#endif 87841502Swpaul 87949611Swpaul rid = WB_RID; 88049611Swpaul sc->wb_res = bus_alloc_resource(dev, WB_RES, &rid, 88149611Swpaul 0, ~0, 1, RF_ACTIVE); 88249611Swpaul 88349611Swpaul if (sc->wb_res == NULL) { 88449611Swpaul printf("wb%d: couldn't map ports/memory\n", unit); 88549611Swpaul error = ENXIO; 88641502Swpaul goto fail; 88741502Swpaul } 88841502Swpaul 88949611Swpaul sc->wb_btag = rman_get_bustag(sc->wb_res); 89049611Swpaul sc->wb_bhandle = rman_get_bushandle(sc->wb_res); 89149611Swpaul 89241502Swpaul /* Allocate interrupt */ 89349611Swpaul rid = 0; 89449611Swpaul sc->wb_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 89549611Swpaul RF_SHAREABLE | RF_ACTIVE); 89649611Swpaul 89749611Swpaul if (sc->wb_irq == NULL) { 89841502Swpaul printf("wb%d: couldn't map interrupt\n", unit); 89949611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 90049611Swpaul error = ENXIO; 90141502Swpaul goto fail; 90241502Swpaul } 90341502Swpaul 90449611Swpaul error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET, 90549611Swpaul wb_intr, sc, &sc->wb_intrhand); 90649611Swpaul 90749611Swpaul if (error) { 90849611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 90949611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 91049611Swpaul printf("wb%d: couldn't set up irq\n", unit); 91149611Swpaul goto fail; 91249611Swpaul } 91350675Swpaul 91450675Swpaul /* Save the cache line size. */ 91550675Swpaul sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; 91650675Swpaul 91741502Swpaul /* Reset the adapter. */ 91841502Swpaul wb_reset(sc); 91941502Swpaul 92041502Swpaul /* 92141502Swpaul * Get station address from the EEPROM. 92241502Swpaul */ 92341502Swpaul wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); 92441502Swpaul 92541502Swpaul /* 92641502Swpaul * A Winbond chip was detected. Inform the world. 92741502Swpaul */ 92841502Swpaul printf("wb%d: Ethernet address: %6D\n", unit, eaddr, ":"); 92941502Swpaul 93041502Swpaul sc->wb_unit = unit; 93141502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 93241502Swpaul 93350675Swpaul sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, 93450675Swpaul M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0); 93550675Swpaul 93650675Swpaul if (sc->wb_ldata == NULL) { 93741502Swpaul printf("wb%d: no memory for list buffers!\n", unit); 93849611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 93949611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 94049611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 94149611Swpaul error = ENXIO; 94249611Swpaul goto fail; 94341502Swpaul } 94441502Swpaul 94541502Swpaul bzero(sc->wb_ldata, sizeof(struct wb_list_data)); 94641502Swpaul 94741502Swpaul ifp = &sc->arpcom.ac_if; 94841502Swpaul ifp->if_softc = sc; 94941502Swpaul ifp->if_unit = unit; 95041502Swpaul ifp->if_name = "wb"; 95141502Swpaul ifp->if_mtu = ETHERMTU; 95241502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 95341502Swpaul ifp->if_ioctl = wb_ioctl; 95441502Swpaul ifp->if_output = ether_output; 95541502Swpaul ifp->if_start = wb_start; 95641502Swpaul ifp->if_watchdog = wb_watchdog; 95741502Swpaul ifp->if_init = wb_init; 95841502Swpaul ifp->if_baudrate = 10000000; 95943515Swpaul ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; 96041502Swpaul 96150675Swpaul /* 96250675Swpaul * Do MII setup. 96350675Swpaul */ 96450675Swpaul if (mii_phy_probe(dev, &sc->wb_miibus, 96550675Swpaul wb_ifmedia_upd, wb_ifmedia_sts)) { 96649611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 96749611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 96849611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 96949611Swpaul free(sc->wb_ldata_ptr, M_DEVBUF); 97049611Swpaul error = ENXIO; 97141502Swpaul goto fail; 97241502Swpaul } 97341502Swpaul 97441502Swpaul /* 97541502Swpaul * Call MI attach routines. 97641502Swpaul */ 97741502Swpaul if_attach(ifp); 97841502Swpaul ether_ifattach(ifp); 97941502Swpaul 98048645Sdes#if NBPF > 0 98141502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 98241502Swpaul#endif 98341502Swpaul 98441502Swpaulfail: 98550675Swpaul if (error) 98650675Swpaul device_delete_child(dev, sc->wb_miibus); 98741502Swpaul splx(s); 98850675Swpaul 98949611Swpaul return(error); 99041502Swpaul} 99141502Swpaul 99249611Swpaulstatic int wb_detach(dev) 99349611Swpaul device_t dev; 99449611Swpaul{ 99549611Swpaul struct wb_softc *sc; 99649611Swpaul struct ifnet *ifp; 99749611Swpaul int s; 99849611Swpaul 99949611Swpaul s = splimp(); 100049611Swpaul 100149611Swpaul sc = device_get_softc(dev); 100249611Swpaul ifp = &sc->arpcom.ac_if; 100349611Swpaul 100449611Swpaul wb_stop(sc); 100549611Swpaul if_detach(ifp); 100649611Swpaul 100750675Swpaul /* Delete any miibus and phy devices attached to this interface */ 100850675Swpaul bus_generic_detach(dev); 100950675Swpaul device_delete_child(dev, sc->wb_miibus); 101050675Swpaul 101149611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 101249611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 101349611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 101449611Swpaul 101549611Swpaul free(sc->wb_ldata_ptr, M_DEVBUF); 101649611Swpaul 101749611Swpaul splx(s); 101849611Swpaul 101949611Swpaul return(0); 102049611Swpaul} 102149611Swpaul 102241502Swpaul/* 102341502Swpaul * Initialize the transmit descriptors. 102441502Swpaul */ 102541502Swpaulstatic int wb_list_tx_init(sc) 102641502Swpaul struct wb_softc *sc; 102741502Swpaul{ 102841502Swpaul struct wb_chain_data *cd; 102941502Swpaul struct wb_list_data *ld; 103041502Swpaul int i; 103141502Swpaul 103241502Swpaul cd = &sc->wb_cdata; 103341502Swpaul ld = sc->wb_ldata; 103441502Swpaul 103541502Swpaul for (i = 0; i < WB_TX_LIST_CNT; i++) { 103641502Swpaul cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; 103741502Swpaul if (i == (WB_TX_LIST_CNT - 1)) { 103841502Swpaul cd->wb_tx_chain[i].wb_nextdesc = 103941502Swpaul &cd->wb_tx_chain[0]; 104041502Swpaul } else { 104141502Swpaul cd->wb_tx_chain[i].wb_nextdesc = 104241502Swpaul &cd->wb_tx_chain[i + 1]; 104341502Swpaul } 104441502Swpaul } 104541502Swpaul 104641502Swpaul cd->wb_tx_free = &cd->wb_tx_chain[0]; 104741502Swpaul cd->wb_tx_tail = cd->wb_tx_head = NULL; 104841502Swpaul 104941502Swpaul return(0); 105041502Swpaul} 105141502Swpaul 105241502Swpaul 105341502Swpaul/* 105441502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 105541502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 105641502Swpaul * points back to the first. 105741502Swpaul */ 105841502Swpaulstatic int wb_list_rx_init(sc) 105941502Swpaul struct wb_softc *sc; 106041502Swpaul{ 106141502Swpaul struct wb_chain_data *cd; 106241502Swpaul struct wb_list_data *ld; 106341502Swpaul int i; 106441502Swpaul 106541502Swpaul cd = &sc->wb_cdata; 106641502Swpaul ld = sc->wb_ldata; 106741502Swpaul 106841502Swpaul for (i = 0; i < WB_RX_LIST_CNT; i++) { 106941502Swpaul cd->wb_rx_chain[i].wb_ptr = 107041502Swpaul (struct wb_desc *)&ld->wb_rx_list[i]; 107150675Swpaul cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; 107248745Swpaul if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) 107341502Swpaul return(ENOBUFS); 107441502Swpaul if (i == (WB_RX_LIST_CNT - 1)) { 107541502Swpaul cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; 107641502Swpaul ld->wb_rx_list[i].wb_next = 107741502Swpaul vtophys(&ld->wb_rx_list[0]); 107841502Swpaul } else { 107941502Swpaul cd->wb_rx_chain[i].wb_nextdesc = 108041502Swpaul &cd->wb_rx_chain[i + 1]; 108141502Swpaul ld->wb_rx_list[i].wb_next = 108241502Swpaul vtophys(&ld->wb_rx_list[i + 1]); 108341502Swpaul } 108441502Swpaul } 108541502Swpaul 108641502Swpaul cd->wb_rx_head = &cd->wb_rx_chain[0]; 108741502Swpaul 108841502Swpaul return(0); 108941502Swpaul} 109041502Swpaul 109150675Swpaulstatic void wb_bfree(buf, size) 109250675Swpaul caddr_t buf; 109350675Swpaul u_int size; 109450675Swpaul{ 109550675Swpaul return; 109650675Swpaul} 109750675Swpaul 109841502Swpaul/* 109941502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 110041502Swpaul */ 110148745Swpaulstatic int wb_newbuf(sc, c, m) 110241502Swpaul struct wb_softc *sc; 110341502Swpaul struct wb_chain_onefrag *c; 110448745Swpaul struct mbuf *m; 110541502Swpaul{ 110641502Swpaul struct mbuf *m_new = NULL; 110741502Swpaul 110848745Swpaul if (m == NULL) { 110948745Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 111048745Swpaul if (m_new == NULL) { 111148745Swpaul printf("wb%d: no memory for rx " 111248745Swpaul "list -- packet dropped!\n", sc->wb_unit); 111348745Swpaul return(ENOBUFS); 111448745Swpaul } 111541502Swpaul 111650675Swpaul m_new->m_data = m_new->m_ext.ext_buf = c->wb_buf; 111750675Swpaul m_new->m_flags |= M_EXT; 111850675Swpaul m_new->m_ext.ext_size = m_new->m_pkthdr.len = 111950675Swpaul m_new->m_len = WB_BUFBYTES; 112050675Swpaul m_new->m_ext.ext_free = wb_bfree; 112150675Swpaul m_new->m_ext.ext_ref = wb_bfree; 112248745Swpaul } else { 112348745Swpaul m_new = m; 112450675Swpaul m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; 112548745Swpaul m_new->m_data = m_new->m_ext.ext_buf; 112641502Swpaul } 112741502Swpaul 112848745Swpaul m_adj(m_new, sizeof(u_int64_t)); 112948745Swpaul 113041502Swpaul c->wb_mbuf = m_new; 113141502Swpaul c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); 113250675Swpaul c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; 113341502Swpaul c->wb_ptr->wb_status = WB_RXSTAT; 113441502Swpaul 113541502Swpaul return(0); 113641502Swpaul} 113741502Swpaul 113841502Swpaul/* 113941502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 114041502Swpaul * the higher level protocols. 114141502Swpaul */ 114241502Swpaulstatic void wb_rxeof(sc) 114341502Swpaul struct wb_softc *sc; 114441502Swpaul{ 114541502Swpaul struct ether_header *eh; 114650675Swpaul struct mbuf *m = NULL; 114741502Swpaul struct ifnet *ifp; 114841502Swpaul struct wb_chain_onefrag *cur_rx; 114941502Swpaul int total_len = 0; 115041502Swpaul u_int32_t rxstat; 115141502Swpaul 115241502Swpaul ifp = &sc->arpcom.ac_if; 115341502Swpaul 115441502Swpaul while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & 115541502Swpaul WB_RXSTAT_OWN)) { 115648745Swpaul struct mbuf *m0 = NULL; 115748745Swpaul 115841502Swpaul cur_rx = sc->wb_cdata.wb_rx_head; 115941502Swpaul sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; 116050675Swpaul 116148745Swpaul m = cur_rx->wb_mbuf; 116241502Swpaul 116350675Swpaul if ((rxstat & WB_RXSTAT_MIIERR) || 116450675Swpaul (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || 116550675Swpaul (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || 116650675Swpaul !(rxstat & WB_RXSTAT_LASTFRAG) || 116750675Swpaul !(rxstat & WB_RXSTAT_RXCMP)) { 116841502Swpaul ifp->if_ierrors++; 116950675Swpaul wb_newbuf(sc, cur_rx, m); 117041502Swpaul printf("wb%x: receiver babbling: possible chip " 117141502Swpaul "bug, forcing reset\n", sc->wb_unit); 117250675Swpaul wb_fixmedia(sc); 117350675Swpaul wb_reset(sc); 117450675Swpaul wb_init(sc); 117541502Swpaul return; 117641502Swpaul } 117741502Swpaul 117842718Swpaul if (rxstat & WB_RXSTAT_RXERR) { 117942718Swpaul ifp->if_ierrors++; 118048745Swpaul wb_newbuf(sc, cur_rx, m); 118150675Swpaul break; 118242718Swpaul } 118342718Swpaul 118441502Swpaul /* No errors; receive the packet. */ 118541502Swpaul total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); 118641502Swpaul 118741502Swpaul /* 118841934Swpaul * XXX The Winbond chip includes the CRC with every 118941934Swpaul * received frame, and there's no way to turn this 119041934Swpaul * behavior off (at least, I can't find anything in 119141934Swpaul * the manual that explains how to do it) so we have 119241934Swpaul * to trim off the CRC manually. 119341934Swpaul */ 119441934Swpaul total_len -= ETHER_CRC_LEN; 119541934Swpaul 119648745Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 119748745Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 119848745Swpaul wb_newbuf(sc, cur_rx, m); 119948745Swpaul if (m0 == NULL) { 120048745Swpaul ifp->if_ierrors++; 120150675Swpaul break; 120241502Swpaul } 120348745Swpaul m_adj(m0, ETHER_ALIGN); 120448745Swpaul m = m0; 120541502Swpaul 120641502Swpaul ifp->if_ipackets++; 120741502Swpaul eh = mtod(m, struct ether_header *); 120841502Swpaul 120948745Swpaul#ifdef BRIDGE 121048745Swpaul if (do_bridge) { 121148745Swpaul struct ifnet *bdg_ifp; 121248745Swpaul bdg_ifp = bridge_in(m); 121348745Swpaul if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_DROP) 121448745Swpaul bdg_forward(&m, bdg_ifp); 121548745Swpaul if (((bdg_ifp != BDG_LOCAL) && (bdg_ifp != BDG_BCAST) && 121648745Swpaul (bdg_ifp != BDG_MCAST)) || bdg_ifp == BDG_DROP) { 121748745Swpaul m_freem(m); 121850675Swpaul break; 121948745Swpaul } 122048745Swpaul } 122148745Swpaul#endif 122248745Swpaul 122348645Sdes#if NBPF > 0 122441502Swpaul /* 122541502Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 122641502Swpaul * don't pass it up to the ether_input() layer unless it's 122741502Swpaul * a broadcast packet, multicast packet, matches our ethernet 122841502Swpaul * address or the interface is in promiscuous mode. 122941502Swpaul */ 123041502Swpaul if (ifp->if_bpf) { 123141502Swpaul bpf_mtap(ifp, m); 123241502Swpaul if (ifp->if_flags & IFF_PROMISC && 123341502Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 123441502Swpaul ETHER_ADDR_LEN) && 123541502Swpaul (eh->ether_dhost[0] & 1) == 0)) { 123641502Swpaul m_freem(m); 123750675Swpaul break; 123841502Swpaul } 123941502Swpaul } 124041502Swpaul#endif 124141502Swpaul /* Remove header from mbuf and pass it on. */ 124241502Swpaul m_adj(m, sizeof(struct ether_header)); 124341502Swpaul ether_input(ifp, eh, m); 124441502Swpaul } 124541502Swpaul 124641502Swpaul return; 124741502Swpaul} 124841502Swpaul 124941502Swpaulvoid wb_rxeoc(sc) 125041502Swpaul struct wb_softc *sc; 125141502Swpaul{ 125241502Swpaul wb_rxeof(sc); 125341502Swpaul 125441502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 125541502Swpaul CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 125641502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 125741502Swpaul if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) 125841502Swpaul CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 125941502Swpaul 126041502Swpaul return; 126141502Swpaul} 126241502Swpaul 126341502Swpaul/* 126441502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 126541502Swpaul * the list buffers. 126641502Swpaul */ 126741502Swpaulstatic void wb_txeof(sc) 126841502Swpaul struct wb_softc *sc; 126941502Swpaul{ 127041502Swpaul struct wb_chain *cur_tx; 127141502Swpaul struct ifnet *ifp; 127241502Swpaul 127341502Swpaul ifp = &sc->arpcom.ac_if; 127441502Swpaul 127541502Swpaul /* Clear the timeout timer. */ 127641502Swpaul ifp->if_timer = 0; 127741502Swpaul 127841502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) 127941502Swpaul return; 128041502Swpaul 128141502Swpaul /* 128241502Swpaul * Go through our tx list and free mbufs for those 128341502Swpaul * frames that have been transmitted. 128441502Swpaul */ 128541502Swpaul while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { 128641502Swpaul u_int32_t txstat; 128741502Swpaul 128841502Swpaul cur_tx = sc->wb_cdata.wb_tx_head; 128941502Swpaul txstat = WB_TXSTATUS(cur_tx); 129041502Swpaul 129141502Swpaul if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) 129241502Swpaul break; 129341502Swpaul 129441502Swpaul if (txstat & WB_TXSTAT_TXERR) { 129541502Swpaul ifp->if_oerrors++; 129641502Swpaul if (txstat & WB_TXSTAT_ABORT) 129741502Swpaul ifp->if_collisions++; 129841502Swpaul if (txstat & WB_TXSTAT_LATECOLL) 129941502Swpaul ifp->if_collisions++; 130041502Swpaul } 130141502Swpaul 130241502Swpaul ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; 130341502Swpaul 130441502Swpaul ifp->if_opackets++; 130541502Swpaul m_freem(cur_tx->wb_mbuf); 130641502Swpaul cur_tx->wb_mbuf = NULL; 130741502Swpaul 130841502Swpaul if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { 130941502Swpaul sc->wb_cdata.wb_tx_head = NULL; 131041502Swpaul sc->wb_cdata.wb_tx_tail = NULL; 131141502Swpaul break; 131241502Swpaul } 131341502Swpaul 131441502Swpaul sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; 131541502Swpaul } 131641502Swpaul 131741502Swpaul return; 131841502Swpaul} 131941502Swpaul 132041502Swpaul/* 132141502Swpaul * TX 'end of channel' interrupt handler. 132241502Swpaul */ 132341502Swpaulstatic void wb_txeoc(sc) 132441502Swpaul struct wb_softc *sc; 132541502Swpaul{ 132641502Swpaul struct ifnet *ifp; 132741502Swpaul 132841502Swpaul ifp = &sc->arpcom.ac_if; 132941502Swpaul 133041502Swpaul ifp->if_timer = 0; 133141502Swpaul 133241502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) { 133341502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 133441502Swpaul sc->wb_cdata.wb_tx_tail = NULL; 133541502Swpaul } else { 133641502Swpaul if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { 133741502Swpaul WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; 133841502Swpaul ifp->if_timer = 5; 133941502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 134041502Swpaul } 134141502Swpaul } 134241502Swpaul 134341502Swpaul return; 134441502Swpaul} 134541502Swpaul 134641502Swpaulstatic void wb_intr(arg) 134741502Swpaul void *arg; 134841502Swpaul{ 134941502Swpaul struct wb_softc *sc; 135041502Swpaul struct ifnet *ifp; 135141502Swpaul u_int32_t status; 135241502Swpaul 135341502Swpaul sc = arg; 135441502Swpaul ifp = &sc->arpcom.ac_if; 135541502Swpaul 135641502Swpaul if (!(ifp->if_flags & IFF_UP)) 135741502Swpaul return; 135841502Swpaul 135941502Swpaul /* Disable interrupts. */ 136041502Swpaul CSR_WRITE_4(sc, WB_IMR, 0x00000000); 136141502Swpaul 136241502Swpaul for (;;) { 136341502Swpaul 136441502Swpaul status = CSR_READ_4(sc, WB_ISR); 136541502Swpaul if (status) 136641502Swpaul CSR_WRITE_4(sc, WB_ISR, status); 136741502Swpaul 136841502Swpaul if ((status & WB_INTRS) == 0) 136941502Swpaul break; 137041502Swpaul 137141502Swpaul if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { 137241502Swpaul ifp->if_ierrors++; 137341502Swpaul wb_reset(sc); 137450675Swpaul if (status & WB_ISR_RX_ERR) 137550675Swpaul wb_fixmedia(sc); 137641502Swpaul wb_init(sc); 137750675Swpaul continue; 137841502Swpaul } 137941502Swpaul 138050675Swpaul if (status & WB_ISR_RX_OK) 138150675Swpaul wb_rxeof(sc); 138250675Swpaul 138350675Swpaul if (status & WB_ISR_RX_IDLE) 138450675Swpaul wb_rxeoc(sc); 138550675Swpaul 138641502Swpaul if (status & WB_ISR_TX_OK) 138741502Swpaul wb_txeof(sc); 138841502Swpaul 138941502Swpaul if (status & WB_ISR_TX_NOBUF) 139041502Swpaul wb_txeoc(sc); 139141502Swpaul 139241502Swpaul if (status & WB_ISR_TX_IDLE) { 139341502Swpaul wb_txeof(sc); 139441502Swpaul if (sc->wb_cdata.wb_tx_head != NULL) { 139541502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 139641502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 139741502Swpaul } 139841502Swpaul } 139941502Swpaul 140041502Swpaul if (status & WB_ISR_TX_UNDERRUN) { 140141502Swpaul ifp->if_oerrors++; 140241502Swpaul wb_txeof(sc); 140341502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 140441502Swpaul /* Jack up TX threshold */ 140541502Swpaul sc->wb_txthresh += WB_TXTHRESH_CHUNK; 140641502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 140741502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 140841502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 140941502Swpaul } 141041502Swpaul 141141502Swpaul if (status & WB_ISR_BUS_ERR) { 141241502Swpaul wb_reset(sc); 141341502Swpaul wb_init(sc); 141441502Swpaul } 141541502Swpaul 141641502Swpaul } 141741502Swpaul 141841502Swpaul /* Re-enable interrupts. */ 141941502Swpaul CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 142041502Swpaul 142141502Swpaul if (ifp->if_snd.ifq_head != NULL) { 142241502Swpaul wb_start(ifp); 142341502Swpaul } 142441502Swpaul 142541502Swpaul return; 142641502Swpaul} 142741502Swpaul 142850675Swpaulstatic void wb_tick(xsc) 142950675Swpaul void *xsc; 143050675Swpaul{ 143150675Swpaul struct wb_softc *sc; 143250675Swpaul struct mii_data *mii; 143350685Swpaul int s; 143450675Swpaul 143550685Swpaul s = splimp(); 143650685Swpaul 143750675Swpaul sc = xsc; 143850675Swpaul mii = device_get_softc(sc->wb_miibus); 143950675Swpaul 144050675Swpaul mii_tick(mii); 144150675Swpaul 144250675Swpaul sc->wb_stat_ch = timeout(wb_tick, sc, hz); 144350675Swpaul 144450685Swpaul splx(s); 144550685Swpaul 144650675Swpaul return; 144750675Swpaul} 144850675Swpaul 144941502Swpaul/* 145041502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 145141502Swpaul * pointers to the fragment pointers. 145241502Swpaul */ 145341502Swpaulstatic int wb_encap(sc, c, m_head) 145441502Swpaul struct wb_softc *sc; 145541502Swpaul struct wb_chain *c; 145641502Swpaul struct mbuf *m_head; 145741502Swpaul{ 145841502Swpaul int frag = 0; 145941502Swpaul struct wb_desc *f = NULL; 146041502Swpaul int total_len; 146141502Swpaul struct mbuf *m; 146241502Swpaul 146341502Swpaul /* 146441502Swpaul * Start packing the mbufs in this chain into 146541502Swpaul * the fragment pointers. Stop when we run out 146641502Swpaul * of fragments or hit the end of the mbuf chain. 146741502Swpaul */ 146841502Swpaul m = m_head; 146941502Swpaul total_len = 0; 147041502Swpaul 147141502Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 147241502Swpaul if (m->m_len != 0) { 147341502Swpaul if (frag == WB_MAXFRAGS) 147441502Swpaul break; 147541502Swpaul total_len += m->m_len; 147641502Swpaul f = &c->wb_ptr->wb_frag[frag]; 147741502Swpaul f->wb_ctl = WB_TXCTL_TLINK | m->m_len; 147841502Swpaul if (frag == 0) { 147941502Swpaul f->wb_ctl |= WB_TXCTL_FIRSTFRAG; 148041502Swpaul f->wb_status = 0; 148141502Swpaul } else 148241502Swpaul f->wb_status = WB_TXSTAT_OWN; 148341502Swpaul f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); 148441502Swpaul f->wb_data = vtophys(mtod(m, vm_offset_t)); 148541502Swpaul frag++; 148641502Swpaul } 148741502Swpaul } 148841502Swpaul 148941502Swpaul /* 149041502Swpaul * Handle special case: we used up all 16 fragments, 149141502Swpaul * but we have more mbufs left in the chain. Copy the 149241502Swpaul * data into an mbuf cluster. Note that we don't 149341502Swpaul * bother clearing the values in the other fragment 149441502Swpaul * pointers/counters; it wouldn't gain us anything, 149541502Swpaul * and would waste cycles. 149641502Swpaul */ 149741502Swpaul if (m != NULL) { 149841502Swpaul struct mbuf *m_new = NULL; 149941502Swpaul 150041502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 150141502Swpaul if (m_new == NULL) { 150241502Swpaul printf("wb%d: no memory for tx list", sc->wb_unit); 150341502Swpaul return(1); 150441502Swpaul } 150541502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 150641502Swpaul MCLGET(m_new, M_DONTWAIT); 150741502Swpaul if (!(m_new->m_flags & M_EXT)) { 150841502Swpaul m_freem(m_new); 150941502Swpaul printf("wb%d: no memory for tx list", 151041502Swpaul sc->wb_unit); 151141502Swpaul return(1); 151241502Swpaul } 151341502Swpaul } 151441502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 151541502Swpaul mtod(m_new, caddr_t)); 151641502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 151741502Swpaul m_freem(m_head); 151841502Swpaul m_head = m_new; 151941502Swpaul f = &c->wb_ptr->wb_frag[0]; 152041502Swpaul f->wb_status = 0; 152141502Swpaul f->wb_data = vtophys(mtod(m_new, caddr_t)); 152241502Swpaul f->wb_ctl = total_len = m_new->m_len; 152341502Swpaul f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; 152441502Swpaul frag = 1; 152541502Swpaul } 152641502Swpaul 152741502Swpaul if (total_len < WB_MIN_FRAMELEN) { 152841502Swpaul f = &c->wb_ptr->wb_frag[frag]; 152941502Swpaul f->wb_ctl = WB_MIN_FRAMELEN - total_len; 153041502Swpaul f->wb_data = vtophys(&sc->wb_cdata.wb_pad); 153141502Swpaul f->wb_ctl |= WB_TXCTL_TLINK; 153241502Swpaul f->wb_status = WB_TXSTAT_OWN; 153341502Swpaul frag++; 153441502Swpaul } 153541502Swpaul 153641502Swpaul c->wb_mbuf = m_head; 153741502Swpaul c->wb_lastdesc = frag - 1; 153841502Swpaul WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; 153941502Swpaul WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); 154041502Swpaul 154141502Swpaul return(0); 154241502Swpaul} 154341502Swpaul 154441502Swpaul/* 154541502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 154641502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 154741502Swpaul * copy of the pointers since the transmit list fragment pointers are 154841502Swpaul * physical addresses. 154941502Swpaul */ 155041502Swpaul 155141502Swpaulstatic void wb_start(ifp) 155241502Swpaul struct ifnet *ifp; 155341502Swpaul{ 155441502Swpaul struct wb_softc *sc; 155541502Swpaul struct mbuf *m_head = NULL; 155641502Swpaul struct wb_chain *cur_tx = NULL, *start_tx; 155741502Swpaul 155841502Swpaul sc = ifp->if_softc; 155941502Swpaul 156041502Swpaul /* 156141502Swpaul * Check for an available queue slot. If there are none, 156241502Swpaul * punt. 156341502Swpaul */ 156441502Swpaul if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { 156541502Swpaul ifp->if_flags |= IFF_OACTIVE; 156641502Swpaul return; 156741502Swpaul } 156841502Swpaul 156941502Swpaul start_tx = sc->wb_cdata.wb_tx_free; 157041502Swpaul 157141502Swpaul while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { 157241502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 157341502Swpaul if (m_head == NULL) 157441502Swpaul break; 157541502Swpaul 157641502Swpaul /* Pick a descriptor off the free list. */ 157741502Swpaul cur_tx = sc->wb_cdata.wb_tx_free; 157841502Swpaul sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; 157941502Swpaul 158041502Swpaul /* Pack the data into the descriptor. */ 158141502Swpaul wb_encap(sc, cur_tx, m_head); 158241502Swpaul 158341502Swpaul if (cur_tx != start_tx) 158441502Swpaul WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; 158541502Swpaul 158648645Sdes#if NBPF > 0 158741502Swpaul /* 158841502Swpaul * If there's a BPF listener, bounce a copy of this frame 158941502Swpaul * to him. 159041502Swpaul */ 159141502Swpaul if (ifp->if_bpf) 159241502Swpaul bpf_mtap(ifp, cur_tx->wb_mbuf); 159341502Swpaul#endif 159441502Swpaul } 159541502Swpaul 159641502Swpaul /* 159741526Swpaul * If there are no packets queued, bail. 159841526Swpaul */ 159941526Swpaul if (cur_tx == NULL) 160041526Swpaul return; 160141526Swpaul 160241526Swpaul /* 160341502Swpaul * Place the request for the upload interrupt 160441502Swpaul * in the last descriptor in the chain. This way, if 160541502Swpaul * we're chaining several packets at once, we'll only 160641502Swpaul * get an interupt once for the whole chain rather than 160741502Swpaul * once for each packet. 160841502Swpaul */ 160941502Swpaul WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; 161042718Swpaul cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; 161141502Swpaul sc->wb_cdata.wb_tx_tail = cur_tx; 161241502Swpaul 161341502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) { 161441502Swpaul sc->wb_cdata.wb_tx_head = start_tx; 161541502Swpaul WB_TXOWN(start_tx) = WB_TXSTAT_OWN; 161641502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 161741502Swpaul } else { 161841502Swpaul /* 161941502Swpaul * We need to distinguish between the case where 162041502Swpaul * the own bit is clear because the chip cleared it 162141502Swpaul * and where the own bit is clear because we haven't 162241502Swpaul * set it yet. The magic value WB_UNSET is just some 162341502Swpaul * ramdomly chosen number which doesn't have the own 162441502Swpaul * bit set. When we actually transmit the frame, the 162541502Swpaul * status word will have _only_ the own bit set, so 162641502Swpaul * the txeoc handler will be able to tell if it needs 162741502Swpaul * to initiate another transmission to flush out pending 162841502Swpaul * frames. 162941502Swpaul */ 163041502Swpaul WB_TXOWN(start_tx) = WB_UNSENT; 163141502Swpaul } 163241502Swpaul 163341502Swpaul /* 163441502Swpaul * Set a timeout in case the chip goes out to lunch. 163541502Swpaul */ 163641502Swpaul ifp->if_timer = 5; 163741502Swpaul 163841502Swpaul return; 163941502Swpaul} 164041502Swpaul 164141502Swpaulstatic void wb_init(xsc) 164241502Swpaul void *xsc; 164341502Swpaul{ 164441502Swpaul struct wb_softc *sc = xsc; 164541502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 164641502Swpaul int s, i; 164750675Swpaul struct mii_data *mii; 164841502Swpaul 164941502Swpaul s = splimp(); 165041502Swpaul 165150675Swpaul mii = device_get_softc(sc->wb_miibus); 165241502Swpaul 165341502Swpaul /* 165441502Swpaul * Cancel pending I/O and free all RX/TX buffers. 165541502Swpaul */ 165641502Swpaul wb_stop(sc); 165741502Swpaul wb_reset(sc); 165841502Swpaul 165941502Swpaul sc->wb_txthresh = WB_TXTHRESH_INIT; 166041502Swpaul 166141502Swpaul /* 166241502Swpaul * Set cache alignment and burst length. 166341502Swpaul */ 166450675Swpaul#ifdef foo 166541502Swpaul CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); 166641502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 166741502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 166850675Swpaul#endif 166941502Swpaul 167050675Swpaul CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); 167150675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); 167250675Swpaul switch(sc->wb_cachesize) { 167350675Swpaul case 32: 167450675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); 167550675Swpaul break; 167650675Swpaul case 16: 167750675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); 167850675Swpaul break; 167950675Swpaul case 8: 168050675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); 168150675Swpaul break; 168250675Swpaul case 0: 168350675Swpaul default: 168450675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); 168550675Swpaul break; 168650675Swpaul } 168750675Swpaul 168841502Swpaul /* This doesn't tend to work too well at 100Mbps. */ 168941502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); 169041502Swpaul 169141502Swpaul /* Init our MAC address */ 169241502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) { 169341502Swpaul CSR_WRITE_1(sc, WB_NODE0 + i, sc->arpcom.ac_enaddr[i]); 169441502Swpaul } 169541502Swpaul 169641502Swpaul /* Init circular RX list. */ 169741502Swpaul if (wb_list_rx_init(sc) == ENOBUFS) { 169841502Swpaul printf("wb%d: initialization failed: no " 169941502Swpaul "memory for rx buffers\n", sc->wb_unit); 170041502Swpaul wb_stop(sc); 170141502Swpaul (void)splx(s); 170241502Swpaul return; 170341502Swpaul } 170441502Swpaul 170541502Swpaul /* Init TX descriptors. */ 170641502Swpaul wb_list_tx_init(sc); 170741502Swpaul 170841502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 170941502Swpaul if (ifp->if_flags & IFF_PROMISC) { 171041502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 171141502Swpaul } else { 171241502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 171341502Swpaul } 171441502Swpaul 171541502Swpaul /* 171641502Swpaul * Set capture broadcast bit to capture broadcast frames. 171741502Swpaul */ 171841502Swpaul if (ifp->if_flags & IFF_BROADCAST) { 171941502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 172041502Swpaul } else { 172141502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 172241502Swpaul } 172341502Swpaul 172441502Swpaul /* 172541502Swpaul * Program the multicast filter, if necessary. 172641502Swpaul */ 172741502Swpaul wb_setmulti(sc); 172841502Swpaul 172941502Swpaul /* 173041502Swpaul * Load the address of the RX list. 173141502Swpaul */ 173241502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 173341502Swpaul CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 173441502Swpaul 173541502Swpaul /* 173641502Swpaul * Enable interrupts. 173741502Swpaul */ 173841502Swpaul CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 173941502Swpaul CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); 174041502Swpaul 174141502Swpaul /* Enable receiver and transmitter. */ 174241502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 174341502Swpaul CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 174441502Swpaul 174541502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 174641502Swpaul CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); 174741502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 174841502Swpaul 174950675Swpaul mii_mediachg(mii); 175041502Swpaul 175141502Swpaul ifp->if_flags |= IFF_RUNNING; 175241502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 175341502Swpaul 175441502Swpaul (void)splx(s); 175541502Swpaul 175650675Swpaul sc->wb_stat_ch = timeout(wb_tick, sc, hz); 175750675Swpaul 175841502Swpaul return; 175941502Swpaul} 176041502Swpaul 176141502Swpaul/* 176241502Swpaul * Set media options. 176341502Swpaul */ 176441502Swpaulstatic int wb_ifmedia_upd(ifp) 176541502Swpaul struct ifnet *ifp; 176641502Swpaul{ 176741502Swpaul struct wb_softc *sc; 176841502Swpaul 176941502Swpaul sc = ifp->if_softc; 177041502Swpaul 177150675Swpaul if (ifp->if_flags & IFF_UP) 177250675Swpaul wb_init(sc); 177341502Swpaul 177441502Swpaul return(0); 177541502Swpaul} 177641502Swpaul 177741502Swpaul/* 177841502Swpaul * Report current media status. 177941502Swpaul */ 178041502Swpaulstatic void wb_ifmedia_sts(ifp, ifmr) 178141502Swpaul struct ifnet *ifp; 178241502Swpaul struct ifmediareq *ifmr; 178341502Swpaul{ 178441502Swpaul struct wb_softc *sc; 178550675Swpaul struct mii_data *mii; 178641502Swpaul 178741502Swpaul sc = ifp->if_softc; 178841502Swpaul 178950675Swpaul mii = device_get_softc(sc->wb_miibus); 179041502Swpaul 179150675Swpaul mii_pollstat(mii); 179250675Swpaul ifmr->ifm_active = mii->mii_media_active; 179350675Swpaul ifmr->ifm_status = mii->mii_media_status; 179441502Swpaul 179541502Swpaul return; 179641502Swpaul} 179741502Swpaul 179841502Swpaulstatic int wb_ioctl(ifp, command, data) 179941502Swpaul struct ifnet *ifp; 180041502Swpaul u_long command; 180141502Swpaul caddr_t data; 180241502Swpaul{ 180341502Swpaul struct wb_softc *sc = ifp->if_softc; 180450675Swpaul struct mii_data *mii; 180541502Swpaul struct ifreq *ifr = (struct ifreq *) data; 180641502Swpaul int s, error = 0; 180741502Swpaul 180841502Swpaul s = splimp(); 180941502Swpaul 181041502Swpaul switch(command) { 181141502Swpaul case SIOCSIFADDR: 181241502Swpaul case SIOCGIFADDR: 181341502Swpaul case SIOCSIFMTU: 181441502Swpaul error = ether_ioctl(ifp, command, data); 181541502Swpaul break; 181641502Swpaul case SIOCSIFFLAGS: 181741502Swpaul if (ifp->if_flags & IFF_UP) { 181841502Swpaul wb_init(sc); 181941502Swpaul } else { 182041502Swpaul if (ifp->if_flags & IFF_RUNNING) 182141502Swpaul wb_stop(sc); 182241502Swpaul } 182341502Swpaul error = 0; 182441502Swpaul break; 182541502Swpaul case SIOCADDMULTI: 182641502Swpaul case SIOCDELMULTI: 182741502Swpaul wb_setmulti(sc); 182841502Swpaul error = 0; 182941502Swpaul break; 183041502Swpaul case SIOCGIFMEDIA: 183141502Swpaul case SIOCSIFMEDIA: 183250675Swpaul mii = device_get_softc(sc->wb_miibus); 183350675Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 183441502Swpaul break; 183541502Swpaul default: 183641502Swpaul error = EINVAL; 183741502Swpaul break; 183841502Swpaul } 183941502Swpaul 184041502Swpaul (void)splx(s); 184141502Swpaul 184241502Swpaul return(error); 184341502Swpaul} 184441502Swpaul 184541502Swpaulstatic void wb_watchdog(ifp) 184641502Swpaul struct ifnet *ifp; 184741502Swpaul{ 184841502Swpaul struct wb_softc *sc; 184941502Swpaul 185041502Swpaul sc = ifp->if_softc; 185141502Swpaul 185241502Swpaul ifp->if_oerrors++; 185341502Swpaul printf("wb%d: watchdog timeout\n", sc->wb_unit); 185450675Swpaul#ifdef foo 185541502Swpaul if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) 185641502Swpaul printf("wb%d: no carrier - transceiver cable problem?\n", 185741502Swpaul sc->wb_unit); 185850675Swpaul#endif 185941502Swpaul wb_stop(sc); 186041502Swpaul wb_reset(sc); 186141502Swpaul wb_init(sc); 186241502Swpaul 186341502Swpaul if (ifp->if_snd.ifq_head != NULL) 186441502Swpaul wb_start(ifp); 186541502Swpaul 186641502Swpaul return; 186741502Swpaul} 186841502Swpaul 186941502Swpaul/* 187041502Swpaul * Stop the adapter and free any mbufs allocated to the 187141502Swpaul * RX and TX lists. 187241502Swpaul */ 187341502Swpaulstatic void wb_stop(sc) 187441502Swpaul struct wb_softc *sc; 187541502Swpaul{ 187641502Swpaul register int i; 187741502Swpaul struct ifnet *ifp; 187841502Swpaul 187941502Swpaul ifp = &sc->arpcom.ac_if; 188041502Swpaul ifp->if_timer = 0; 188141502Swpaul 188250675Swpaul untimeout(wb_tick, sc, sc->wb_stat_ch); 188350675Swpaul 188441502Swpaul WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); 188541502Swpaul CSR_WRITE_4(sc, WB_IMR, 0x00000000); 188641502Swpaul CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); 188741502Swpaul CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); 188841502Swpaul 188941502Swpaul /* 189041502Swpaul * Free data in the RX lists. 189141502Swpaul */ 189241502Swpaul for (i = 0; i < WB_RX_LIST_CNT; i++) { 189341502Swpaul if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { 189441502Swpaul m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); 189541502Swpaul sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; 189641502Swpaul } 189741502Swpaul } 189841502Swpaul bzero((char *)&sc->wb_ldata->wb_rx_list, 189941502Swpaul sizeof(sc->wb_ldata->wb_rx_list)); 190041502Swpaul 190141502Swpaul /* 190241502Swpaul * Free the TX list buffers. 190341502Swpaul */ 190441502Swpaul for (i = 0; i < WB_TX_LIST_CNT; i++) { 190541502Swpaul if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { 190641502Swpaul m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); 190741502Swpaul sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; 190841502Swpaul } 190941502Swpaul } 191041502Swpaul 191141502Swpaul bzero((char *)&sc->wb_ldata->wb_tx_list, 191241502Swpaul sizeof(sc->wb_ldata->wb_tx_list)); 191341502Swpaul 191441502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 191541502Swpaul 191641502Swpaul return; 191741502Swpaul} 191841502Swpaul 191941502Swpaul/* 192041502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 192141502Swpaul * get confused by errant DMAs when rebooting. 192241502Swpaul */ 192349611Swpaulstatic void wb_shutdown(dev) 192449611Swpaul device_t dev; 192541502Swpaul{ 192649611Swpaul struct wb_softc *sc; 192741502Swpaul 192849611Swpaul sc = device_get_softc(dev); 192941502Swpaul wb_stop(sc); 193041502Swpaul 193141502Swpaul return; 193241502Swpaul} 1933