if_wb.c revision 51657
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 51657 1999-09-25 17:29:02Z 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 8648745Swpaul#include "opt_bdg.h" 8741502Swpaul 8841502Swpaul#include <sys/param.h> 8941502Swpaul#include <sys/systm.h> 9041502Swpaul#include <sys/sockio.h> 9141502Swpaul#include <sys/mbuf.h> 9241502Swpaul#include <sys/malloc.h> 9341502Swpaul#include <sys/kernel.h> 9441502Swpaul#include <sys/socket.h> 9550675Swpaul#include <sys/queue.h> 9641502Swpaul 9741502Swpaul#include <net/if.h> 9841502Swpaul#include <net/if_arp.h> 9941502Swpaul#include <net/ethernet.h> 10041502Swpaul#include <net/if_dl.h> 10141502Swpaul#include <net/if_media.h> 10241502Swpaul 10341502Swpaul#include <net/bpf.h> 10441502Swpaul 10548745Swpaul#ifdef BRIDGE 10648745Swpaul#include <net/bridge.h> 10748745Swpaul#endif 10848745Swpaul 10941502Swpaul#include <vm/vm.h> /* for vtophys */ 11041502Swpaul#include <vm/pmap.h> /* for vtophys */ 11141502Swpaul#include <machine/clock.h> /* for DELAY */ 11241502Swpaul#include <machine/bus_memio.h> 11341502Swpaul#include <machine/bus_pio.h> 11441502Swpaul#include <machine/bus.h> 11549611Swpaul#include <machine/resource.h> 11649611Swpaul#include <sys/bus.h> 11749611Swpaul#include <sys/rman.h> 11841502Swpaul 11941502Swpaul#include <pci/pcireg.h> 12041502Swpaul#include <pci/pcivar.h> 12141502Swpaul 12250675Swpaul#include <dev/mii/mii.h> 12350675Swpaul#include <dev/mii/miivar.h> 12450675Swpaul 12551089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 12650675Swpaul#include "miibus_if.h" 12750675Swpaul 12841502Swpaul#define WB_USEIOSPACE 12941502Swpaul 13041502Swpaul#include <pci/if_wbreg.h> 13141502Swpaul 13241502Swpaul#ifndef lint 13341633Sarchiestatic const char rcsid[] = 13450477Speter "$FreeBSD: head/sys/pci/if_wb.c 51657 1999-09-25 17:29:02Z wpaul $"; 13541502Swpaul#endif 13641502Swpaul 13741502Swpaul/* 13841502Swpaul * Various supported device vendors/types and their names. 13941502Swpaul */ 14041502Swpaulstatic struct wb_type wb_devs[] = { 14141502Swpaul { WB_VENDORID, WB_DEVICEID_840F, 14241502Swpaul "Winbond W89C840F 10/100BaseTX" }, 14341502Swpaul { CP_VENDORID, CP_DEVICEID_RL100, 14441502Swpaul "Compex RL100-ATX 10/100baseTX" }, 14541502Swpaul { 0, 0, NULL } 14641502Swpaul}; 14741502Swpaul 14849611Swpaulstatic int wb_probe __P((device_t)); 14949611Swpaulstatic int wb_attach __P((device_t)); 15049611Swpaulstatic int wb_detach __P((device_t)); 15141502Swpaul 15250675Swpaulstatic void wb_bfree __P((caddr_t, u_int)); 15341502Swpaulstatic int wb_newbuf __P((struct wb_softc *, 15448745Swpaul struct wb_chain_onefrag *, 15548745Swpaul struct mbuf *)); 15641502Swpaulstatic int wb_encap __P((struct wb_softc *, struct wb_chain *, 15750675Swpaul struct mbuf *)); 15841502Swpaul 15941502Swpaulstatic void wb_rxeof __P((struct wb_softc *)); 16041502Swpaulstatic void wb_rxeoc __P((struct wb_softc *)); 16141502Swpaulstatic void wb_txeof __P((struct wb_softc *)); 16241502Swpaulstatic void wb_txeoc __P((struct wb_softc *)); 16341502Swpaulstatic void wb_intr __P((void *)); 16450675Swpaulstatic void wb_tick __P((void *)); 16541502Swpaulstatic void wb_start __P((struct ifnet *)); 16641502Swpaulstatic int wb_ioctl __P((struct ifnet *, u_long, caddr_t)); 16741502Swpaulstatic void wb_init __P((void *)); 16841502Swpaulstatic void wb_stop __P((struct wb_softc *)); 16941502Swpaulstatic void wb_watchdog __P((struct ifnet *)); 17049611Swpaulstatic void wb_shutdown __P((device_t)); 17141502Swpaulstatic int wb_ifmedia_upd __P((struct ifnet *)); 17241502Swpaulstatic void wb_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 17341502Swpaul 17442718Swpaulstatic void wb_eeprom_putbyte __P((struct wb_softc *, int)); 17542718Swpaulstatic void wb_eeprom_getword __P((struct wb_softc *, int, u_int16_t *)); 17641502Swpaulstatic void wb_read_eeprom __P((struct wb_softc *, caddr_t, int, 17741502Swpaul int, int)); 17841502Swpaulstatic void wb_mii_sync __P((struct wb_softc *)); 17941502Swpaulstatic void wb_mii_send __P((struct wb_softc *, u_int32_t, int)); 18041502Swpaulstatic int wb_mii_readreg __P((struct wb_softc *, struct wb_mii_frame *)); 18141502Swpaulstatic int wb_mii_writereg __P((struct wb_softc *, struct wb_mii_frame *)); 18241502Swpaul 18350675Swpaulstatic void wb_setcfg __P((struct wb_softc *, u_int32_t)); 18442718Swpaulstatic u_int8_t wb_calchash __P((caddr_t)); 18541502Swpaulstatic void wb_setmulti __P((struct wb_softc *)); 18641502Swpaulstatic void wb_reset __P((struct wb_softc *)); 18750675Swpaulstatic void wb_fixmedia __P((struct wb_softc *)); 18841502Swpaulstatic int wb_list_rx_init __P((struct wb_softc *)); 18941502Swpaulstatic int wb_list_tx_init __P((struct wb_softc *)); 19041502Swpaul 19150675Swpaulstatic int wb_miibus_readreg __P((device_t, int, int)); 19250675Swpaulstatic int wb_miibus_writereg __P((device_t, int, int, int)); 19350675Swpaulstatic void wb_miibus_statchg __P((device_t)); 19450675Swpaul 19549611Swpaul#ifdef WB_USEIOSPACE 19649611Swpaul#define WB_RES SYS_RES_IOPORT 19749611Swpaul#define WB_RID WB_PCI_LOIO 19849611Swpaul#else 19949611Swpaul#define WB_RES SYS_RES_MEMORY 20049611Swpaul#define WB_RID WB_PCI_LOMEM 20149611Swpaul#endif 20249611Swpaul 20349611Swpaulstatic device_method_t wb_methods[] = { 20449611Swpaul /* Device interface */ 20549611Swpaul DEVMETHOD(device_probe, wb_probe), 20649611Swpaul DEVMETHOD(device_attach, wb_attach), 20749611Swpaul DEVMETHOD(device_detach, wb_detach), 20849611Swpaul DEVMETHOD(device_shutdown, wb_shutdown), 20950675Swpaul 21050675Swpaul /* bus interface, for miibus */ 21150675Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 21250675Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 21350675Swpaul 21450675Swpaul /* MII interface */ 21550675Swpaul DEVMETHOD(miibus_readreg, wb_miibus_readreg), 21650675Swpaul DEVMETHOD(miibus_writereg, wb_miibus_writereg), 21750675Swpaul DEVMETHOD(miibus_statchg, wb_miibus_statchg), 21849611Swpaul { 0, 0 } 21949611Swpaul}; 22049611Swpaul 22149611Swpaulstatic driver_t wb_driver = { 22251455Swpaul "wb", 22349611Swpaul wb_methods, 22449611Swpaul sizeof(struct wb_softc) 22549611Swpaul}; 22649611Swpaul 22749611Swpaulstatic devclass_t wb_devclass; 22849611Swpaul 22951533SwpaulDRIVER_MODULE(if_wb, pci, wb_driver, wb_devclass, 0, 0); 23051473SwpaulDRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); 23149611Swpaul 23241502Swpaul#define WB_SETBIT(sc, reg, x) \ 23341502Swpaul CSR_WRITE_4(sc, reg, \ 23441502Swpaul CSR_READ_4(sc, reg) | x) 23541502Swpaul 23641502Swpaul#define WB_CLRBIT(sc, reg, x) \ 23741502Swpaul CSR_WRITE_4(sc, reg, \ 23841502Swpaul CSR_READ_4(sc, reg) & ~x) 23941502Swpaul 24041502Swpaul#define SIO_SET(x) \ 24141502Swpaul CSR_WRITE_4(sc, WB_SIO, \ 24241502Swpaul CSR_READ_4(sc, WB_SIO) | x) 24341502Swpaul 24441502Swpaul#define SIO_CLR(x) \ 24541502Swpaul CSR_WRITE_4(sc, WB_SIO, \ 24641502Swpaul CSR_READ_4(sc, WB_SIO) & ~x) 24741502Swpaul 24841502Swpaul/* 24941502Swpaul * Send a read command and address to the EEPROM, check for ACK. 25041502Swpaul */ 25141502Swpaulstatic void wb_eeprom_putbyte(sc, addr) 25241502Swpaul struct wb_softc *sc; 25342718Swpaul int addr; 25441502Swpaul{ 25541502Swpaul register int d, i; 25641502Swpaul 25741502Swpaul d = addr | WB_EECMD_READ; 25841502Swpaul 25941502Swpaul /* 26041502Swpaul * Feed in each bit and stobe the clock. 26141502Swpaul */ 26241502Swpaul for (i = 0x400; i; i >>= 1) { 26341502Swpaul if (d & i) { 26441502Swpaul SIO_SET(WB_SIO_EE_DATAIN); 26541502Swpaul } else { 26641502Swpaul SIO_CLR(WB_SIO_EE_DATAIN); 26741502Swpaul } 26841502Swpaul DELAY(100); 26941502Swpaul SIO_SET(WB_SIO_EE_CLK); 27041502Swpaul DELAY(150); 27141502Swpaul SIO_CLR(WB_SIO_EE_CLK); 27241502Swpaul DELAY(100); 27341502Swpaul } 27441502Swpaul 27541502Swpaul return; 27641502Swpaul} 27741502Swpaul 27841502Swpaul/* 27941502Swpaul * Read a word of data stored in the EEPROM at address 'addr.' 28041502Swpaul */ 28141502Swpaulstatic void wb_eeprom_getword(sc, addr, dest) 28241502Swpaul struct wb_softc *sc; 28342718Swpaul int addr; 28441502Swpaul u_int16_t *dest; 28541502Swpaul{ 28641502Swpaul register int i; 28741502Swpaul u_int16_t word = 0; 28841502Swpaul 28941502Swpaul /* Enter EEPROM access mode. */ 29041502Swpaul CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 29141502Swpaul 29241502Swpaul /* 29341502Swpaul * Send address of word we want to read. 29441502Swpaul */ 29541502Swpaul wb_eeprom_putbyte(sc, addr); 29641502Swpaul 29741502Swpaul CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); 29841502Swpaul 29941502Swpaul /* 30041502Swpaul * Start reading bits from EEPROM. 30141502Swpaul */ 30241502Swpaul for (i = 0x8000; i; i >>= 1) { 30341502Swpaul SIO_SET(WB_SIO_EE_CLK); 30441502Swpaul DELAY(100); 30541502Swpaul if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) 30641502Swpaul word |= i; 30741502Swpaul SIO_CLR(WB_SIO_EE_CLK); 30841502Swpaul DELAY(100); 30941502Swpaul } 31041502Swpaul 31141502Swpaul /* Turn off EEPROM access mode. */ 31241502Swpaul CSR_WRITE_4(sc, WB_SIO, 0); 31341502Swpaul 31441502Swpaul *dest = word; 31541502Swpaul 31641502Swpaul return; 31741502Swpaul} 31841502Swpaul 31941502Swpaul/* 32041502Swpaul * Read a sequence of words from the EEPROM. 32141502Swpaul */ 32241502Swpaulstatic void wb_read_eeprom(sc, dest, off, cnt, swap) 32341502Swpaul struct wb_softc *sc; 32441502Swpaul caddr_t dest; 32541502Swpaul int off; 32641502Swpaul int cnt; 32741502Swpaul int swap; 32841502Swpaul{ 32941502Swpaul int i; 33041502Swpaul u_int16_t word = 0, *ptr; 33141502Swpaul 33241502Swpaul for (i = 0; i < cnt; i++) { 33341502Swpaul wb_eeprom_getword(sc, off + i, &word); 33441502Swpaul ptr = (u_int16_t *)(dest + (i * 2)); 33541502Swpaul if (swap) 33641502Swpaul *ptr = ntohs(word); 33741502Swpaul else 33841502Swpaul *ptr = word; 33941502Swpaul } 34041502Swpaul 34141502Swpaul return; 34241502Swpaul} 34341502Swpaul 34441502Swpaul/* 34541502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 34641502Swpaul */ 34741502Swpaulstatic void wb_mii_sync(sc) 34841502Swpaul struct wb_softc *sc; 34941502Swpaul{ 35041502Swpaul register int i; 35141502Swpaul 35241502Swpaul SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN); 35341502Swpaul 35441502Swpaul for (i = 0; i < 32; i++) { 35541502Swpaul SIO_SET(WB_SIO_MII_CLK); 35641502Swpaul DELAY(1); 35741502Swpaul SIO_CLR(WB_SIO_MII_CLK); 35841502Swpaul DELAY(1); 35941502Swpaul } 36041502Swpaul 36141502Swpaul return; 36241502Swpaul} 36341502Swpaul 36441502Swpaul/* 36541502Swpaul * Clock a series of bits through the MII. 36641502Swpaul */ 36741502Swpaulstatic void wb_mii_send(sc, bits, cnt) 36841502Swpaul struct wb_softc *sc; 36941502Swpaul u_int32_t bits; 37041502Swpaul int cnt; 37141502Swpaul{ 37241502Swpaul int i; 37341502Swpaul 37441502Swpaul SIO_CLR(WB_SIO_MII_CLK); 37541502Swpaul 37641502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 37741502Swpaul if (bits & i) { 37841502Swpaul SIO_SET(WB_SIO_MII_DATAIN); 37941502Swpaul } else { 38041502Swpaul SIO_CLR(WB_SIO_MII_DATAIN); 38141502Swpaul } 38241502Swpaul DELAY(1); 38341502Swpaul SIO_CLR(WB_SIO_MII_CLK); 38441502Swpaul DELAY(1); 38541502Swpaul SIO_SET(WB_SIO_MII_CLK); 38641502Swpaul } 38741502Swpaul} 38841502Swpaul 38941502Swpaul/* 39041502Swpaul * Read an PHY register through the MII. 39141502Swpaul */ 39241502Swpaulstatic int wb_mii_readreg(sc, frame) 39341502Swpaul struct wb_softc *sc; 39441502Swpaul struct wb_mii_frame *frame; 39541502Swpaul 39641502Swpaul{ 39741502Swpaul int i, ack, s; 39841502Swpaul 39941502Swpaul s = splimp(); 40041502Swpaul 40141502Swpaul /* 40241502Swpaul * Set up frame for RX. 40341502Swpaul */ 40441502Swpaul frame->mii_stdelim = WB_MII_STARTDELIM; 40541502Swpaul frame->mii_opcode = WB_MII_READOP; 40641502Swpaul frame->mii_turnaround = 0; 40741502Swpaul frame->mii_data = 0; 40841502Swpaul 40941502Swpaul CSR_WRITE_4(sc, WB_SIO, 0); 41041502Swpaul 41141502Swpaul /* 41241502Swpaul * Turn on data xmit. 41341502Swpaul */ 41441502Swpaul SIO_SET(WB_SIO_MII_DIR); 41541502Swpaul 41641502Swpaul wb_mii_sync(sc); 41741502Swpaul 41841502Swpaul /* 41941502Swpaul * Send command/address info. 42041502Swpaul */ 42141502Swpaul wb_mii_send(sc, frame->mii_stdelim, 2); 42241502Swpaul wb_mii_send(sc, frame->mii_opcode, 2); 42341502Swpaul wb_mii_send(sc, frame->mii_phyaddr, 5); 42441502Swpaul wb_mii_send(sc, frame->mii_regaddr, 5); 42541502Swpaul 42641502Swpaul /* Idle bit */ 42741502Swpaul SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN)); 42841502Swpaul DELAY(1); 42941502Swpaul SIO_SET(WB_SIO_MII_CLK); 43041502Swpaul DELAY(1); 43141502Swpaul 43241502Swpaul /* Turn off xmit. */ 43341502Swpaul SIO_CLR(WB_SIO_MII_DIR); 43441502Swpaul /* Check for ack */ 43541502Swpaul SIO_CLR(WB_SIO_MII_CLK); 43641502Swpaul DELAY(1); 43741502Swpaul SIO_SET(WB_SIO_MII_CLK); 43841502Swpaul DELAY(1); 43941502Swpaul ack = CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT; 44041502Swpaul SIO_CLR(WB_SIO_MII_CLK); 44141502Swpaul DELAY(1); 44241502Swpaul SIO_SET(WB_SIO_MII_CLK); 44341502Swpaul DELAY(1); 44441502Swpaul 44541502Swpaul /* 44641502Swpaul * Now try reading data bits. If the ack failed, we still 44741502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 44841502Swpaul */ 44941502Swpaul if (ack) { 45041502Swpaul for(i = 0; i < 16; i++) { 45141502Swpaul SIO_CLR(WB_SIO_MII_CLK); 45241502Swpaul DELAY(1); 45341502Swpaul SIO_SET(WB_SIO_MII_CLK); 45441502Swpaul DELAY(1); 45541502Swpaul } 45641502Swpaul goto fail; 45741502Swpaul } 45841502Swpaul 45941502Swpaul for (i = 0x8000; i; i >>= 1) { 46041502Swpaul SIO_CLR(WB_SIO_MII_CLK); 46141502Swpaul DELAY(1); 46241502Swpaul if (!ack) { 46341502Swpaul if (CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT) 46441502Swpaul frame->mii_data |= i; 46541502Swpaul DELAY(1); 46641502Swpaul } 46741502Swpaul SIO_SET(WB_SIO_MII_CLK); 46841502Swpaul DELAY(1); 46941502Swpaul } 47041502Swpaul 47141502Swpaulfail: 47241502Swpaul 47341502Swpaul SIO_CLR(WB_SIO_MII_CLK); 47441502Swpaul DELAY(1); 47541502Swpaul SIO_SET(WB_SIO_MII_CLK); 47641502Swpaul DELAY(1); 47741502Swpaul 47841502Swpaul splx(s); 47941502Swpaul 48041502Swpaul if (ack) 48141502Swpaul return(1); 48241502Swpaul return(0); 48341502Swpaul} 48441502Swpaul 48541502Swpaul/* 48641502Swpaul * Write to a PHY register through the MII. 48741502Swpaul */ 48841502Swpaulstatic int wb_mii_writereg(sc, frame) 48941502Swpaul struct wb_softc *sc; 49041502Swpaul struct wb_mii_frame *frame; 49141502Swpaul 49241502Swpaul{ 49341502Swpaul int s; 49441502Swpaul 49541502Swpaul s = splimp(); 49641502Swpaul /* 49741502Swpaul * Set up frame for TX. 49841502Swpaul */ 49941502Swpaul 50041502Swpaul frame->mii_stdelim = WB_MII_STARTDELIM; 50141502Swpaul frame->mii_opcode = WB_MII_WRITEOP; 50241502Swpaul frame->mii_turnaround = WB_MII_TURNAROUND; 50341502Swpaul 50441502Swpaul /* 50541502Swpaul * Turn on data output. 50641502Swpaul */ 50741502Swpaul SIO_SET(WB_SIO_MII_DIR); 50841502Swpaul 50941502Swpaul wb_mii_sync(sc); 51041502Swpaul 51141502Swpaul wb_mii_send(sc, frame->mii_stdelim, 2); 51241502Swpaul wb_mii_send(sc, frame->mii_opcode, 2); 51341502Swpaul wb_mii_send(sc, frame->mii_phyaddr, 5); 51441502Swpaul wb_mii_send(sc, frame->mii_regaddr, 5); 51541502Swpaul wb_mii_send(sc, frame->mii_turnaround, 2); 51641502Swpaul wb_mii_send(sc, frame->mii_data, 16); 51741502Swpaul 51841502Swpaul /* Idle bit. */ 51941502Swpaul SIO_SET(WB_SIO_MII_CLK); 52041502Swpaul DELAY(1); 52141502Swpaul SIO_CLR(WB_SIO_MII_CLK); 52241502Swpaul DELAY(1); 52341502Swpaul 52441502Swpaul /* 52541502Swpaul * Turn off xmit. 52641502Swpaul */ 52741502Swpaul SIO_CLR(WB_SIO_MII_DIR); 52841502Swpaul 52941502Swpaul splx(s); 53041502Swpaul 53141502Swpaul return(0); 53241502Swpaul} 53341502Swpaul 53450675Swpaulstatic int wb_miibus_readreg(dev, phy, reg) 53550675Swpaul device_t dev; 53650675Swpaul int phy, reg; 53750675Swpaul{ 53841502Swpaul struct wb_softc *sc; 53941502Swpaul struct wb_mii_frame frame; 54041502Swpaul 54150675Swpaul sc = device_get_softc(dev); 54250675Swpaul 54341502Swpaul bzero((char *)&frame, sizeof(frame)); 54441502Swpaul 54550675Swpaul frame.mii_phyaddr = phy; 54641502Swpaul frame.mii_regaddr = reg; 54741502Swpaul wb_mii_readreg(sc, &frame); 54841502Swpaul 54941502Swpaul return(frame.mii_data); 55041502Swpaul} 55141502Swpaul 55250675Swpaulstatic int wb_miibus_writereg(dev, phy, reg, data) 55350675Swpaul device_t dev; 55450675Swpaul int phy, reg, data; 55550675Swpaul{ 55641502Swpaul struct wb_softc *sc; 55741502Swpaul struct wb_mii_frame frame; 55841502Swpaul 55950675Swpaul sc = device_get_softc(dev); 56050675Swpaul 56141502Swpaul bzero((char *)&frame, sizeof(frame)); 56241502Swpaul 56350675Swpaul frame.mii_phyaddr = phy; 56441502Swpaul frame.mii_regaddr = reg; 56541502Swpaul frame.mii_data = data; 56641502Swpaul 56741502Swpaul wb_mii_writereg(sc, &frame); 56841502Swpaul 56950675Swpaul return(0); 57050675Swpaul} 57150675Swpaul 57250675Swpaulstatic void wb_miibus_statchg(dev) 57350675Swpaul device_t dev; 57450675Swpaul{ 57550675Swpaul struct wb_softc *sc; 57650675Swpaul struct mii_data *mii; 57750675Swpaul 57850675Swpaul sc = device_get_softc(dev); 57950675Swpaul mii = device_get_softc(sc->wb_miibus); 58050675Swpaul wb_setcfg(sc, mii->mii_media_active); 58150675Swpaul 58241502Swpaul return; 58341502Swpaul} 58441502Swpaul 58541502Swpaulstatic u_int8_t wb_calchash(addr) 58642718Swpaul caddr_t addr; 58741502Swpaul{ 58841502Swpaul u_int32_t crc, carry; 58941502Swpaul int i, j; 59041502Swpaul u_int8_t c; 59141502Swpaul 59241502Swpaul /* Compute CRC for the address value. */ 59341502Swpaul crc = 0xFFFFFFFF; /* initial value */ 59441502Swpaul 59541502Swpaul for (i = 0; i < 6; i++) { 59641502Swpaul c = *(addr + i); 59741502Swpaul for (j = 0; j < 8; j++) { 59841502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 59941502Swpaul crc <<= 1; 60041502Swpaul c >>= 1; 60141502Swpaul if (carry) 60241502Swpaul crc = (crc ^ 0x04c11db6) | carry; 60341502Swpaul } 60441502Swpaul } 60541502Swpaul 60641502Swpaul /* 60741502Swpaul * return the filter bit position 60841502Swpaul * Note: I arrived at the following nonsense 60941502Swpaul * through experimentation. It's not the usual way to 61041502Swpaul * generate the bit position but it's the only thing 61141502Swpaul * I could come up with that works. 61241502Swpaul */ 61341502Swpaul return(~(crc >> 26) & 0x0000003F); 61441502Swpaul} 61541502Swpaul 61641502Swpaul/* 61741502Swpaul * Program the 64-bit multicast hash filter. 61841502Swpaul */ 61941502Swpaulstatic void wb_setmulti(sc) 62041502Swpaul struct wb_softc *sc; 62141502Swpaul{ 62241502Swpaul struct ifnet *ifp; 62341502Swpaul int h = 0; 62441502Swpaul u_int32_t hashes[2] = { 0, 0 }; 62541502Swpaul struct ifmultiaddr *ifma; 62641502Swpaul u_int32_t rxfilt; 62741502Swpaul int mcnt = 0; 62841502Swpaul 62941502Swpaul ifp = &sc->arpcom.ac_if; 63041502Swpaul 63141502Swpaul rxfilt = CSR_READ_4(sc, WB_NETCFG); 63241502Swpaul 63341502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 63441502Swpaul rxfilt |= WB_NETCFG_RX_MULTI; 63541502Swpaul CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 63641502Swpaul CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); 63741502Swpaul CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); 63841502Swpaul return; 63941502Swpaul } 64041502Swpaul 64141502Swpaul /* first, zot all the existing hash bits */ 64241502Swpaul CSR_WRITE_4(sc, WB_MAR0, 0); 64341502Swpaul CSR_WRITE_4(sc, WB_MAR1, 0); 64441502Swpaul 64541502Swpaul /* now program new ones */ 64641502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 64741502Swpaul ifma = ifma->ifma_link.le_next) { 64841502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 64941502Swpaul continue; 65041502Swpaul h = wb_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 65141502Swpaul if (h < 32) 65241502Swpaul hashes[0] |= (1 << h); 65341502Swpaul else 65441502Swpaul hashes[1] |= (1 << (h - 32)); 65541502Swpaul mcnt++; 65641502Swpaul } 65741502Swpaul 65841502Swpaul if (mcnt) 65941502Swpaul rxfilt |= WB_NETCFG_RX_MULTI; 66041502Swpaul else 66141502Swpaul rxfilt &= ~WB_NETCFG_RX_MULTI; 66241502Swpaul 66341502Swpaul CSR_WRITE_4(sc, WB_MAR0, hashes[0]); 66441502Swpaul CSR_WRITE_4(sc, WB_MAR1, hashes[1]); 66541502Swpaul CSR_WRITE_4(sc, WB_NETCFG, rxfilt); 66641502Swpaul 66741502Swpaul return; 66841502Swpaul} 66941502Swpaul 67041502Swpaul/* 67141502Swpaul * The Winbond manual states that in order to fiddle with the 67241502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 67341502Swpaul * first have to put the transmit and/or receive logic in the idle state. 67441502Swpaul */ 67550675Swpaulstatic void wb_setcfg(sc, media) 67641502Swpaul struct wb_softc *sc; 67750675Swpaul u_int32_t media; 67841502Swpaul{ 67941502Swpaul int i, restart = 0; 68041502Swpaul 68141502Swpaul if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { 68241502Swpaul restart = 1; 68341502Swpaul WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); 68441502Swpaul 68541502Swpaul for (i = 0; i < WB_TIMEOUT; i++) { 68641502Swpaul DELAY(10); 68741502Swpaul if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && 68841502Swpaul (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) 68941502Swpaul break; 69041502Swpaul } 69141502Swpaul 69241502Swpaul if (i == WB_TIMEOUT) 69341502Swpaul printf("wb%d: failed to force tx and " 69441502Swpaul "rx to idle state\n", sc->wb_unit); 69541502Swpaul } 69641502Swpaul 69750675Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) 69850675Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 69950675Swpaul else 70041502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); 70141502Swpaul 70250675Swpaul if ((media & IFM_GMASK) == IFM_FDX) 70341502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 70441502Swpaul else 70541502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); 70641502Swpaul 70741502Swpaul if (restart) 70841502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); 70941502Swpaul 71041502Swpaul return; 71141502Swpaul} 71241502Swpaul 71341502Swpaulstatic void wb_reset(sc) 71441502Swpaul struct wb_softc *sc; 71541502Swpaul{ 71641502Swpaul register int i; 71750675Swpaul struct mii_data *mii; 71841502Swpaul 71950675Swpaul CSR_WRITE_4(sc, WB_NETCFG, 0); 72050675Swpaul CSR_WRITE_4(sc, WB_BUSCTL, 0); 72150675Swpaul CSR_WRITE_4(sc, WB_TXADDR, 0); 72250675Swpaul CSR_WRITE_4(sc, WB_RXADDR, 0); 72350675Swpaul 72441502Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 72550675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); 72641502Swpaul 72741502Swpaul for (i = 0; i < WB_TIMEOUT; i++) { 72841502Swpaul DELAY(10); 72941502Swpaul if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) 73041502Swpaul break; 73141502Swpaul } 73241502Swpaul if (i == WB_TIMEOUT) 73341502Swpaul printf("wb%d: reset never completed!\n", sc->wb_unit); 73441502Swpaul 73541502Swpaul /* Wait a little while for the chip to get its brains in order. */ 73641502Swpaul DELAY(1000); 73741502Swpaul 73850675Swpaul if (sc->wb_miibus == NULL) 73950675Swpaul return; 74041502Swpaul 74150675Swpaul mii = device_get_softc(sc->wb_miibus); 74250675Swpaul if (mii == NULL) 74350675Swpaul return; 74450675Swpaul 74550675Swpaul if (mii->mii_instance) { 74650675Swpaul struct mii_softc *miisc; 74750675Swpaul for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 74850675Swpaul miisc = LIST_NEXT(miisc, mii_list)) 74950675Swpaul mii_phy_reset(miisc); 75050675Swpaul } 75150675Swpaul 75241502Swpaul return; 75341502Swpaul} 75441502Swpaul 75550675Swpaulstatic void wb_fixmedia(sc) 75650675Swpaul struct wb_softc *sc; 75750675Swpaul{ 75850675Swpaul struct mii_data *mii = NULL; 75950675Swpaul struct ifnet *ifp; 76050675Swpaul u_int32_t media; 76150675Swpaul 76250675Swpaul if (sc->wb_miibus == NULL) 76350675Swpaul return; 76450675Swpaul 76550675Swpaul mii = device_get_softc(sc->wb_miibus); 76650675Swpaul ifp = &sc->arpcom.ac_if; 76750675Swpaul 76850675Swpaul mii_pollstat(mii); 76950675Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 77050675Swpaul media = mii->mii_media_active & ~IFM_10_T; 77150675Swpaul media |= IFM_100_TX; 77250675Swpaul } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { 77350675Swpaul media = mii->mii_media_active & ~IFM_100_TX; 77450675Swpaul media |= IFM_10_T; 77550675Swpaul } else 77650675Swpaul return; 77750675Swpaul 77850675Swpaul ifmedia_set(&mii->mii_media, media); 77950675Swpaul 78050675Swpaul return; 78150675Swpaul} 78250675Swpaul 78341502Swpaul/* 78441502Swpaul * Probe for a Winbond chip. Check the PCI vendor and device 78541502Swpaul * IDs against our list and return a device name if we find a match. 78641502Swpaul */ 78749611Swpaulstatic int wb_probe(dev) 78849611Swpaul device_t dev; 78941502Swpaul{ 79041502Swpaul struct wb_type *t; 79141502Swpaul 79241502Swpaul t = wb_devs; 79341502Swpaul 79441502Swpaul while(t->wb_name != NULL) { 79549611Swpaul if ((pci_get_vendor(dev) == t->wb_vid) && 79649611Swpaul (pci_get_device(dev) == t->wb_did)) { 79749611Swpaul device_set_desc(dev, t->wb_name); 79849611Swpaul return(0); 79941502Swpaul } 80041502Swpaul t++; 80141502Swpaul } 80241502Swpaul 80349611Swpaul return(ENXIO); 80441502Swpaul} 80541502Swpaul 80641502Swpaul/* 80741502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 80841502Swpaul * setup and ethernet/BPF attach. 80941502Swpaul */ 81049611Swpaulstatic int wb_attach(dev) 81149611Swpaul device_t dev; 81241502Swpaul{ 81350675Swpaul int s; 81441502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 81541502Swpaul u_int32_t command; 81641502Swpaul struct wb_softc *sc; 81741502Swpaul struct ifnet *ifp; 81849611Swpaul int unit, error = 0, rid; 81941502Swpaul 82041502Swpaul s = splimp(); 82141502Swpaul 82249611Swpaul sc = device_get_softc(dev); 82349611Swpaul unit = device_get_unit(dev); 82441502Swpaul 82541502Swpaul /* 82641502Swpaul * Handle power management nonsense. 82741502Swpaul */ 82841502Swpaul 82949611Swpaul command = pci_read_config(dev, WB_PCI_CAPID, 4) & 0x000000FF; 83041502Swpaul if (command == 0x01) { 83141502Swpaul 83249611Swpaul command = pci_read_config(dev, WB_PCI_PWRMGMTCTRL, 4); 83341502Swpaul if (command & WB_PSTATE_MASK) { 83441502Swpaul u_int32_t iobase, membase, irq; 83541502Swpaul 83641502Swpaul /* Save important PCI config data. */ 83749611Swpaul iobase = pci_read_config(dev, WB_PCI_LOIO, 4); 83849611Swpaul membase = pci_read_config(dev, WB_PCI_LOMEM, 4); 83949611Swpaul irq = pci_read_config(dev, WB_PCI_INTLINE, 4); 84041502Swpaul 84141502Swpaul /* Reset the power state. */ 84241502Swpaul printf("wb%d: chip is in D%d power mode " 84341502Swpaul "-- setting to D0\n", unit, command & WB_PSTATE_MASK); 84441502Swpaul command &= 0xFFFFFFFC; 84549611Swpaul pci_write_config(dev, WB_PCI_PWRMGMTCTRL, command, 4); 84641502Swpaul 84741502Swpaul /* Restore PCI config data. */ 84849611Swpaul pci_write_config(dev, WB_PCI_LOIO, iobase, 4); 84949611Swpaul pci_write_config(dev, WB_PCI_LOMEM, membase, 4); 85049611Swpaul pci_write_config(dev, WB_PCI_INTLINE, irq, 4); 85141502Swpaul } 85241502Swpaul } 85341502Swpaul 85441502Swpaul /* 85541502Swpaul * Map control/status registers. 85641502Swpaul */ 85749611Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 85841502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 85949611Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 86049611Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 86141502Swpaul 86241502Swpaul#ifdef WB_USEIOSPACE 86341502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 86441502Swpaul printf("wb%d: failed to enable I/O ports!\n", unit); 86549611Swpaul error = ENXIO; 86641502Swpaul goto fail; 86741502Swpaul } 86841502Swpaul#else 86941502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 87041502Swpaul printf("wb%d: failed to enable memory mapping!\n", unit); 87149611Swpaul error = ENXIO; 87241502Swpaul goto fail; 87341502Swpaul } 87449611Swpaul#endif 87541502Swpaul 87649611Swpaul rid = WB_RID; 87749611Swpaul sc->wb_res = bus_alloc_resource(dev, WB_RES, &rid, 87849611Swpaul 0, ~0, 1, RF_ACTIVE); 87949611Swpaul 88049611Swpaul if (sc->wb_res == NULL) { 88149611Swpaul printf("wb%d: couldn't map ports/memory\n", unit); 88249611Swpaul error = ENXIO; 88341502Swpaul goto fail; 88441502Swpaul } 88541502Swpaul 88649611Swpaul sc->wb_btag = rman_get_bustag(sc->wb_res); 88749611Swpaul sc->wb_bhandle = rman_get_bushandle(sc->wb_res); 88849611Swpaul 88941502Swpaul /* Allocate interrupt */ 89049611Swpaul rid = 0; 89149611Swpaul sc->wb_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 89249611Swpaul RF_SHAREABLE | RF_ACTIVE); 89349611Swpaul 89449611Swpaul if (sc->wb_irq == NULL) { 89541502Swpaul printf("wb%d: couldn't map interrupt\n", unit); 89649611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 89749611Swpaul error = ENXIO; 89841502Swpaul goto fail; 89941502Swpaul } 90041502Swpaul 90149611Swpaul error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET, 90249611Swpaul wb_intr, sc, &sc->wb_intrhand); 90349611Swpaul 90449611Swpaul if (error) { 90549611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 90649611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 90749611Swpaul printf("wb%d: couldn't set up irq\n", unit); 90849611Swpaul goto fail; 90949611Swpaul } 91050675Swpaul 91150675Swpaul /* Save the cache line size. */ 91250675Swpaul sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; 91350675Swpaul 91441502Swpaul /* Reset the adapter. */ 91541502Swpaul wb_reset(sc); 91641502Swpaul 91741502Swpaul /* 91841502Swpaul * Get station address from the EEPROM. 91941502Swpaul */ 92041502Swpaul wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); 92141502Swpaul 92241502Swpaul /* 92341502Swpaul * A Winbond chip was detected. Inform the world. 92441502Swpaul */ 92541502Swpaul printf("wb%d: Ethernet address: %6D\n", unit, eaddr, ":"); 92641502Swpaul 92741502Swpaul sc->wb_unit = unit; 92841502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 92941502Swpaul 93050675Swpaul sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, 93151657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 93250675Swpaul 93350675Swpaul if (sc->wb_ldata == NULL) { 93441502Swpaul printf("wb%d: no memory for list buffers!\n", unit); 93549611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 93649611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 93749611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 93849611Swpaul error = ENXIO; 93949611Swpaul goto fail; 94041502Swpaul } 94141502Swpaul 94241502Swpaul bzero(sc->wb_ldata, sizeof(struct wb_list_data)); 94341502Swpaul 94441502Swpaul ifp = &sc->arpcom.ac_if; 94541502Swpaul ifp->if_softc = sc; 94641502Swpaul ifp->if_unit = unit; 94741502Swpaul ifp->if_name = "wb"; 94841502Swpaul ifp->if_mtu = ETHERMTU; 94941502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 95041502Swpaul ifp->if_ioctl = wb_ioctl; 95141502Swpaul ifp->if_output = ether_output; 95241502Swpaul ifp->if_start = wb_start; 95341502Swpaul ifp->if_watchdog = wb_watchdog; 95441502Swpaul ifp->if_init = wb_init; 95541502Swpaul ifp->if_baudrate = 10000000; 95643515Swpaul ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; 95741502Swpaul 95850675Swpaul /* 95950675Swpaul * Do MII setup. 96050675Swpaul */ 96150675Swpaul if (mii_phy_probe(dev, &sc->wb_miibus, 96250675Swpaul wb_ifmedia_upd, wb_ifmedia_sts)) { 96349611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 96449611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 96549611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 96649611Swpaul free(sc->wb_ldata_ptr, M_DEVBUF); 96749611Swpaul error = ENXIO; 96841502Swpaul goto fail; 96941502Swpaul } 97041502Swpaul 97141502Swpaul /* 97241502Swpaul * Call MI attach routines. 97341502Swpaul */ 97441502Swpaul if_attach(ifp); 97541502Swpaul ether_ifattach(ifp); 97641502Swpaul 97741502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 97841502Swpaul 97941502Swpaulfail: 98050675Swpaul if (error) 98150675Swpaul device_delete_child(dev, sc->wb_miibus); 98241502Swpaul splx(s); 98350675Swpaul 98449611Swpaul return(error); 98541502Swpaul} 98641502Swpaul 98749611Swpaulstatic int wb_detach(dev) 98849611Swpaul device_t dev; 98949611Swpaul{ 99049611Swpaul struct wb_softc *sc; 99149611Swpaul struct ifnet *ifp; 99249611Swpaul int s; 99349611Swpaul 99449611Swpaul s = splimp(); 99549611Swpaul 99649611Swpaul sc = device_get_softc(dev); 99749611Swpaul ifp = &sc->arpcom.ac_if; 99849611Swpaul 99949611Swpaul wb_stop(sc); 100049611Swpaul if_detach(ifp); 100149611Swpaul 100250675Swpaul /* Delete any miibus and phy devices attached to this interface */ 100350675Swpaul bus_generic_detach(dev); 100450675Swpaul device_delete_child(dev, sc->wb_miibus); 100550675Swpaul 100649611Swpaul bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); 100749611Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); 100849611Swpaul bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); 100949611Swpaul 101049611Swpaul free(sc->wb_ldata_ptr, M_DEVBUF); 101149611Swpaul 101249611Swpaul splx(s); 101349611Swpaul 101449611Swpaul return(0); 101549611Swpaul} 101649611Swpaul 101741502Swpaul/* 101841502Swpaul * Initialize the transmit descriptors. 101941502Swpaul */ 102041502Swpaulstatic int wb_list_tx_init(sc) 102141502Swpaul struct wb_softc *sc; 102241502Swpaul{ 102341502Swpaul struct wb_chain_data *cd; 102441502Swpaul struct wb_list_data *ld; 102541502Swpaul int i; 102641502Swpaul 102741502Swpaul cd = &sc->wb_cdata; 102841502Swpaul ld = sc->wb_ldata; 102941502Swpaul 103041502Swpaul for (i = 0; i < WB_TX_LIST_CNT; i++) { 103141502Swpaul cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; 103241502Swpaul if (i == (WB_TX_LIST_CNT - 1)) { 103341502Swpaul cd->wb_tx_chain[i].wb_nextdesc = 103441502Swpaul &cd->wb_tx_chain[0]; 103541502Swpaul } else { 103641502Swpaul cd->wb_tx_chain[i].wb_nextdesc = 103741502Swpaul &cd->wb_tx_chain[i + 1]; 103841502Swpaul } 103941502Swpaul } 104041502Swpaul 104141502Swpaul cd->wb_tx_free = &cd->wb_tx_chain[0]; 104241502Swpaul cd->wb_tx_tail = cd->wb_tx_head = NULL; 104341502Swpaul 104441502Swpaul return(0); 104541502Swpaul} 104641502Swpaul 104741502Swpaul 104841502Swpaul/* 104941502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 105041502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 105141502Swpaul * points back to the first. 105241502Swpaul */ 105341502Swpaulstatic int wb_list_rx_init(sc) 105441502Swpaul struct wb_softc *sc; 105541502Swpaul{ 105641502Swpaul struct wb_chain_data *cd; 105741502Swpaul struct wb_list_data *ld; 105841502Swpaul int i; 105941502Swpaul 106041502Swpaul cd = &sc->wb_cdata; 106141502Swpaul ld = sc->wb_ldata; 106241502Swpaul 106341502Swpaul for (i = 0; i < WB_RX_LIST_CNT; i++) { 106441502Swpaul cd->wb_rx_chain[i].wb_ptr = 106541502Swpaul (struct wb_desc *)&ld->wb_rx_list[i]; 106650675Swpaul cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; 106748745Swpaul if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) 106841502Swpaul return(ENOBUFS); 106941502Swpaul if (i == (WB_RX_LIST_CNT - 1)) { 107041502Swpaul cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; 107141502Swpaul ld->wb_rx_list[i].wb_next = 107241502Swpaul vtophys(&ld->wb_rx_list[0]); 107341502Swpaul } else { 107441502Swpaul cd->wb_rx_chain[i].wb_nextdesc = 107541502Swpaul &cd->wb_rx_chain[i + 1]; 107641502Swpaul ld->wb_rx_list[i].wb_next = 107741502Swpaul vtophys(&ld->wb_rx_list[i + 1]); 107841502Swpaul } 107941502Swpaul } 108041502Swpaul 108141502Swpaul cd->wb_rx_head = &cd->wb_rx_chain[0]; 108241502Swpaul 108341502Swpaul return(0); 108441502Swpaul} 108541502Swpaul 108650675Swpaulstatic void wb_bfree(buf, size) 108750675Swpaul caddr_t buf; 108850675Swpaul u_int size; 108950675Swpaul{ 109050675Swpaul return; 109150675Swpaul} 109250675Swpaul 109341502Swpaul/* 109441502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 109541502Swpaul */ 109648745Swpaulstatic int wb_newbuf(sc, c, m) 109741502Swpaul struct wb_softc *sc; 109841502Swpaul struct wb_chain_onefrag *c; 109948745Swpaul struct mbuf *m; 110041502Swpaul{ 110141502Swpaul struct mbuf *m_new = NULL; 110241502Swpaul 110348745Swpaul if (m == NULL) { 110448745Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 110548745Swpaul if (m_new == NULL) { 110648745Swpaul printf("wb%d: no memory for rx " 110748745Swpaul "list -- packet dropped!\n", sc->wb_unit); 110848745Swpaul return(ENOBUFS); 110948745Swpaul } 111041502Swpaul 111150675Swpaul m_new->m_data = m_new->m_ext.ext_buf = c->wb_buf; 111250675Swpaul m_new->m_flags |= M_EXT; 111350675Swpaul m_new->m_ext.ext_size = m_new->m_pkthdr.len = 111450675Swpaul m_new->m_len = WB_BUFBYTES; 111550675Swpaul m_new->m_ext.ext_free = wb_bfree; 111650675Swpaul m_new->m_ext.ext_ref = wb_bfree; 111748745Swpaul } else { 111848745Swpaul m_new = m; 111950675Swpaul m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; 112048745Swpaul m_new->m_data = m_new->m_ext.ext_buf; 112141502Swpaul } 112241502Swpaul 112348745Swpaul m_adj(m_new, sizeof(u_int64_t)); 112448745Swpaul 112541502Swpaul c->wb_mbuf = m_new; 112641502Swpaul c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); 112750675Swpaul c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; 112841502Swpaul c->wb_ptr->wb_status = WB_RXSTAT; 112941502Swpaul 113041502Swpaul return(0); 113141502Swpaul} 113241502Swpaul 113341502Swpaul/* 113441502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 113541502Swpaul * the higher level protocols. 113641502Swpaul */ 113741502Swpaulstatic void wb_rxeof(sc) 113841502Swpaul struct wb_softc *sc; 113941502Swpaul{ 114041502Swpaul struct ether_header *eh; 114150675Swpaul struct mbuf *m = NULL; 114241502Swpaul struct ifnet *ifp; 114341502Swpaul struct wb_chain_onefrag *cur_rx; 114441502Swpaul int total_len = 0; 114541502Swpaul u_int32_t rxstat; 114641502Swpaul 114741502Swpaul ifp = &sc->arpcom.ac_if; 114841502Swpaul 114941502Swpaul while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & 115041502Swpaul WB_RXSTAT_OWN)) { 115148745Swpaul struct mbuf *m0 = NULL; 115248745Swpaul 115341502Swpaul cur_rx = sc->wb_cdata.wb_rx_head; 115441502Swpaul sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; 115550675Swpaul 115648745Swpaul m = cur_rx->wb_mbuf; 115741502Swpaul 115850675Swpaul if ((rxstat & WB_RXSTAT_MIIERR) || 115950675Swpaul (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || 116050675Swpaul (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || 116150675Swpaul !(rxstat & WB_RXSTAT_LASTFRAG) || 116250675Swpaul !(rxstat & WB_RXSTAT_RXCMP)) { 116341502Swpaul ifp->if_ierrors++; 116450675Swpaul wb_newbuf(sc, cur_rx, m); 116541502Swpaul printf("wb%x: receiver babbling: possible chip " 116641502Swpaul "bug, forcing reset\n", sc->wb_unit); 116750675Swpaul wb_fixmedia(sc); 116850675Swpaul wb_reset(sc); 116950675Swpaul wb_init(sc); 117041502Swpaul return; 117141502Swpaul } 117241502Swpaul 117342718Swpaul if (rxstat & WB_RXSTAT_RXERR) { 117442718Swpaul ifp->if_ierrors++; 117548745Swpaul wb_newbuf(sc, cur_rx, m); 117650675Swpaul break; 117742718Swpaul } 117842718Swpaul 117941502Swpaul /* No errors; receive the packet. */ 118041502Swpaul total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); 118141502Swpaul 118241502Swpaul /* 118341934Swpaul * XXX The Winbond chip includes the CRC with every 118441934Swpaul * received frame, and there's no way to turn this 118541934Swpaul * behavior off (at least, I can't find anything in 118641934Swpaul * the manual that explains how to do it) so we have 118741934Swpaul * to trim off the CRC manually. 118841934Swpaul */ 118941934Swpaul total_len -= ETHER_CRC_LEN; 119041934Swpaul 119148745Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 119248745Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 119348745Swpaul wb_newbuf(sc, cur_rx, m); 119448745Swpaul if (m0 == NULL) { 119548745Swpaul ifp->if_ierrors++; 119650675Swpaul break; 119741502Swpaul } 119848745Swpaul m_adj(m0, ETHER_ALIGN); 119948745Swpaul m = m0; 120041502Swpaul 120141502Swpaul ifp->if_ipackets++; 120241502Swpaul eh = mtod(m, struct ether_header *); 120341502Swpaul 120448745Swpaul#ifdef BRIDGE 120548745Swpaul if (do_bridge) { 120648745Swpaul struct ifnet *bdg_ifp; 120748745Swpaul bdg_ifp = bridge_in(m); 120848745Swpaul if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_DROP) 120948745Swpaul bdg_forward(&m, bdg_ifp); 121048745Swpaul if (((bdg_ifp != BDG_LOCAL) && (bdg_ifp != BDG_BCAST) && 121148745Swpaul (bdg_ifp != BDG_MCAST)) || bdg_ifp == BDG_DROP) { 121248745Swpaul m_freem(m); 121350675Swpaul break; 121448745Swpaul } 121548745Swpaul } 121648745Swpaul#endif 121748745Swpaul 121841502Swpaul /* 121941502Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 122041502Swpaul * don't pass it up to the ether_input() layer unless it's 122141502Swpaul * a broadcast packet, multicast packet, matches our ethernet 122241502Swpaul * address or the interface is in promiscuous mode. 122341502Swpaul */ 122441502Swpaul if (ifp->if_bpf) { 122541502Swpaul bpf_mtap(ifp, m); 122641502Swpaul if (ifp->if_flags & IFF_PROMISC && 122741502Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 122841502Swpaul ETHER_ADDR_LEN) && 122941502Swpaul (eh->ether_dhost[0] & 1) == 0)) { 123041502Swpaul m_freem(m); 123150675Swpaul break; 123241502Swpaul } 123341502Swpaul } 123451583Swpaul 123541502Swpaul /* Remove header from mbuf and pass it on. */ 123641502Swpaul m_adj(m, sizeof(struct ether_header)); 123741502Swpaul ether_input(ifp, eh, m); 123841502Swpaul } 123941502Swpaul 124041502Swpaul return; 124141502Swpaul} 124241502Swpaul 124341502Swpaulvoid wb_rxeoc(sc) 124441502Swpaul struct wb_softc *sc; 124541502Swpaul{ 124641502Swpaul wb_rxeof(sc); 124741502Swpaul 124841502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 124941502Swpaul CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 125041502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 125141502Swpaul if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) 125241502Swpaul CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 125341502Swpaul 125441502Swpaul return; 125541502Swpaul} 125641502Swpaul 125741502Swpaul/* 125841502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 125941502Swpaul * the list buffers. 126041502Swpaul */ 126141502Swpaulstatic void wb_txeof(sc) 126241502Swpaul struct wb_softc *sc; 126341502Swpaul{ 126441502Swpaul struct wb_chain *cur_tx; 126541502Swpaul struct ifnet *ifp; 126641502Swpaul 126741502Swpaul ifp = &sc->arpcom.ac_if; 126841502Swpaul 126941502Swpaul /* Clear the timeout timer. */ 127041502Swpaul ifp->if_timer = 0; 127141502Swpaul 127241502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) 127341502Swpaul return; 127441502Swpaul 127541502Swpaul /* 127641502Swpaul * Go through our tx list and free mbufs for those 127741502Swpaul * frames that have been transmitted. 127841502Swpaul */ 127941502Swpaul while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { 128041502Swpaul u_int32_t txstat; 128141502Swpaul 128241502Swpaul cur_tx = sc->wb_cdata.wb_tx_head; 128341502Swpaul txstat = WB_TXSTATUS(cur_tx); 128441502Swpaul 128541502Swpaul if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) 128641502Swpaul break; 128741502Swpaul 128841502Swpaul if (txstat & WB_TXSTAT_TXERR) { 128941502Swpaul ifp->if_oerrors++; 129041502Swpaul if (txstat & WB_TXSTAT_ABORT) 129141502Swpaul ifp->if_collisions++; 129241502Swpaul if (txstat & WB_TXSTAT_LATECOLL) 129341502Swpaul ifp->if_collisions++; 129441502Swpaul } 129541502Swpaul 129641502Swpaul ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; 129741502Swpaul 129841502Swpaul ifp->if_opackets++; 129941502Swpaul m_freem(cur_tx->wb_mbuf); 130041502Swpaul cur_tx->wb_mbuf = NULL; 130141502Swpaul 130241502Swpaul if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { 130341502Swpaul sc->wb_cdata.wb_tx_head = NULL; 130441502Swpaul sc->wb_cdata.wb_tx_tail = NULL; 130541502Swpaul break; 130641502Swpaul } 130741502Swpaul 130841502Swpaul sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; 130941502Swpaul } 131041502Swpaul 131141502Swpaul return; 131241502Swpaul} 131341502Swpaul 131441502Swpaul/* 131541502Swpaul * TX 'end of channel' interrupt handler. 131641502Swpaul */ 131741502Swpaulstatic void wb_txeoc(sc) 131841502Swpaul struct wb_softc *sc; 131941502Swpaul{ 132041502Swpaul struct ifnet *ifp; 132141502Swpaul 132241502Swpaul ifp = &sc->arpcom.ac_if; 132341502Swpaul 132441502Swpaul ifp->if_timer = 0; 132541502Swpaul 132641502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) { 132741502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 132841502Swpaul sc->wb_cdata.wb_tx_tail = NULL; 132941502Swpaul } else { 133041502Swpaul if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { 133141502Swpaul WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; 133241502Swpaul ifp->if_timer = 5; 133341502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 133441502Swpaul } 133541502Swpaul } 133641502Swpaul 133741502Swpaul return; 133841502Swpaul} 133941502Swpaul 134041502Swpaulstatic void wb_intr(arg) 134141502Swpaul void *arg; 134241502Swpaul{ 134341502Swpaul struct wb_softc *sc; 134441502Swpaul struct ifnet *ifp; 134541502Swpaul u_int32_t status; 134641502Swpaul 134741502Swpaul sc = arg; 134841502Swpaul ifp = &sc->arpcom.ac_if; 134941502Swpaul 135041502Swpaul if (!(ifp->if_flags & IFF_UP)) 135141502Swpaul return; 135241502Swpaul 135341502Swpaul /* Disable interrupts. */ 135441502Swpaul CSR_WRITE_4(sc, WB_IMR, 0x00000000); 135541502Swpaul 135641502Swpaul for (;;) { 135741502Swpaul 135841502Swpaul status = CSR_READ_4(sc, WB_ISR); 135941502Swpaul if (status) 136041502Swpaul CSR_WRITE_4(sc, WB_ISR, status); 136141502Swpaul 136241502Swpaul if ((status & WB_INTRS) == 0) 136341502Swpaul break; 136441502Swpaul 136541502Swpaul if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { 136641502Swpaul ifp->if_ierrors++; 136741502Swpaul wb_reset(sc); 136850675Swpaul if (status & WB_ISR_RX_ERR) 136950675Swpaul wb_fixmedia(sc); 137041502Swpaul wb_init(sc); 137150675Swpaul continue; 137241502Swpaul } 137341502Swpaul 137450675Swpaul if (status & WB_ISR_RX_OK) 137550675Swpaul wb_rxeof(sc); 137650675Swpaul 137750675Swpaul if (status & WB_ISR_RX_IDLE) 137850675Swpaul wb_rxeoc(sc); 137950675Swpaul 138041502Swpaul if (status & WB_ISR_TX_OK) 138141502Swpaul wb_txeof(sc); 138241502Swpaul 138341502Swpaul if (status & WB_ISR_TX_NOBUF) 138441502Swpaul wb_txeoc(sc); 138541502Swpaul 138641502Swpaul if (status & WB_ISR_TX_IDLE) { 138741502Swpaul wb_txeof(sc); 138841502Swpaul if (sc->wb_cdata.wb_tx_head != NULL) { 138941502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 139041502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 139141502Swpaul } 139241502Swpaul } 139341502Swpaul 139441502Swpaul if (status & WB_ISR_TX_UNDERRUN) { 139541502Swpaul ifp->if_oerrors++; 139641502Swpaul wb_txeof(sc); 139741502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 139841502Swpaul /* Jack up TX threshold */ 139941502Swpaul sc->wb_txthresh += WB_TXTHRESH_CHUNK; 140041502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 140141502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 140241502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 140341502Swpaul } 140441502Swpaul 140541502Swpaul if (status & WB_ISR_BUS_ERR) { 140641502Swpaul wb_reset(sc); 140741502Swpaul wb_init(sc); 140841502Swpaul } 140941502Swpaul 141041502Swpaul } 141141502Swpaul 141241502Swpaul /* Re-enable interrupts. */ 141341502Swpaul CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 141441502Swpaul 141541502Swpaul if (ifp->if_snd.ifq_head != NULL) { 141641502Swpaul wb_start(ifp); 141741502Swpaul } 141841502Swpaul 141941502Swpaul return; 142041502Swpaul} 142141502Swpaul 142250675Swpaulstatic void wb_tick(xsc) 142350675Swpaul void *xsc; 142450675Swpaul{ 142550675Swpaul struct wb_softc *sc; 142650675Swpaul struct mii_data *mii; 142750685Swpaul int s; 142850675Swpaul 142950685Swpaul s = splimp(); 143050685Swpaul 143150675Swpaul sc = xsc; 143250675Swpaul mii = device_get_softc(sc->wb_miibus); 143350675Swpaul 143450675Swpaul mii_tick(mii); 143550675Swpaul 143650675Swpaul sc->wb_stat_ch = timeout(wb_tick, sc, hz); 143750675Swpaul 143850685Swpaul splx(s); 143950685Swpaul 144050675Swpaul return; 144150675Swpaul} 144250675Swpaul 144341502Swpaul/* 144441502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 144541502Swpaul * pointers to the fragment pointers. 144641502Swpaul */ 144741502Swpaulstatic int wb_encap(sc, c, m_head) 144841502Swpaul struct wb_softc *sc; 144941502Swpaul struct wb_chain *c; 145041502Swpaul struct mbuf *m_head; 145141502Swpaul{ 145241502Swpaul int frag = 0; 145341502Swpaul struct wb_desc *f = NULL; 145441502Swpaul int total_len; 145541502Swpaul struct mbuf *m; 145641502Swpaul 145741502Swpaul /* 145841502Swpaul * Start packing the mbufs in this chain into 145941502Swpaul * the fragment pointers. Stop when we run out 146041502Swpaul * of fragments or hit the end of the mbuf chain. 146141502Swpaul */ 146241502Swpaul m = m_head; 146341502Swpaul total_len = 0; 146441502Swpaul 146541502Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 146641502Swpaul if (m->m_len != 0) { 146741502Swpaul if (frag == WB_MAXFRAGS) 146841502Swpaul break; 146941502Swpaul total_len += m->m_len; 147041502Swpaul f = &c->wb_ptr->wb_frag[frag]; 147141502Swpaul f->wb_ctl = WB_TXCTL_TLINK | m->m_len; 147241502Swpaul if (frag == 0) { 147341502Swpaul f->wb_ctl |= WB_TXCTL_FIRSTFRAG; 147441502Swpaul f->wb_status = 0; 147541502Swpaul } else 147641502Swpaul f->wb_status = WB_TXSTAT_OWN; 147741502Swpaul f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); 147841502Swpaul f->wb_data = vtophys(mtod(m, vm_offset_t)); 147941502Swpaul frag++; 148041502Swpaul } 148141502Swpaul } 148241502Swpaul 148341502Swpaul /* 148441502Swpaul * Handle special case: we used up all 16 fragments, 148541502Swpaul * but we have more mbufs left in the chain. Copy the 148641502Swpaul * data into an mbuf cluster. Note that we don't 148741502Swpaul * bother clearing the values in the other fragment 148841502Swpaul * pointers/counters; it wouldn't gain us anything, 148941502Swpaul * and would waste cycles. 149041502Swpaul */ 149141502Swpaul if (m != NULL) { 149241502Swpaul struct mbuf *m_new = NULL; 149341502Swpaul 149441502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 149541502Swpaul if (m_new == NULL) { 149641502Swpaul printf("wb%d: no memory for tx list", sc->wb_unit); 149741502Swpaul return(1); 149841502Swpaul } 149941502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 150041502Swpaul MCLGET(m_new, M_DONTWAIT); 150141502Swpaul if (!(m_new->m_flags & M_EXT)) { 150241502Swpaul m_freem(m_new); 150341502Swpaul printf("wb%d: no memory for tx list", 150441502Swpaul sc->wb_unit); 150541502Swpaul return(1); 150641502Swpaul } 150741502Swpaul } 150841502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 150941502Swpaul mtod(m_new, caddr_t)); 151041502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 151141502Swpaul m_freem(m_head); 151241502Swpaul m_head = m_new; 151341502Swpaul f = &c->wb_ptr->wb_frag[0]; 151441502Swpaul f->wb_status = 0; 151541502Swpaul f->wb_data = vtophys(mtod(m_new, caddr_t)); 151641502Swpaul f->wb_ctl = total_len = m_new->m_len; 151741502Swpaul f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; 151841502Swpaul frag = 1; 151941502Swpaul } 152041502Swpaul 152141502Swpaul if (total_len < WB_MIN_FRAMELEN) { 152241502Swpaul f = &c->wb_ptr->wb_frag[frag]; 152341502Swpaul f->wb_ctl = WB_MIN_FRAMELEN - total_len; 152441502Swpaul f->wb_data = vtophys(&sc->wb_cdata.wb_pad); 152541502Swpaul f->wb_ctl |= WB_TXCTL_TLINK; 152641502Swpaul f->wb_status = WB_TXSTAT_OWN; 152741502Swpaul frag++; 152841502Swpaul } 152941502Swpaul 153041502Swpaul c->wb_mbuf = m_head; 153141502Swpaul c->wb_lastdesc = frag - 1; 153241502Swpaul WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; 153341502Swpaul WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); 153441502Swpaul 153541502Swpaul return(0); 153641502Swpaul} 153741502Swpaul 153841502Swpaul/* 153941502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 154041502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 154141502Swpaul * copy of the pointers since the transmit list fragment pointers are 154241502Swpaul * physical addresses. 154341502Swpaul */ 154441502Swpaul 154541502Swpaulstatic void wb_start(ifp) 154641502Swpaul struct ifnet *ifp; 154741502Swpaul{ 154841502Swpaul struct wb_softc *sc; 154941502Swpaul struct mbuf *m_head = NULL; 155041502Swpaul struct wb_chain *cur_tx = NULL, *start_tx; 155141502Swpaul 155241502Swpaul sc = ifp->if_softc; 155341502Swpaul 155441502Swpaul /* 155541502Swpaul * Check for an available queue slot. If there are none, 155641502Swpaul * punt. 155741502Swpaul */ 155841502Swpaul if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { 155941502Swpaul ifp->if_flags |= IFF_OACTIVE; 156041502Swpaul return; 156141502Swpaul } 156241502Swpaul 156341502Swpaul start_tx = sc->wb_cdata.wb_tx_free; 156441502Swpaul 156541502Swpaul while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { 156641502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 156741502Swpaul if (m_head == NULL) 156841502Swpaul break; 156941502Swpaul 157041502Swpaul /* Pick a descriptor off the free list. */ 157141502Swpaul cur_tx = sc->wb_cdata.wb_tx_free; 157241502Swpaul sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; 157341502Swpaul 157441502Swpaul /* Pack the data into the descriptor. */ 157541502Swpaul wb_encap(sc, cur_tx, m_head); 157641502Swpaul 157741502Swpaul if (cur_tx != start_tx) 157841502Swpaul WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; 157941502Swpaul 158041502Swpaul /* 158141502Swpaul * If there's a BPF listener, bounce a copy of this frame 158241502Swpaul * to him. 158341502Swpaul */ 158441502Swpaul if (ifp->if_bpf) 158541502Swpaul bpf_mtap(ifp, cur_tx->wb_mbuf); 158641502Swpaul } 158741502Swpaul 158841502Swpaul /* 158941526Swpaul * If there are no packets queued, bail. 159041526Swpaul */ 159141526Swpaul if (cur_tx == NULL) 159241526Swpaul return; 159341526Swpaul 159441526Swpaul /* 159541502Swpaul * Place the request for the upload interrupt 159641502Swpaul * in the last descriptor in the chain. This way, if 159741502Swpaul * we're chaining several packets at once, we'll only 159841502Swpaul * get an interupt once for the whole chain rather than 159941502Swpaul * once for each packet. 160041502Swpaul */ 160141502Swpaul WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; 160242718Swpaul cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; 160341502Swpaul sc->wb_cdata.wb_tx_tail = cur_tx; 160441502Swpaul 160541502Swpaul if (sc->wb_cdata.wb_tx_head == NULL) { 160641502Swpaul sc->wb_cdata.wb_tx_head = start_tx; 160741502Swpaul WB_TXOWN(start_tx) = WB_TXSTAT_OWN; 160841502Swpaul CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); 160941502Swpaul } else { 161041502Swpaul /* 161141502Swpaul * We need to distinguish between the case where 161241502Swpaul * the own bit is clear because the chip cleared it 161341502Swpaul * and where the own bit is clear because we haven't 161441502Swpaul * set it yet. The magic value WB_UNSET is just some 161541502Swpaul * ramdomly chosen number which doesn't have the own 161641502Swpaul * bit set. When we actually transmit the frame, the 161741502Swpaul * status word will have _only_ the own bit set, so 161841502Swpaul * the txeoc handler will be able to tell if it needs 161941502Swpaul * to initiate another transmission to flush out pending 162041502Swpaul * frames. 162141502Swpaul */ 162241502Swpaul WB_TXOWN(start_tx) = WB_UNSENT; 162341502Swpaul } 162441502Swpaul 162541502Swpaul /* 162641502Swpaul * Set a timeout in case the chip goes out to lunch. 162741502Swpaul */ 162841502Swpaul ifp->if_timer = 5; 162941502Swpaul 163041502Swpaul return; 163141502Swpaul} 163241502Swpaul 163341502Swpaulstatic void wb_init(xsc) 163441502Swpaul void *xsc; 163541502Swpaul{ 163641502Swpaul struct wb_softc *sc = xsc; 163741502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 163841502Swpaul int s, i; 163950675Swpaul struct mii_data *mii; 164041502Swpaul 164141502Swpaul s = splimp(); 164241502Swpaul 164350675Swpaul mii = device_get_softc(sc->wb_miibus); 164441502Swpaul 164541502Swpaul /* 164641502Swpaul * Cancel pending I/O and free all RX/TX buffers. 164741502Swpaul */ 164841502Swpaul wb_stop(sc); 164941502Swpaul wb_reset(sc); 165041502Swpaul 165141502Swpaul sc->wb_txthresh = WB_TXTHRESH_INIT; 165241502Swpaul 165341502Swpaul /* 165441502Swpaul * Set cache alignment and burst length. 165541502Swpaul */ 165650675Swpaul#ifdef foo 165741502Swpaul CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); 165841502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); 165941502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); 166050675Swpaul#endif 166141502Swpaul 166250675Swpaul CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); 166350675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); 166450675Swpaul switch(sc->wb_cachesize) { 166550675Swpaul case 32: 166650675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); 166750675Swpaul break; 166850675Swpaul case 16: 166950675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); 167050675Swpaul break; 167150675Swpaul case 8: 167250675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); 167350675Swpaul break; 167450675Swpaul case 0: 167550675Swpaul default: 167650675Swpaul WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); 167750675Swpaul break; 167850675Swpaul } 167950675Swpaul 168041502Swpaul /* This doesn't tend to work too well at 100Mbps. */ 168141502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); 168241502Swpaul 168341502Swpaul /* Init our MAC address */ 168441502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) { 168541502Swpaul CSR_WRITE_1(sc, WB_NODE0 + i, sc->arpcom.ac_enaddr[i]); 168641502Swpaul } 168741502Swpaul 168841502Swpaul /* Init circular RX list. */ 168941502Swpaul if (wb_list_rx_init(sc) == ENOBUFS) { 169041502Swpaul printf("wb%d: initialization failed: no " 169141502Swpaul "memory for rx buffers\n", sc->wb_unit); 169241502Swpaul wb_stop(sc); 169341502Swpaul (void)splx(s); 169441502Swpaul return; 169541502Swpaul } 169641502Swpaul 169741502Swpaul /* Init TX descriptors. */ 169841502Swpaul wb_list_tx_init(sc); 169941502Swpaul 170041502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 170141502Swpaul if (ifp->if_flags & IFF_PROMISC) { 170241502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 170341502Swpaul } else { 170441502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); 170541502Swpaul } 170641502Swpaul 170741502Swpaul /* 170841502Swpaul * Set capture broadcast bit to capture broadcast frames. 170941502Swpaul */ 171041502Swpaul if (ifp->if_flags & IFF_BROADCAST) { 171141502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 171241502Swpaul } else { 171341502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); 171441502Swpaul } 171541502Swpaul 171641502Swpaul /* 171741502Swpaul * Program the multicast filter, if necessary. 171841502Swpaul */ 171941502Swpaul wb_setmulti(sc); 172041502Swpaul 172141502Swpaul /* 172241502Swpaul * Load the address of the RX list. 172341502Swpaul */ 172441502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 172541502Swpaul CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); 172641502Swpaul 172741502Swpaul /* 172841502Swpaul * Enable interrupts. 172941502Swpaul */ 173041502Swpaul CSR_WRITE_4(sc, WB_IMR, WB_INTRS); 173141502Swpaul CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); 173241502Swpaul 173341502Swpaul /* Enable receiver and transmitter. */ 173441502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); 173541502Swpaul CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); 173641502Swpaul 173741502Swpaul WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 173841502Swpaul CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); 173941502Swpaul WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); 174041502Swpaul 174150675Swpaul mii_mediachg(mii); 174241502Swpaul 174341502Swpaul ifp->if_flags |= IFF_RUNNING; 174441502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 174541502Swpaul 174641502Swpaul (void)splx(s); 174741502Swpaul 174850675Swpaul sc->wb_stat_ch = timeout(wb_tick, sc, hz); 174950675Swpaul 175041502Swpaul return; 175141502Swpaul} 175241502Swpaul 175341502Swpaul/* 175441502Swpaul * Set media options. 175541502Swpaul */ 175641502Swpaulstatic int wb_ifmedia_upd(ifp) 175741502Swpaul struct ifnet *ifp; 175841502Swpaul{ 175941502Swpaul struct wb_softc *sc; 176041502Swpaul 176141502Swpaul sc = ifp->if_softc; 176241502Swpaul 176350675Swpaul if (ifp->if_flags & IFF_UP) 176450675Swpaul wb_init(sc); 176541502Swpaul 176641502Swpaul return(0); 176741502Swpaul} 176841502Swpaul 176941502Swpaul/* 177041502Swpaul * Report current media status. 177141502Swpaul */ 177241502Swpaulstatic void wb_ifmedia_sts(ifp, ifmr) 177341502Swpaul struct ifnet *ifp; 177441502Swpaul struct ifmediareq *ifmr; 177541502Swpaul{ 177641502Swpaul struct wb_softc *sc; 177750675Swpaul struct mii_data *mii; 177841502Swpaul 177941502Swpaul sc = ifp->if_softc; 178041502Swpaul 178150675Swpaul mii = device_get_softc(sc->wb_miibus); 178241502Swpaul 178350675Swpaul mii_pollstat(mii); 178450675Swpaul ifmr->ifm_active = mii->mii_media_active; 178550675Swpaul ifmr->ifm_status = mii->mii_media_status; 178641502Swpaul 178741502Swpaul return; 178841502Swpaul} 178941502Swpaul 179041502Swpaulstatic int wb_ioctl(ifp, command, data) 179141502Swpaul struct ifnet *ifp; 179241502Swpaul u_long command; 179341502Swpaul caddr_t data; 179441502Swpaul{ 179541502Swpaul struct wb_softc *sc = ifp->if_softc; 179650675Swpaul struct mii_data *mii; 179741502Swpaul struct ifreq *ifr = (struct ifreq *) data; 179841502Swpaul int s, error = 0; 179941502Swpaul 180041502Swpaul s = splimp(); 180141502Swpaul 180241502Swpaul switch(command) { 180341502Swpaul case SIOCSIFADDR: 180441502Swpaul case SIOCGIFADDR: 180541502Swpaul case SIOCSIFMTU: 180641502Swpaul error = ether_ioctl(ifp, command, data); 180741502Swpaul break; 180841502Swpaul case SIOCSIFFLAGS: 180941502Swpaul if (ifp->if_flags & IFF_UP) { 181041502Swpaul wb_init(sc); 181141502Swpaul } else { 181241502Swpaul if (ifp->if_flags & IFF_RUNNING) 181341502Swpaul wb_stop(sc); 181441502Swpaul } 181541502Swpaul error = 0; 181641502Swpaul break; 181741502Swpaul case SIOCADDMULTI: 181841502Swpaul case SIOCDELMULTI: 181941502Swpaul wb_setmulti(sc); 182041502Swpaul error = 0; 182141502Swpaul break; 182241502Swpaul case SIOCGIFMEDIA: 182341502Swpaul case SIOCSIFMEDIA: 182450675Swpaul mii = device_get_softc(sc->wb_miibus); 182550675Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 182641502Swpaul break; 182741502Swpaul default: 182841502Swpaul error = EINVAL; 182941502Swpaul break; 183041502Swpaul } 183141502Swpaul 183241502Swpaul (void)splx(s); 183341502Swpaul 183441502Swpaul return(error); 183541502Swpaul} 183641502Swpaul 183741502Swpaulstatic void wb_watchdog(ifp) 183841502Swpaul struct ifnet *ifp; 183941502Swpaul{ 184041502Swpaul struct wb_softc *sc; 184141502Swpaul 184241502Swpaul sc = ifp->if_softc; 184341502Swpaul 184441502Swpaul ifp->if_oerrors++; 184541502Swpaul printf("wb%d: watchdog timeout\n", sc->wb_unit); 184650675Swpaul#ifdef foo 184741502Swpaul if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) 184841502Swpaul printf("wb%d: no carrier - transceiver cable problem?\n", 184941502Swpaul sc->wb_unit); 185050675Swpaul#endif 185141502Swpaul wb_stop(sc); 185241502Swpaul wb_reset(sc); 185341502Swpaul wb_init(sc); 185441502Swpaul 185541502Swpaul if (ifp->if_snd.ifq_head != NULL) 185641502Swpaul wb_start(ifp); 185741502Swpaul 185841502Swpaul return; 185941502Swpaul} 186041502Swpaul 186141502Swpaul/* 186241502Swpaul * Stop the adapter and free any mbufs allocated to the 186341502Swpaul * RX and TX lists. 186441502Swpaul */ 186541502Swpaulstatic void wb_stop(sc) 186641502Swpaul struct wb_softc *sc; 186741502Swpaul{ 186841502Swpaul register int i; 186941502Swpaul struct ifnet *ifp; 187041502Swpaul 187141502Swpaul ifp = &sc->arpcom.ac_if; 187241502Swpaul ifp->if_timer = 0; 187341502Swpaul 187450675Swpaul untimeout(wb_tick, sc, sc->wb_stat_ch); 187550675Swpaul 187641502Swpaul WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); 187741502Swpaul CSR_WRITE_4(sc, WB_IMR, 0x00000000); 187841502Swpaul CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); 187941502Swpaul CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); 188041502Swpaul 188141502Swpaul /* 188241502Swpaul * Free data in the RX lists. 188341502Swpaul */ 188441502Swpaul for (i = 0; i < WB_RX_LIST_CNT; i++) { 188541502Swpaul if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { 188641502Swpaul m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); 188741502Swpaul sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; 188841502Swpaul } 188941502Swpaul } 189041502Swpaul bzero((char *)&sc->wb_ldata->wb_rx_list, 189141502Swpaul sizeof(sc->wb_ldata->wb_rx_list)); 189241502Swpaul 189341502Swpaul /* 189441502Swpaul * Free the TX list buffers. 189541502Swpaul */ 189641502Swpaul for (i = 0; i < WB_TX_LIST_CNT; i++) { 189741502Swpaul if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { 189841502Swpaul m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); 189941502Swpaul sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; 190041502Swpaul } 190141502Swpaul } 190241502Swpaul 190341502Swpaul bzero((char *)&sc->wb_ldata->wb_tx_list, 190441502Swpaul sizeof(sc->wb_ldata->wb_tx_list)); 190541502Swpaul 190641502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 190741502Swpaul 190841502Swpaul return; 190941502Swpaul} 191041502Swpaul 191141502Swpaul/* 191241502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 191341502Swpaul * get confused by errant DMAs when rebooting. 191441502Swpaul */ 191549611Swpaulstatic void wb_shutdown(dev) 191649611Swpaul device_t dev; 191741502Swpaul{ 191849611Swpaul struct wb_softc *sc; 191941502Swpaul 192049611Swpaul sc = device_get_softc(dev); 192141502Swpaul wb_stop(sc); 192241502Swpaul 192341502Swpaul return; 192441502Swpaul} 1925