1139825Simp/*- 236270Swpaul * Copyright (c) 1997, 1998 336270Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 436270Swpaul * 536270Swpaul * Redistribution and use in source and binary forms, with or without 636270Swpaul * modification, are permitted provided that the following conditions 736270Swpaul * are met: 836270Swpaul * 1. Redistributions of source code must retain the above copyright 936270Swpaul * notice, this list of conditions and the following disclaimer. 1036270Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1136270Swpaul * notice, this list of conditions and the following disclaimer in the 1236270Swpaul * documentation and/or other materials provided with the distribution. 1336270Swpaul * 3. All advertising materials mentioning features or use of this software 1436270Swpaul * must display the following acknowledgement: 1536270Swpaul * This product includes software developed by Bill Paul. 1636270Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1736270Swpaul * may be used to endorse or promote products derived from this software 1836270Swpaul * without specific prior written permission. 1936270Swpaul * 2036270Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2136270Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2236270Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2336270Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2436270Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2536270Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2636270Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2736270Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2836270Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2936270Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3036270Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3136270Swpaul */ 3236270Swpaul 33122678Sobrien#include <sys/cdefs.h> 34122678Sobrien__FBSDID("$FreeBSD: stable/11/sys/dev/tl/if_tl.c 347962 2019-05-18 20:43:13Z brooks $"); 35122678Sobrien 3636270Swpaul/* 3736270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3836270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3936270Swpaul * the National Semiconductor DP83840A physical interface and the 4036270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 4136270Swpaul * 4239583Swpaul * Written using the following four documents: 4336270Swpaul * 4436270Swpaul * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) 4536270Swpaul * National Semiconductor DP83840A data sheet (www.national.com) 4636270Swpaul * Microchip Technology 24C02C data sheet (www.microchip.com) 4739583Swpaul * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) 4836270Swpaul * 4936270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 5036270Swpaul * Electrical Engineering Department 5136270Swpaul * Columbia University, New York City 5236270Swpaul */ 5336270Swpaul/* 5436270Swpaul * Some notes about the ThunderLAN: 5536270Swpaul * 5636270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5736270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5839583Swpaul * independent interface (MII) bus. The MII allows the ThunderLAN chip to 5936270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 6036270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 6136270Swpaul * to act as a complete ethernet interface. 6236270Swpaul * 6336270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6436270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6536270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6639583Swpaul * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters 6739583Swpaul * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in 6839583Swpaul * concert with the ThunderLAN's internal PHY to provide full 10/100 6939583Swpaul * support. This is cheaper than using a standalone external PHY for both 7039583Swpaul * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. 7139583Swpaul * A serial EEPROM is also attached to the ThunderLAN chip to provide 7239583Swpaul * power-up default register settings and for storing the adapter's 7339583Swpaul * station address. Although not supported by this driver, the ThunderLAN 7439583Swpaul * chip can also be connected to token ring PHYs. 7536270Swpaul * 7636270Swpaul * The ThunderLAN has a set of registers which can be used to issue 7739583Swpaul * commands, acknowledge interrupts, and to manipulate other internal 7836270Swpaul * registers on its DIO bus. The primary registers can be accessed 7936270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 8036270Swpaul * depending on how the card is configured during the PCI probing 8136270Swpaul * phase. It is even possible to have both PIO and memory mapped 8236270Swpaul * access turned on at the same time. 8336270Swpaul * 8436270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 8536270Swpaul * using frame 'lists.' A list structure looks more or less like this: 8636270Swpaul * 8736270Swpaul * struct tl_frag { 8836270Swpaul * u_int32_t fragment_address; 8936270Swpaul * u_int32_t fragment_size; 9036270Swpaul * }; 9136270Swpaul * struct tl_list { 9236270Swpaul * u_int32_t forward_pointer; 9336270Swpaul * u_int16_t cstat; 9436270Swpaul * u_int16_t frame_size; 9536270Swpaul * struct tl_frag fragments[10]; 9636270Swpaul * }; 9736270Swpaul * 9836270Swpaul * The forward pointer in the list header can be either a 0 or the address 9936270Swpaul * of another list, which allows several lists to be linked together. Each 10036270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 10136270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 10236270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 10336270Swpaul * addresses are physical memory addresses, not virtual. Note also that 10436270Swpaul * a single ethernet frame can not span lists: if the host wants to 10536270Swpaul * transmit a frame and the frame data is split up over more than 10 10636270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 10736270Swpaul * 10836270Swpaul * To receive frames, the driver sets up a number of lists and populates 10936270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 11036270Swpaul * When a frame is received, the chip will DMA it into the memory regions 11136270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 11236270Swpaul * frame interrupt' when done. The driver may choose to use only one 11336270Swpaul * fragment per list; this may result is slighltly less efficient use 11436270Swpaul * of memory in exchange for improving performance. 11536270Swpaul * 11636270Swpaul * To transmit frames, the driver again sets up lists and fragment 11736270Swpaul * descriptors, only this time the buffers contain frame data that 11836270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 119298955Spfg * has transferred the data into its on-board SRAM, it will trigger a 12036270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 12136270Swpaul * interrupt when it reaches the end of the list. 12236270Swpaul */ 12336270Swpaul/* 12436270Swpaul * Some notes about this driver: 12536270Swpaul * 12636270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 12736270Swpaul * reception, transmission and interrupt handling. The simplest approach 12836270Swpaul * is to use one list each for transmission and reception. In this mode, 12936270Swpaul * the ThunderLAN will generate two interrupts for every received frame 13036270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 13136270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 13236270Swpaul * performance to have to handle so many interrupts. 13336270Swpaul * 13436270Swpaul * Initially I wanted to create a circular list of receive buffers so 13536270Swpaul * that the ThunderLAN chip would think there was an infinitely long 13636270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 13736270Swpaul * doesn't work correctly under heavy load: while the manual says the 13836270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 13936270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 14036270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 14136270Swpaul * is that the chip might traverse the entire circular list and then wrap 14236270Swpaul * around before you have a chance to do anything about it. Consequently, 14336270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 14436270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 14536270Swpaul * is shifted to the end of the list. This gives the appearance of an 14636270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 14736270Swpaul * the chip and allow all of the lists to be filled up. 14836270Swpaul * 14936270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 15036270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 15136270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 15236270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 15336270Swpaul * 15436270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 15536270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 15636270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 15736270Swpaul * queue several frames in one shot, and only have to handle a total 15836270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 15936270Swpaul * are transmitted. Frame transmission is done directly out of the 16036270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 16136270Swpaul * The driver simply sets up the fragment descriptors in the transmit 16236270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 16336270Swpaul * 16436270Swpaul * Note that since the RX and TX lists themselves are always used 16536270Swpaul * only by the driver, the are malloc()ed once at driver initialization 16636270Swpaul * time and never free()ed. 16736270Swpaul * 16836270Swpaul * Also, in order to remain as platform independent as possible, this 16936270Swpaul * driver uses memory mapped register access to manipulate the card 17036270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 17136270Swpaul * (and related) instructions which are specific to the i386 platform. 17236270Swpaul * 17336270Swpaul * Using these techniques, this driver achieves very high performance 17436270Swpaul * by minimizing the amount of interrupts generated during large 17536270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 17636270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 17736270Swpaul * itself thereby reducing the load on the host CPU. 17836270Swpaul */ 17936270Swpaul 18036270Swpaul#include <sys/param.h> 18136270Swpaul#include <sys/systm.h> 18236270Swpaul#include <sys/sockio.h> 18336270Swpaul#include <sys/mbuf.h> 18436270Swpaul#include <sys/malloc.h> 18536270Swpaul#include <sys/kernel.h> 186129878Sphk#include <sys/module.h> 18736270Swpaul#include <sys/socket.h> 18836270Swpaul 18936270Swpaul#include <net/if.h> 190257176Sglebius#include <net/if_var.h> 19136270Swpaul#include <net/if_arp.h> 19236270Swpaul#include <net/ethernet.h> 19336270Swpaul#include <net/if_dl.h> 19436270Swpaul#include <net/if_media.h> 195147256Sbrooks#include <net/if_types.h> 19636270Swpaul 19736270Swpaul#include <net/bpf.h> 19836270Swpaul 19936270Swpaul#include <vm/vm.h> /* for vtophys */ 20036270Swpaul#include <vm/pmap.h> /* for vtophys */ 20145155Swpaul#include <machine/bus.h> 20248992Swpaul#include <machine/resource.h> 20348992Swpaul#include <sys/bus.h> 20448992Swpaul#include <sys/rman.h> 20536270Swpaul 20650462Swpaul#include <dev/mii/mii.h> 207226995Smarius#include <dev/mii/mii_bitbang.h> 20850462Swpaul#include <dev/mii/miivar.h> 20950462Swpaul 210119288Simp#include <dev/pci/pcireg.h> 211119288Simp#include <dev/pci/pcivar.h> 21236270Swpaul 21339957Swpaul/* 21439957Swpaul * Default to using PIO register access mode to pacify certain 21539957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21639957Swpaul * don't seem to handle memory mapped mode properly. 21739957Swpaul */ 21839957Swpaul#define TL_USEIOSPACE 21939957Swpaul 220181738Simp#include <dev/tl/if_tlreg.h> 22136270Swpaul 222113506SmdoddMODULE_DEPEND(tl, pci, 1, 1, 1); 223113506SmdoddMODULE_DEPEND(tl, ether, 1, 1, 1); 22459758SpeterMODULE_DEPEND(tl, miibus, 1, 1, 1); 22559758Speter 226151545Simp/* "device miibus" required. See GENERIC if you get errors here. */ 22750462Swpaul#include "miibus_if.h" 22850462Swpaul 22936270Swpaul/* 23036270Swpaul * Various supported device vendors/types and their names. 23136270Swpaul */ 23236270Swpaul 233242625Sdimstatic const struct tl_type tl_devs[] = { 23436270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23536270Swpaul "Texas Instruments ThunderLAN" }, 23636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23736270Swpaul "Compaq Netelligent 10" }, 23836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 23936270Swpaul "Compaq Netelligent 10/100" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24136270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24336270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24536270Swpaul "Compaq NetFlex-3/P Integrated" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24736270Swpaul "Compaq NetFlex-3/P" }, 24836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 24936270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25037626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25137626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25237626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25337626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25437626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25537626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25637626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25737626Swpaul "Olicom OC-2183/2185" }, 25837626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 25937626Swpaul "Olicom OC-2325" }, 26037626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26137626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26236270Swpaul { 0, 0, NULL } 26336270Swpaul}; 26436270Swpaul 265142407Simpstatic int tl_probe(device_t); 266142407Simpstatic int tl_attach(device_t); 267142407Simpstatic int tl_detach(device_t); 268142407Simpstatic int tl_intvec_rxeoc(void *, u_int32_t); 269142407Simpstatic int tl_intvec_txeoc(void *, u_int32_t); 270142407Simpstatic int tl_intvec_txeof(void *, u_int32_t); 271142407Simpstatic int tl_intvec_rxeof(void *, u_int32_t); 272142407Simpstatic int tl_intvec_adchk(void *, u_int32_t); 273142407Simpstatic int tl_intvec_netsts(void *, u_int32_t); 27436270Swpaul 275142407Simpstatic int tl_newbuf(struct tl_softc *, struct tl_chain_onefrag *); 276142407Simpstatic void tl_stats_update(void *); 277142407Simpstatic int tl_encap(struct tl_softc *, struct tl_chain *, struct mbuf *); 27836270Swpaul 279142407Simpstatic void tl_intr(void *); 280142407Simpstatic void tl_start(struct ifnet *); 281150171Sjhbstatic void tl_start_locked(struct ifnet *); 282142407Simpstatic int tl_ioctl(struct ifnet *, u_long, caddr_t); 283142407Simpstatic void tl_init(void *); 284150171Sjhbstatic void tl_init_locked(struct tl_softc *); 285142407Simpstatic void tl_stop(struct tl_softc *); 286199560Sjhbstatic void tl_watchdog(struct tl_softc *); 287188463Simpstatic int tl_shutdown(device_t); 288142407Simpstatic int tl_ifmedia_upd(struct ifnet *); 289142407Simpstatic void tl_ifmedia_sts(struct ifnet *, struct ifmediareq *); 29036270Swpaul 291142407Simpstatic u_int8_t tl_eeprom_putbyte(struct tl_softc *, int); 292142407Simpstatic u_int8_t tl_eeprom_getbyte(struct tl_softc *, int, u_int8_t *); 293142407Simpstatic int tl_read_eeprom(struct tl_softc *, caddr_t, int, int); 29436270Swpaul 295142407Simpstatic int tl_miibus_readreg(device_t, int, int); 296142407Simpstatic int tl_miibus_writereg(device_t, int, int, int); 297142407Simpstatic void tl_miibus_statchg(device_t); 29836270Swpaul 299142407Simpstatic void tl_setmode(struct tl_softc *, int); 300142407Simpstatic uint32_t tl_mchash(const uint8_t *); 301142407Simpstatic void tl_setmulti(struct tl_softc *); 302142407Simpstatic void tl_setfilt(struct tl_softc *, caddr_t, int); 303142407Simpstatic void tl_softreset(struct tl_softc *, int); 304142407Simpstatic void tl_hardreset(device_t); 305142407Simpstatic int tl_list_rx_init(struct tl_softc *); 306142407Simpstatic int tl_list_tx_init(struct tl_softc *); 30736270Swpaul 308142407Simpstatic u_int8_t tl_dio_read8(struct tl_softc *, int); 309142407Simpstatic u_int16_t tl_dio_read16(struct tl_softc *, int); 310142407Simpstatic u_int32_t tl_dio_read32(struct tl_softc *, int); 311142407Simpstatic void tl_dio_write8(struct tl_softc *, int, int); 312142407Simpstatic void tl_dio_write16(struct tl_softc *, int, int); 313142407Simpstatic void tl_dio_write32(struct tl_softc *, int, int); 314142407Simpstatic void tl_dio_setbit(struct tl_softc *, int, int); 315142407Simpstatic void tl_dio_clrbit(struct tl_softc *, int, int); 316142407Simpstatic void tl_dio_setbit16(struct tl_softc *, int, int); 317142407Simpstatic void tl_dio_clrbit16(struct tl_softc *, int, int); 31839583Swpaul 319226995Smarius/* 320226995Smarius * MII bit-bang glue 321226995Smarius */ 322226995Smariusstatic uint32_t tl_mii_bitbang_read(device_t); 323226995Smariusstatic void tl_mii_bitbang_write(device_t, uint32_t); 324226995Smarius 325226995Smariusstatic const struct mii_bitbang_ops tl_mii_bitbang_ops = { 326226995Smarius tl_mii_bitbang_read, 327226995Smarius tl_mii_bitbang_write, 328226995Smarius { 329226995Smarius TL_SIO_MDATA, /* MII_BIT_MDO */ 330226995Smarius TL_SIO_MDATA, /* MII_BIT_MDI */ 331226995Smarius TL_SIO_MCLK, /* MII_BIT_MDC */ 332226995Smarius TL_SIO_MTXEN, /* MII_BIT_DIR_HOST_PHY */ 333226995Smarius 0, /* MII_BIT_DIR_PHY_HOST */ 334226995Smarius } 335226995Smarius}; 336226995Smarius 33749010Swpaul#ifdef TL_USEIOSPACE 33849010Swpaul#define TL_RES SYS_RES_IOPORT 33949010Swpaul#define TL_RID TL_PCI_LOIO 34049010Swpaul#else 34149010Swpaul#define TL_RES SYS_RES_MEMORY 34249010Swpaul#define TL_RID TL_PCI_LOMEM 34349010Swpaul#endif 34449010Swpaul 34548992Swpaulstatic device_method_t tl_methods[] = { 34648992Swpaul /* Device interface */ 34748992Swpaul DEVMETHOD(device_probe, tl_probe), 34848992Swpaul DEVMETHOD(device_attach, tl_attach), 34948992Swpaul DEVMETHOD(device_detach, tl_detach), 35048992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 35150462Swpaul 35250462Swpaul /* MII interface */ 35350462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 35450462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 35550462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 35650462Swpaul 357227843Smarius DEVMETHOD_END 35848992Swpaul}; 35948992Swpaul 36048992Swpaulstatic driver_t tl_driver = { 36151455Swpaul "tl", 36248992Swpaul tl_methods, 36348992Swpaul sizeof(struct tl_softc) 36448992Swpaul}; 36548992Swpaul 36648992Swpaulstatic devclass_t tl_devclass; 36748992Swpaul 368113506SmdoddDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 36951473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 37048992Swpaul 37139583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 37241656Swpaul struct tl_softc *sc; 37341656Swpaul int reg; 37439583Swpaul{ 375226995Smarius 376226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 377226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 37839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 379226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 380226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 38139583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 38239583Swpaul} 38339583Swpaul 38439583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 38541656Swpaul struct tl_softc *sc; 38641656Swpaul int reg; 38739583Swpaul{ 388226995Smarius 389226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 390226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 39139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 392226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 393226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 39439583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 39539583Swpaul} 39639583Swpaul 39739583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 39841656Swpaul struct tl_softc *sc; 39941656Swpaul int reg; 40039583Swpaul{ 401226995Smarius 402226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 403226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 40439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 405226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 406226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 40739583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 40839583Swpaul} 40939583Swpaul 41039583Swpaulstatic void tl_dio_write8(sc, reg, val) 41141656Swpaul struct tl_softc *sc; 41241656Swpaul int reg; 41341656Swpaul int val; 41439583Swpaul{ 415226995Smarius 416226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 417226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 41839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 419226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 420226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 42139583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 42239583Swpaul} 42339583Swpaul 42439583Swpaulstatic void tl_dio_write16(sc, reg, val) 42541656Swpaul struct tl_softc *sc; 42641656Swpaul int reg; 42741656Swpaul int val; 42839583Swpaul{ 429226995Smarius 430226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 431226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 43239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 433226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 434226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 43539583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 43639583Swpaul} 43739583Swpaul 43839583Swpaulstatic void tl_dio_write32(sc, reg, val) 43941656Swpaul struct tl_softc *sc; 44041656Swpaul int reg; 44141656Swpaul int val; 44239583Swpaul{ 443226995Smarius 444226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 445226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 44639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 447226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 448226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 44939583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 45039583Swpaul} 45139583Swpaul 452102336Salfredstatic void 453102336Salfredtl_dio_setbit(sc, reg, bit) 45441656Swpaul struct tl_softc *sc; 45541656Swpaul int reg; 45641656Swpaul int bit; 45739583Swpaul{ 45839583Swpaul u_int8_t f; 45939583Swpaul 460226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 461226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 46239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 463226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 464226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 46539583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 46639583Swpaul f |= bit; 467226995Smarius CSR_BARRIER(sc, TL_DIO_DATA + (reg & 3), 1, 468226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 46939583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 47039583Swpaul} 47139583Swpaul 472102336Salfredstatic void 473102336Salfredtl_dio_clrbit(sc, reg, bit) 47441656Swpaul struct tl_softc *sc; 47541656Swpaul int reg; 47641656Swpaul int bit; 47739583Swpaul{ 47839583Swpaul u_int8_t f; 47939583Swpaul 480226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 481226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 48239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 483226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 484226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 48539583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 48639583Swpaul f &= ~bit; 487226995Smarius CSR_BARRIER(sc, TL_DIO_DATA + (reg & 3), 1, 488226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 48939583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 49039583Swpaul} 49139583Swpaul 49239583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 49341656Swpaul struct tl_softc *sc; 49441656Swpaul int reg; 49541656Swpaul int bit; 49639583Swpaul{ 49739583Swpaul u_int16_t f; 49839583Swpaul 499226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 500226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 50139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 502226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 503226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 50439583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 50539583Swpaul f |= bit; 506226995Smarius CSR_BARRIER(sc, TL_DIO_DATA + (reg & 3), 2, 507226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 50839583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 50939583Swpaul} 51039583Swpaul 51139583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 51241656Swpaul struct tl_softc *sc; 51341656Swpaul int reg; 51441656Swpaul int bit; 51539583Swpaul{ 51639583Swpaul u_int16_t f; 51739583Swpaul 518226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 519226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 52039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 521226995Smarius CSR_BARRIER(sc, TL_DIO_ADDR, 2, 522226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 52339583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 52439583Swpaul f &= ~bit; 525226995Smarius CSR_BARRIER(sc, TL_DIO_DATA + (reg & 3), 2, 526226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 52739583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 52839583Swpaul} 52939583Swpaul 53036270Swpaul/* 53136270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 53236270Swpaul */ 53339583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 53439583Swpaul struct tl_softc *sc; 53541656Swpaul int byte; 53636270Swpaul{ 537331643Sdim int i, ack = 0; 53836270Swpaul 53936270Swpaul /* 54036270Swpaul * Make sure we're in TX mode. 54136270Swpaul */ 54239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 54336270Swpaul 54436270Swpaul /* 54536270Swpaul * Feed in each bit and stobe the clock. 54636270Swpaul */ 54736270Swpaul for (i = 0x80; i; i >>= 1) { 54836270Swpaul if (byte & i) { 54939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 55036270Swpaul } else { 55139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 55236270Swpaul } 55339583Swpaul DELAY(1); 55439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 55539583Swpaul DELAY(1); 55639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 55736270Swpaul } 55836270Swpaul 55936270Swpaul /* 56036270Swpaul * Turn off TX mode. 56136270Swpaul */ 56239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56336270Swpaul 56436270Swpaul /* 56536270Swpaul * Check for ack. 56636270Swpaul */ 56739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 56839583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 56939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57036270Swpaul 57136270Swpaul return(ack); 57236270Swpaul} 57336270Swpaul 57436270Swpaul/* 57536270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 57636270Swpaul */ 57739583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 57839583Swpaul struct tl_softc *sc; 57941656Swpaul int addr; 58036270Swpaul u_int8_t *dest; 58136270Swpaul{ 582331643Sdim int i; 58336270Swpaul u_int8_t byte = 0; 584162315Sglebius device_t tl_dev = sc->tl_dev; 58536270Swpaul 58639583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 58739583Swpaul 58836270Swpaul EEPROM_START; 58939583Swpaul 59036270Swpaul /* 59136270Swpaul * Send write control code to EEPROM. 59236270Swpaul */ 59339583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 594162315Sglebius device_printf(tl_dev, "failed to send write command, status: %x\n", 595105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 59636270Swpaul return(1); 59739583Swpaul } 59836270Swpaul 59936270Swpaul /* 60036270Swpaul * Send address of byte we want to read. 60136270Swpaul */ 60239583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 603162315Sglebius device_printf(tl_dev, "failed to send address, status: %x\n", 604105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 60536270Swpaul return(1); 60639583Swpaul } 60736270Swpaul 60836270Swpaul EEPROM_STOP; 60936270Swpaul EEPROM_START; 61036270Swpaul /* 61136270Swpaul * Send read control code to EEPROM. 61236270Swpaul */ 61339583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 614162315Sglebius device_printf(tl_dev, "failed to send write command, status: %x\n", 615105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 61636270Swpaul return(1); 61739583Swpaul } 61836270Swpaul 61936270Swpaul /* 62036270Swpaul * Start reading bits from EEPROM. 62136270Swpaul */ 62239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 62336270Swpaul for (i = 0x80; i; i >>= 1) { 62439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 62539583Swpaul DELAY(1); 62639583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 62736270Swpaul byte |= i; 62839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 62936501Swpaul DELAY(1); 63036270Swpaul } 63136270Swpaul 63236270Swpaul EEPROM_STOP; 63336270Swpaul 63436270Swpaul /* 63536270Swpaul * No ACK generated for read, so just return byte. 63636270Swpaul */ 63736270Swpaul 63836270Swpaul *dest = byte; 63936270Swpaul 64036270Swpaul return(0); 64136270Swpaul} 64236270Swpaul 64339583Swpaul/* 64439583Swpaul * Read a sequence of bytes from the EEPROM. 64539583Swpaul */ 646102336Salfredstatic int 647102336Salfredtl_read_eeprom(sc, dest, off, cnt) 64839583Swpaul struct tl_softc *sc; 64939583Swpaul caddr_t dest; 65039583Swpaul int off; 65139583Swpaul int cnt; 65236270Swpaul{ 65339583Swpaul int err = 0, i; 65439583Swpaul u_int8_t byte = 0; 65539583Swpaul 65639583Swpaul for (i = 0; i < cnt; i++) { 65739583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 65839583Swpaul if (err) 65939583Swpaul break; 66039583Swpaul *(dest + i) = byte; 66139583Swpaul } 66239583Swpaul 66339583Swpaul return(err ? 1 : 0); 66439583Swpaul} 66539583Swpaul 666226995Smarius#define TL_SIO_MII (TL_SIO_MCLK | TL_SIO_MDATA | TL_SIO_MTXEN) 667226995Smarius 668226995Smarius/* 669226995Smarius * Read the MII serial port for the MII bit-bang module. 670226995Smarius */ 671226995Smariusstatic uint32_t 672226995Smariustl_mii_bitbang_read(device_t dev) 67339583Swpaul{ 674226995Smarius struct tl_softc *sc; 675226995Smarius uint32_t val; 67636270Swpaul 677226995Smarius sc = device_get_softc(dev); 67836270Swpaul 679226995Smarius val = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MII; 680226995Smarius CSR_BARRIER(sc, TL_NETSIO, 1, 681226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 68236270Swpaul 683226995Smarius return (val); 68436270Swpaul} 68536270Swpaul 686226995Smarius/* 687226995Smarius * Write the MII serial port for the MII bit-bang module. 688226995Smarius */ 689102336Salfredstatic void 690226995Smariustl_mii_bitbang_write(device_t dev, uint32_t val) 69136270Swpaul{ 692226995Smarius struct tl_softc *sc; 69336270Swpaul 694226995Smarius sc = device_get_softc(dev); 695226995Smarius 696226995Smarius val = (tl_dio_read8(sc, TL_NETSIO) & ~TL_SIO_MII) | val; 697226995Smarius CSR_BARRIER(sc, TL_NETSIO, 1, 698226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 699226995Smarius tl_dio_write8(sc, TL_NETSIO, val); 700226995Smarius CSR_BARRIER(sc, TL_NETSIO, 1, 701226995Smarius BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 70236270Swpaul} 70336270Swpaul 704102336Salfredstatic int 705226995Smariustl_miibus_readreg(dev, phy, reg) 706226995Smarius device_t dev; 707226995Smarius int phy, reg; 708226995Smarius{ 70939583Swpaul struct tl_softc *sc; 710226995Smarius int minten, val; 71136270Swpaul 712226995Smarius sc = device_get_softc(dev); 71336270Swpaul 71436270Swpaul /* 71536270Swpaul * Turn off MII interrupt by forcing MINTEN low. 71636270Swpaul */ 71739583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 71836270Swpaul if (minten) { 71939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 72036270Swpaul } 72136270Swpaul 722226995Smarius val = mii_bitbang_readreg(dev, &tl_mii_bitbang_ops, phy, reg); 72336270Swpaul 724226995Smarius /* Reenable interrupts. */ 72536270Swpaul if (minten) { 72639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 72736270Swpaul } 72836270Swpaul 729226995Smarius return (val); 73036270Swpaul} 73136270Swpaul 732102336Salfredstatic int 733226995Smariustl_miibus_writereg(dev, phy, reg, data) 734226995Smarius device_t dev; 735226995Smarius int phy, reg, data; 736226995Smarius{ 73739583Swpaul struct tl_softc *sc; 73836270Swpaul int minten; 73936270Swpaul 740226995Smarius sc = device_get_softc(dev); 74136270Swpaul 74236270Swpaul /* 74336270Swpaul * Turn off MII interrupt by forcing MINTEN low. 74436270Swpaul */ 74539583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 74636270Swpaul if (minten) { 74739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 74836270Swpaul } 74936270Swpaul 750226995Smarius mii_bitbang_writereg(dev, &tl_mii_bitbang_ops, phy, reg, data); 75136270Swpaul 752226995Smarius /* Reenable interrupts. */ 753226995Smarius if (minten) { 75439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 755226995Smarius } 75636270Swpaul 75736270Swpaul return(0); 75836270Swpaul} 75936270Swpaul 760102336Salfredstatic void 761102336Salfredtl_miibus_statchg(dev) 76250462Swpaul device_t dev; 76350462Swpaul{ 76436270Swpaul struct tl_softc *sc; 76550462Swpaul struct mii_data *mii; 76636270Swpaul 76750462Swpaul sc = device_get_softc(dev); 76850462Swpaul mii = device_get_softc(sc->tl_miibus); 76936270Swpaul 77050462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 77150462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 77236270Swpaul } else { 77350462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 77436270Swpaul } 77536270Swpaul} 77636270Swpaul 77736270Swpaul/* 77850462Swpaul * Set modes for bitrate devices. 77936270Swpaul */ 780102336Salfredstatic void 781102336Salfredtl_setmode(sc, media) 78236270Swpaul struct tl_softc *sc; 78336270Swpaul int media; 78436270Swpaul{ 78550462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 78650462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 78736270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 78850462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 78936270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 79050462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 79139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 79236270Swpaul } else { 79350462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 79439583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 79536270Swpaul } 79636270Swpaul } 79736270Swpaul} 79836270Swpaul 79936464Swpaul/* 80036464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 80136464Swpaul * table. This hash is simply the address split into 6-bit chunks 80236464Swpaul * XOR'd, e.g. 80336464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 80436464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 80536464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 80636464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 80736464Swpaul */ 808123289Sobrienstatic uint32_t 809122625Sobrientl_mchash(addr) 810123289Sobrien const uint8_t *addr; 81136270Swpaul{ 812123289Sobrien int t; 81336270Swpaul 81436464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 81536464Swpaul (addr[2] ^ addr[5]); 81636464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 81736270Swpaul} 81836270Swpaul 81939583Swpaul/* 82039583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 82139583Swpaul * the multicast hash filter. The perfect filter can be programmed 82239583Swpaul * with up to four MAC addresses. The first one is always used to 82339583Swpaul * hold the station address, which leaves us free to use the other 82439583Swpaul * three for multicast addresses. 82539583Swpaul */ 826102336Salfredstatic void 827102336Salfredtl_setfilt(sc, addr, slot) 82839583Swpaul struct tl_softc *sc; 82941656Swpaul caddr_t addr; 83039583Swpaul int slot; 83139583Swpaul{ 83239583Swpaul int i; 83339583Swpaul u_int16_t regaddr; 83439583Swpaul 83539583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 83639583Swpaul 83739583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 83839583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 83939583Swpaul} 84039583Swpaul 84139583Swpaul/* 84239583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 84339583Swpaul * linked list. This is fine, except addresses are added from the head 84439583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 84539583Swpaul * group to always be in the perfect filter, but as more groups are added, 84639583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 84739583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 84839583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 84939583Swpaul * and into the hash table. 85039583Swpaul * 85139583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 85239583Swpaul * circular queue, we don't have the ability to just grab the tail of 85339583Swpaul * the list and traverse it backwards. Instead, we have to traverse 85439583Swpaul * the list once to find the tail, then traverse it again backwards to 85539583Swpaul * update the multicast filter. 85639583Swpaul */ 857102336Salfredstatic void 858102336Salfredtl_setmulti(sc) 85936270Swpaul struct tl_softc *sc; 86036270Swpaul{ 86136270Swpaul struct ifnet *ifp; 86236270Swpaul u_int32_t hashes[2] = { 0, 0 }; 86339583Swpaul int h, i; 86436270Swpaul struct ifmultiaddr *ifma; 86539583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 866147256Sbrooks ifp = sc->tl_ifp; 86736270Swpaul 86839583Swpaul /* First, zot all the existing filters. */ 86939583Swpaul for (i = 1; i < 4; i++) 87041656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 87139583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 87239583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 87339583Swpaul 87439583Swpaul /* Now program new ones. */ 87539583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 87636270Swpaul hashes[0] = 0xFFFFFFFF; 87736270Swpaul hashes[1] = 0xFFFFFFFF; 87836270Swpaul } else { 87939583Swpaul i = 1; 880195049Srwatson if_maddr_rlock(ifp); 88172084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 88236270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 88336270Swpaul continue; 88439583Swpaul /* 88539583Swpaul * Program the first three multicast groups 88639583Swpaul * into the perfect filter. For all others, 88739583Swpaul * use the hash table. 88839583Swpaul */ 88939583Swpaul if (i < 4) { 89039583Swpaul tl_setfilt(sc, 89139583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 89239583Swpaul i++; 89339583Swpaul continue; 89439583Swpaul } 89539583Swpaul 896122625Sobrien h = tl_mchash( 89736270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 89836270Swpaul if (h < 32) 89936270Swpaul hashes[0] |= (1 << h); 90036270Swpaul else 90136317Swpaul hashes[1] |= (1 << (h - 32)); 90236270Swpaul } 903195049Srwatson if_maddr_runlock(ifp); 90436270Swpaul } 90536270Swpaul 90639583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 90739583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 90836270Swpaul} 90936270Swpaul 91039583Swpaul/* 91139583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 91239583Swpaul * the internal PHY is powered up correctly. It also recommends a one 91339583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 91439583Swpaul * experience this isn't necessary. 91539583Swpaul */ 916102336Salfredstatic void 917102336Salfredtl_hardreset(dev) 91850468Swpaul device_t dev; 91950468Swpaul{ 92039583Swpaul int i; 92150468Swpaul u_int16_t flags; 92239583Swpaul 923226995Smarius mii_bitbang_sync(dev, &tl_mii_bitbang_ops); 92439583Swpaul 92550468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 92639583Swpaul 92750468Swpaul for (i = 0; i < MII_NPHY; i++) 92850468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 92939583Swpaul 93050468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 93139583Swpaul DELAY(50000); 93250468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 933226995Smarius mii_bitbang_sync(dev, &tl_mii_bitbang_ops); 93450468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 93539583Swpaul 93650468Swpaul DELAY(50000); 93739583Swpaul} 93839583Swpaul 939102336Salfredstatic void 940102336Salfredtl_softreset(sc, internal) 94139583Swpaul struct tl_softc *sc; 94236270Swpaul int internal; 94336270Swpaul{ 94439583Swpaul u_int32_t cmd, dummy, i; 94536270Swpaul 94636270Swpaul /* Assert the adapter reset bit. */ 94739583Swpaul CMD_SET(sc, TL_CMD_ADRST); 94850468Swpaul 94936270Swpaul /* Turn off interrupts */ 95039583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 95136270Swpaul 95236270Swpaul /* First, clear the stats registers. */ 95339583Swpaul for (i = 0; i < 5; i++) 95439583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 95536270Swpaul 95636270Swpaul /* Clear Areg and Hash registers */ 95739583Swpaul for (i = 0; i < 8; i++) 95839583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 95936270Swpaul 96036270Swpaul /* 96136270Swpaul * Set up Netconfig register. Enable one channel and 96236270Swpaul * one fragment mode. 96336270Swpaul */ 96439583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 96545155Swpaul if (internal && !sc->tl_bitrate) { 96639583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 96736270Swpaul } else { 96839583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 96936270Swpaul } 97036270Swpaul 97145155Swpaul /* Handle cards with bitrate devices. */ 97245155Swpaul if (sc->tl_bitrate) 97345155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 97445155Swpaul 97536270Swpaul /* 97636270Swpaul * Load adapter irq pacing timer and tx threshold. 97736270Swpaul * We make the transmit threshold 1 initially but we may 97836270Swpaul * change that later. 97936270Swpaul */ 98039583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 98136270Swpaul cmd |= TL_CMD_NES; 98236270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 98339583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 98439583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 98536270Swpaul 98636270Swpaul /* Unreset the MII */ 98739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 98836270Swpaul 98936270Swpaul /* Take the adapter out of reset */ 99039583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 99136270Swpaul 99236270Swpaul /* Wait for things to settle down a little. */ 99336270Swpaul DELAY(500); 99436270Swpaul} 99536270Swpaul 99636270Swpaul/* 99736270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 99839583Swpaul * against our list and return its name if we find a match. 99936270Swpaul */ 1000102336Salfredstatic int 1001102336Salfredtl_probe(dev) 100248992Swpaul device_t dev; 100336270Swpaul{ 1004226995Smarius const struct tl_type *t; 100536270Swpaul 100636270Swpaul t = tl_devs; 100736270Swpaul 100836270Swpaul while(t->tl_name != NULL) { 100948992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 101048992Swpaul (pci_get_device(dev) == t->tl_did)) { 101148992Swpaul device_set_desc(dev, t->tl_name); 1012142398Simp return (BUS_PROBE_DEFAULT); 101348992Swpaul } 101436270Swpaul t++; 101536270Swpaul } 101636270Swpaul 101748992Swpaul return(ENXIO); 101836270Swpaul} 101936270Swpaul 1020102336Salfredstatic int 1021102336Salfredtl_attach(dev) 102248992Swpaul device_t dev; 102336270Swpaul{ 102439583Swpaul u_int16_t did, vid; 1025226995Smarius const struct tl_type *t; 102639583Swpaul struct ifnet *ifp; 102739583Swpaul struct tl_softc *sc; 1028214264Smarius int error, flags, i, rid, unit; 1029147256Sbrooks u_char eaddr[6]; 103036270Swpaul 103148992Swpaul vid = pci_get_vendor(dev); 103248992Swpaul did = pci_get_device(dev); 103348992Swpaul sc = device_get_softc(dev); 1034162315Sglebius sc->tl_dev = dev; 103548992Swpaul unit = device_get_unit(dev); 103639583Swpaul 103739583Swpaul t = tl_devs; 103839583Swpaul while(t->tl_name != NULL) { 103939583Swpaul if (vid == t->tl_vid && did == t->tl_did) 104036270Swpaul break; 104139583Swpaul t++; 104239583Swpaul } 104336270Swpaul 104439583Swpaul if (t->tl_name == NULL) { 1045105599Sbrooks device_printf(dev, "unknown device!?\n"); 1046112878Sjhb return (ENXIO); 104736270Swpaul } 104836270Swpaul 104993818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 1050150171Sjhb MTX_DEF); 105169583Swpaul 105236270Swpaul /* 105336270Swpaul * Map control/status registers. 105436270Swpaul */ 105572813Swpaul pci_enable_busmaster(dev); 105636270Swpaul 105739583Swpaul#ifdef TL_USEIOSPACE 105839583Swpaul 105948992Swpaul rid = TL_PCI_LOIO; 1060127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1061127135Snjl RF_ACTIVE); 106248992Swpaul 106348992Swpaul /* 106448992Swpaul * Some cards have the I/O and memory mapped address registers 106548992Swpaul * reversed. Try both combinations before giving up. 106648992Swpaul */ 106748992Swpaul if (sc->tl_res == NULL) { 106848992Swpaul rid = TL_PCI_LOMEM; 1069127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1070127135Snjl RF_ACTIVE); 107145155Swpaul } 107239583Swpaul#else 107348992Swpaul rid = TL_PCI_LOMEM; 1074127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1075127135Snjl RF_ACTIVE); 107648992Swpaul if (sc->tl_res == NULL) { 107748992Swpaul rid = TL_PCI_LOIO; 1078127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1079127135Snjl RF_ACTIVE); 108036270Swpaul } 108139583Swpaul#endif 108236270Swpaul 108348992Swpaul if (sc->tl_res == NULL) { 1084105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 108548992Swpaul error = ENXIO; 108648992Swpaul goto fail; 108748992Swpaul } 108848992Swpaul 108939583Swpaul#ifdef notdef 109039583Swpaul /* 109139583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 109239583Swpaul * timer all the way up to its maximum value. I'm not sure 109339583Swpaul * if this is really necessary, but what the manual wants, 109439583Swpaul * the manual gets. 109539583Swpaul */ 109648992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 109739583Swpaul command |= 0x0000FF00; 109848992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 109939583Swpaul#endif 110036270Swpaul 110136270Swpaul /* Allocate interrupt */ 110248992Swpaul rid = 0; 1103127135Snjl sc->tl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 110448992Swpaul RF_SHAREABLE | RF_ACTIVE); 110548992Swpaul 110648992Swpaul if (sc->tl_irq == NULL) { 1107105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 110848992Swpaul error = ENXIO; 110936270Swpaul goto fail; 111036270Swpaul } 111136270Swpaul 111236270Swpaul /* 111351439Swpaul * Now allocate memory for the TX and RX lists. 111436270Swpaul */ 111551439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 111651657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 111739583Swpaul 111851439Swpaul if (sc->tl_ldata == NULL) { 1119105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 112048992Swpaul error = ENXIO; 112136270Swpaul goto fail; 112236270Swpaul } 112336270Swpaul 112439583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 112539583Swpaul 1126214264Smarius if (vid == COMPAQ_VENDORID || vid == TI_VENDORID) 112739583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 1128214264Smarius if (vid == OLICOM_VENDORID) 112939583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 113039583Swpaul 113139583Swpaul /* Reset the adapter. */ 113239583Swpaul tl_softreset(sc, 1); 113350468Swpaul tl_hardreset(dev); 113439583Swpaul tl_softreset(sc, 1); 113539583Swpaul 113638030Swpaul /* 113739583Swpaul * Get station address from the EEPROM. 113839583Swpaul */ 1139147256Sbrooks if (tl_read_eeprom(sc, eaddr, sc->tl_eeaddr, ETHER_ADDR_LEN)) { 1140105599Sbrooks device_printf(dev, "failed to read station address\n"); 114148992Swpaul error = ENXIO; 114239583Swpaul goto fail; 114339583Swpaul } 114439583Swpaul 114539583Swpaul /* 114639583Swpaul * XXX Olicom, in its desire to be different from the 114739583Swpaul * rest of the world, has done strange things with the 114839583Swpaul * encoding of the station address in the EEPROM. First 114939583Swpaul * of all, they store the address at offset 0xF8 rather 115039583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 115139583Swpaul * Second, they store the address in three 16-bit words in 115239583Swpaul * network byte order, as opposed to storing it sequentially 115339583Swpaul * like all the other ThunderLAN cards. In order to get 115439583Swpaul * the station address in a form that matches what the Olicom 115539583Swpaul * diagnostic utility specifies, we have to byte-swap each 115639583Swpaul * word. To make things even more confusing, neither 00:00:28 115739583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 115839583Swpaul */ 1159214264Smarius if (vid == OLICOM_VENDORID) { 116039583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 116139583Swpaul u_int16_t *p; 1162147256Sbrooks p = (u_int16_t *)&eaddr[i]; 116339583Swpaul *p = ntohs(*p); 116439583Swpaul } 116539583Swpaul } 116639583Swpaul 1167147256Sbrooks ifp = sc->tl_ifp = if_alloc(IFT_ETHER); 1168147256Sbrooks if (ifp == NULL) { 1169147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 1170147256Sbrooks error = ENOSPC; 1171147256Sbrooks goto fail; 1172147256Sbrooks } 117339583Swpaul ifp->if_softc = sc; 1174121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1175150171Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 117639583Swpaul ifp->if_ioctl = tl_ioctl; 117739583Swpaul ifp->if_start = tl_start; 117839583Swpaul ifp->if_init = tl_init; 117951439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 1180169414Syar ifp->if_capabilities |= IFCAP_VLAN_MTU; 1181169414Syar ifp->if_capenable |= IFCAP_VLAN_MTU; 1182150171Sjhb callout_init_mtx(&sc->tl_stat_callout, &sc->tl_mtx, 0); 118339583Swpaul 118439583Swpaul /* Reset the adapter again. */ 118539583Swpaul tl_softreset(sc, 1); 118650468Swpaul tl_hardreset(dev); 118739583Swpaul tl_softreset(sc, 1); 118839583Swpaul 118936270Swpaul /* 119050462Swpaul * Do MII setup. If no PHYs are found, then this is a 119150462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 119250462Swpaul * and AUI/BNC. 1193213894Smarius * XXX mii_attach() can fail for reason different than 1194213894Smarius * no PHYs found! 119536270Swpaul */ 1196214264Smarius flags = 0; 1197214264Smarius if (vid == COMPAQ_VENDORID) { 1198214264Smarius if (did == COMPAQ_DEVICEID_NETEL_10_100_PROLIANT || 1199214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED || 1200214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P_BNC || 1201214264Smarius did == COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX) 1202214264Smarius flags |= MIIF_MACPRIV0; 1203214264Smarius if (did == COMPAQ_DEVICEID_NETEL_10 || 1204214264Smarius did == COMPAQ_DEVICEID_NETEL_10_100_DUAL || 1205214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P || 1206214264Smarius did == COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED) 1207214264Smarius flags |= MIIF_MACPRIV1; 1208214264Smarius } else if (vid == OLICOM_VENDORID && did == OLICOM_DEVICEID_OC2183) 1209214264Smarius flags |= MIIF_MACPRIV0 | MIIF_MACPRIV1; 1210213894Smarius if (mii_attach(dev, &sc->tl_miibus, ifp, tl_ifmedia_upd, 1211213894Smarius tl_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0)) { 121245155Swpaul struct ifmedia *ifm; 121345155Swpaul sc->tl_bitrate = 1; 121445155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 121545155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 121645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 121745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 121845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 121945166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 122045155Swpaul /* Reset again, this time setting bitrate mode. */ 122145155Swpaul tl_softreset(sc, 1); 122245155Swpaul ifm = &sc->ifmedia; 122345155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 122445155Swpaul tl_ifmedia_upd(ifp); 122536270Swpaul } 122636270Swpaul 122739583Swpaul /* 122863090Sarchie * Call MI attach routine. 122939583Swpaul */ 1230147256Sbrooks ether_ifattach(ifp, eaddr); 123138030Swpaul 1232113609Snjl /* Hook interrupt last to avoid having to lock softc */ 1233150171Sjhb error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET | INTR_MPSAFE, 1234166901Spiso NULL, tl_intr, sc, &sc->tl_intrhand); 1235112872Snjl 1236112872Snjl if (error) { 1237112872Snjl device_printf(dev, "couldn't set up irq\n"); 1238113609Snjl ether_ifdetach(ifp); 1239112872Snjl goto fail; 1240112872Snjl } 1241112872Snjl 1242347962Sbrooks gone_by_fcp101_dev(dev); 1243347962Sbrooks 124436270Swpaulfail: 1245112872Snjl if (error) 1246112872Snjl tl_detach(dev); 1247112872Snjl 124848992Swpaul return(error); 124936270Swpaul} 125036270Swpaul 1251113609Snjl/* 1252113609Snjl * Shutdown hardware and free up resources. This can be called any 1253113609Snjl * time after the mutex has been initialized. It is called in both 1254113609Snjl * the error case in attach and the normal detach case so it needs 1255113609Snjl * to be careful about only freeing resources that have actually been 1256113609Snjl * allocated. 1257113609Snjl */ 1258102336Salfredstatic int 1259102336Salfredtl_detach(dev) 126048992Swpaul device_t dev; 126148992Swpaul{ 126248992Swpaul struct tl_softc *sc; 126348992Swpaul struct ifnet *ifp; 126448992Swpaul 126548992Swpaul sc = device_get_softc(dev); 1266112880Sjhb KASSERT(mtx_initialized(&sc->tl_mtx), ("tl mutex not initialized")); 1267147256Sbrooks ifp = sc->tl_ifp; 126848992Swpaul 1269113609Snjl /* These should only be active if attach succeeded */ 1270113812Simp if (device_is_attached(dev)) { 1271199560Sjhb ether_ifdetach(ifp); 1272150171Sjhb TL_LOCK(sc); 1273113609Snjl tl_stop(sc); 1274150171Sjhb TL_UNLOCK(sc); 1275150171Sjhb callout_drain(&sc->tl_stat_callout); 1276150213Sru } 1277113609Snjl if (sc->tl_miibus) 1278112872Snjl device_delete_child(dev, sc->tl_miibus); 1279113609Snjl bus_generic_detach(dev); 128048992Swpaul 1281112872Snjl if (sc->tl_ldata) 1282112872Snjl contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 128350462Swpaul if (sc->tl_bitrate) 128450462Swpaul ifmedia_removeall(&sc->ifmedia); 128548992Swpaul 1286112872Snjl if (sc->tl_intrhand) 1287112872Snjl bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 1288112872Snjl if (sc->tl_irq) 1289112872Snjl bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 1290112872Snjl if (sc->tl_res) 1291112872Snjl bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 129248992Swpaul 1293151297Sru if (ifp) 1294151297Sru if_free(ifp); 1295151297Sru 129667087Swpaul mtx_destroy(&sc->tl_mtx); 129748992Swpaul 129848992Swpaul return(0); 129948992Swpaul} 130048992Swpaul 130136270Swpaul/* 130236270Swpaul * Initialize the transmit lists. 130336270Swpaul */ 1304102336Salfredstatic int 1305102336Salfredtl_list_tx_init(sc) 130636270Swpaul struct tl_softc *sc; 130736270Swpaul{ 130836270Swpaul struct tl_chain_data *cd; 130936270Swpaul struct tl_list_data *ld; 131036270Swpaul int i; 131136270Swpaul 131236270Swpaul cd = &sc->tl_cdata; 131336270Swpaul ld = sc->tl_ldata; 131436270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 131536270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 131636270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 131736270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 131836270Swpaul else 131936270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 132036270Swpaul } 132136270Swpaul 132236270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 132336270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 132436270Swpaul sc->tl_txeoc = 1; 132536270Swpaul 132636270Swpaul return(0); 132736270Swpaul} 132836270Swpaul 132936270Swpaul/* 133036270Swpaul * Initialize the RX lists and allocate mbufs for them. 133136270Swpaul */ 1332102336Salfredstatic int 1333102336Salfredtl_list_rx_init(sc) 133436270Swpaul struct tl_softc *sc; 133536270Swpaul{ 1336226995Smarius struct tl_chain_data *cd; 1337226995Smarius struct tl_list_data *ld; 1338226995Smarius int i; 133936270Swpaul 134036270Swpaul cd = &sc->tl_cdata; 134136270Swpaul ld = sc->tl_ldata; 134236270Swpaul 134340795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 134436270Swpaul cd->tl_rx_chain[i].tl_ptr = 134537626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 134639583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 134739583Swpaul return(ENOBUFS); 134840795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 134936270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 135036270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 135136270Swpaul } else { 135236270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 135336270Swpaul ld->tl_rx_list[i].tlist_fptr = 135436270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 135536270Swpaul } 135636270Swpaul } 135736270Swpaul 135836270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 135936270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 136036270Swpaul 136136270Swpaul return(0); 136236270Swpaul} 136336270Swpaul 1364102336Salfredstatic int 1365102336Salfredtl_newbuf(sc, c) 136636270Swpaul struct tl_softc *sc; 136737626Swpaul struct tl_chain_onefrag *c; 136836270Swpaul{ 136936270Swpaul struct mbuf *m_new = NULL; 137036270Swpaul 1371243857Sglebius m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 137287846Sluigi if (m_new == NULL) 137336270Swpaul return(ENOBUFS); 137436270Swpaul 137536270Swpaul c->tl_mbuf = m_new; 137636270Swpaul c->tl_next = NULL; 137736270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 137836270Swpaul c->tl_ptr->tlist_fptr = 0; 137937626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 138037626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 138156060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 138236270Swpaul 138336270Swpaul return(0); 138436270Swpaul} 138536270Swpaul/* 138636270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 138736270Swpaul * tells us that a full ethernet frame has been captured and we need 138836270Swpaul * to handle it. 138936270Swpaul * 139036270Swpaul * Reception is done using 'lists' which consist of a header and a 139136270Swpaul * series of 10 data count/data address pairs that point to buffers. 139236270Swpaul * Initially you're supposed to create a list, populate it with pointers 139336270Swpaul * to buffers, then load the physical address of the list into the 139436270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 139536270Swpaul * frame into the buffers for you. 139636270Swpaul * 139736270Swpaul * To make things as fast as possible, we have the chip DMA directly 139836270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 139936270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 140036270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 140136270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 140236270Swpaul * the chip, it will always think it has an endless receive channel. 140336270Swpaul * 140436270Swpaul * If we happen to fall behind and the chip manages to fill up all of 140536270Swpaul * the buffers, it will generate an end of channel interrupt and wait 140636270Swpaul * for us to empty the chain and restart the receiver. 140736270Swpaul */ 1408102336Salfredstatic int 1409102336Salfredtl_intvec_rxeof(xsc, type) 141036270Swpaul void *xsc; 141136270Swpaul u_int32_t type; 141236270Swpaul{ 141336270Swpaul struct tl_softc *sc; 141436270Swpaul int r = 0, total_len = 0; 141536270Swpaul struct ether_header *eh; 141636270Swpaul struct mbuf *m; 141736270Swpaul struct ifnet *ifp; 141837626Swpaul struct tl_chain_onefrag *cur_rx; 141936270Swpaul 142036270Swpaul sc = xsc; 1421147256Sbrooks ifp = sc->tl_ifp; 142236270Swpaul 1423122689Ssam TL_LOCK_ASSERT(sc); 1424122689Ssam 142556060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 142656060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 142756060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 142856060Swpaul break; 142936270Swpaul r++; 143036270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 143136270Swpaul m = cur_rx->tl_mbuf; 143236270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 143336270Swpaul 143439583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 1435271803Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 143639583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 143739583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 143839583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 143939583Swpaul continue; 144039583Swpaul } 144136270Swpaul 144236270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 144336270Swpaul vtophys(cur_rx->tl_ptr); 144436270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 144536270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 144636270Swpaul 144737626Swpaul /* 144837626Swpaul * Note: when the ThunderLAN chip is in 'capture all 144937626Swpaul * frames' mode, it will receive its own transmissions. 145037626Swpaul * We drop don't need to process our own transmissions, 145137626Swpaul * so we drop them here and continue. 145237626Swpaul */ 1453106936Ssam eh = mtod(m, struct ether_header *); 145439583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 1455152315Sru if (!bcmp(eh->ether_shost, IF_LLADDR(sc->tl_ifp), 145637626Swpaul ETHER_ADDR_LEN)) { 145737626Swpaul m_freem(m); 145837626Swpaul continue; 145937626Swpaul } 146037626Swpaul 1461106936Ssam m->m_pkthdr.rcvif = ifp; 1462106936Ssam m->m_pkthdr.len = m->m_len = total_len; 1463106936Ssam 1464122689Ssam TL_UNLOCK(sc); 1465106936Ssam (*ifp->if_input)(ifp, m); 1466122689Ssam TL_LOCK(sc); 146736270Swpaul } 146836270Swpaul 146936270Swpaul return(r); 147036270Swpaul} 147136270Swpaul 147236270Swpaul/* 147336270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 147436270Swpaul * initialized or the adapter reached a list with a forward pointer 147536270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 147636270Swpaul * the card has hit the end of the receive buffer chain and we need to 147736270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 147836270Swpaul */ 1479102336Salfredstatic int 1480102336Salfredtl_intvec_rxeoc(xsc, type) 148136270Swpaul void *xsc; 148236270Swpaul u_int32_t type; 148336270Swpaul{ 148436270Swpaul struct tl_softc *sc; 148536270Swpaul int r; 148656060Swpaul struct tl_chain_data *cd; 148736270Swpaul 148856060Swpaul 148936270Swpaul sc = xsc; 149056060Swpaul cd = &sc->tl_cdata; 149136270Swpaul 149236270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 149336270Swpaul r = tl_intvec_rxeof(xsc, type); 149439583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 149536270Swpaul r = 1; 149656060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 149756060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 149839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 149936270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 150036270Swpaul return(r); 150136270Swpaul} 150236270Swpaul 1503102336Salfredstatic int 1504102336Salfredtl_intvec_txeof(xsc, type) 150536270Swpaul void *xsc; 150636270Swpaul u_int32_t type; 150736270Swpaul{ 150836270Swpaul struct tl_softc *sc; 150936270Swpaul int r = 0; 151036270Swpaul struct tl_chain *cur_tx; 151136270Swpaul 151236270Swpaul sc = xsc; 151336270Swpaul 151436270Swpaul /* 151536270Swpaul * Go through our tx list and free mbufs for those 151636270Swpaul * frames that have been sent. 151736270Swpaul */ 151836270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 151936270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 152036270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 152136270Swpaul break; 152236270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 152336270Swpaul 152436270Swpaul r++; 152536270Swpaul m_freem(cur_tx->tl_mbuf); 152636270Swpaul cur_tx->tl_mbuf = NULL; 152736270Swpaul 152836270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 152936270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 153037626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 153137626Swpaul break; 153236270Swpaul } 153336270Swpaul 153436270Swpaul return(r); 153536270Swpaul} 153636270Swpaul 153736270Swpaul/* 153836270Swpaul * The transmit end of channel interrupt. The adapter triggers this 153936270Swpaul * interrupt to tell us it hit the end of the current transmit list. 154036270Swpaul * 154136270Swpaul * A note about this: it's possible for a condition to arise where 154236270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 154336270Swpaul * You have to avoid this since the chip expects things to go in a 154436270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 154536270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 154636270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 154736270Swpaul * interrupt should be received and acknowledged before any more frames 154836270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 154936270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 155036270Swpaul * it could attempt to issue a transmit command prematurely. 155136270Swpaul * 155236270Swpaul * To guard against this, tl_start() will only issue transmit commands 155336270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 155436270Swpaul * can set this flag once tl_start() has cleared it. 155536270Swpaul */ 1556102336Salfredstatic int 1557102336Salfredtl_intvec_txeoc(xsc, type) 155836270Swpaul void *xsc; 155936270Swpaul u_int32_t type; 156036270Swpaul{ 156136270Swpaul struct tl_softc *sc; 156236270Swpaul struct ifnet *ifp; 156336270Swpaul u_int32_t cmd; 156436270Swpaul 156536270Swpaul sc = xsc; 1566147256Sbrooks ifp = sc->tl_ifp; 156736270Swpaul 156836270Swpaul /* Clear the timeout timer. */ 1569199560Sjhb sc->tl_timer = 0; 157036270Swpaul 157136270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 1572148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 157336270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 157436270Swpaul sc->tl_txeoc = 1; 157536270Swpaul } else { 157636270Swpaul sc->tl_txeoc = 0; 157736270Swpaul /* First we have to ack the EOC interrupt. */ 157839583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 157936270Swpaul /* Then load the address of the next TX list. */ 158039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 158151439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 158236270Swpaul /* Restart TX channel. */ 158339583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 158436270Swpaul cmd &= ~TL_CMD_RT; 158536270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 158639583Swpaul CMD_PUT(sc, cmd); 158736270Swpaul return(0); 158836270Swpaul } 158936270Swpaul 159036270Swpaul return(1); 159136270Swpaul} 159236270Swpaul 1593102336Salfredstatic int 1594102336Salfredtl_intvec_adchk(xsc, type) 159536270Swpaul void *xsc; 159636270Swpaul u_int32_t type; 159736270Swpaul{ 159836270Swpaul struct tl_softc *sc; 159936270Swpaul 160036270Swpaul sc = xsc; 160136270Swpaul 160239627Swpaul if (type) 1603162315Sglebius device_printf(sc->tl_dev, "adapter check: %x\n", 160441656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 160536270Swpaul 160639583Swpaul tl_softreset(sc, 1); 160737626Swpaul tl_stop(sc); 1608150171Sjhb tl_init_locked(sc); 160939583Swpaul CMD_SET(sc, TL_CMD_INTSON); 161036270Swpaul 161136270Swpaul return(0); 161236270Swpaul} 161336270Swpaul 1614102336Salfredstatic int 1615102336Salfredtl_intvec_netsts(xsc, type) 161636270Swpaul void *xsc; 161736270Swpaul u_int32_t type; 161836270Swpaul{ 161936270Swpaul struct tl_softc *sc; 162036270Swpaul u_int16_t netsts; 162136270Swpaul 162236270Swpaul sc = xsc; 162336270Swpaul 162439583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 162539583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 162636270Swpaul 1627162315Sglebius device_printf(sc->tl_dev, "network status: %x\n", netsts); 162836270Swpaul 162936270Swpaul return(1); 163036270Swpaul} 163136270Swpaul 1632102336Salfredstatic void 1633102336Salfredtl_intr(xsc) 163439583Swpaul void *xsc; 163536270Swpaul{ 163636270Swpaul struct tl_softc *sc; 163736270Swpaul struct ifnet *ifp; 163836270Swpaul int r = 0; 163936270Swpaul u_int32_t type = 0; 164036270Swpaul u_int16_t ints = 0; 164136270Swpaul u_int8_t ivec = 0; 164236270Swpaul 164339583Swpaul sc = xsc; 164467087Swpaul TL_LOCK(sc); 164536270Swpaul 164636270Swpaul /* Disable interrupts */ 164739583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 164839583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 164936270Swpaul type = (ints << 16) & 0xFFFF0000; 165036270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 165136270Swpaul ints = (ints & TL_INT_MASK) >> 2; 165236270Swpaul 1653147256Sbrooks ifp = sc->tl_ifp; 165436270Swpaul 165536270Swpaul switch(ints) { 165636270Swpaul case (TL_INTR_INVALID): 165739583Swpaul#ifdef DIAGNOSTIC 1658162315Sglebius device_printf(sc->tl_dev, "got an invalid interrupt!\n"); 165939583Swpaul#endif 166039583Swpaul /* Re-enable interrupts but don't ack this one. */ 166139583Swpaul CMD_PUT(sc, type); 166239583Swpaul r = 0; 166336270Swpaul break; 166436270Swpaul case (TL_INTR_TXEOF): 166536270Swpaul r = tl_intvec_txeof((void *)sc, type); 166636270Swpaul break; 166736270Swpaul case (TL_INTR_TXEOC): 166836270Swpaul r = tl_intvec_txeoc((void *)sc, type); 166936270Swpaul break; 167036270Swpaul case (TL_INTR_STATOFLOW): 167139583Swpaul tl_stats_update(sc); 167239583Swpaul r = 1; 167336270Swpaul break; 167436270Swpaul case (TL_INTR_RXEOF): 167536270Swpaul r = tl_intvec_rxeof((void *)sc, type); 167636270Swpaul break; 167736270Swpaul case (TL_INTR_DUMMY): 1678162315Sglebius device_printf(sc->tl_dev, "got a dummy interrupt\n"); 167939583Swpaul r = 1; 168036270Swpaul break; 168136270Swpaul case (TL_INTR_ADCHK): 168236270Swpaul if (ivec) 168336270Swpaul r = tl_intvec_adchk((void *)sc, type); 168436270Swpaul else 168536270Swpaul r = tl_intvec_netsts((void *)sc, type); 168636270Swpaul break; 168736270Swpaul case (TL_INTR_RXEOC): 168836270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 168936270Swpaul break; 169036270Swpaul default: 1691162315Sglebius device_printf(sc->tl_dev, "bogus interrupt type\n"); 169236270Swpaul break; 169336270Swpaul } 169436270Swpaul 169536270Swpaul /* Re-enable interrupts */ 169637626Swpaul if (r) { 169739583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 169837626Swpaul } 169936270Swpaul 170037626Swpaul if (ifp->if_snd.ifq_head != NULL) 1701150171Sjhb tl_start_locked(ifp); 170237626Swpaul 170367087Swpaul TL_UNLOCK(sc); 170436270Swpaul} 170536270Swpaul 1706102336Salfredstatic void 1707102336Salfredtl_stats_update(xsc) 170836270Swpaul void *xsc; 170936270Swpaul{ 171036270Swpaul struct tl_softc *sc; 171136270Swpaul struct ifnet *ifp; 171236270Swpaul struct tl_stats tl_stats; 171350462Swpaul struct mii_data *mii; 171436270Swpaul u_int32_t *p; 171536270Swpaul 171636270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 171736270Swpaul 171836270Swpaul sc = xsc; 1719150171Sjhb TL_LOCK_ASSERT(sc); 1720147256Sbrooks ifp = sc->tl_ifp; 172136270Swpaul 172236270Swpaul p = (u_int32_t *)&tl_stats; 172336270Swpaul 172439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 172539583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 172639583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 172739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 172839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 172939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 173036270Swpaul 1731271803Sglebius if_inc_counter(ifp, IFCOUNTER_OPACKETS, tl_tx_goodframes(tl_stats)); 1732271803Sglebius if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1733271803Sglebius tl_stats.tl_tx_single_collision + tl_stats.tl_tx_multi_collision); 1734271803Sglebius if_inc_counter(ifp, IFCOUNTER_IPACKETS, tl_rx_goodframes(tl_stats)); 1735271803Sglebius if_inc_counter(ifp, IFCOUNTER_IERRORS, tl_stats.tl_crc_errors + 1736271803Sglebius tl_stats.tl_code_errors + tl_rx_overrun(tl_stats)); 1737271803Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, tl_tx_underrun(tl_stats)); 173836270Swpaul 173951439Swpaul if (tl_tx_underrun(tl_stats)) { 174051439Swpaul u_int8_t tx_thresh; 174151439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 174251439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 174351439Swpaul tx_thresh >>= 4; 174451439Swpaul tx_thresh++; 1745162315Sglebius device_printf(sc->tl_dev, "tx underrun -- increasing " 1746105599Sbrooks "tx threshold to %d bytes\n", 174751439Swpaul (64 * (tx_thresh * 4))); 174851439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 174951439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 175051439Swpaul } 175151439Swpaul } 175251439Swpaul 1753199560Sjhb if (sc->tl_timer > 0 && --sc->tl_timer == 0) 1754199560Sjhb tl_watchdog(sc); 1755199560Sjhb 1756150171Sjhb callout_reset(&sc->tl_stat_callout, hz, tl_stats_update, sc); 175736302Swpaul 175850462Swpaul if (!sc->tl_bitrate) { 175950462Swpaul mii = device_get_softc(sc->tl_miibus); 176050462Swpaul mii_tick(mii); 176150462Swpaul } 176236270Swpaul} 176336270Swpaul 176436270Swpaul/* 176536270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 176636270Swpaul * pointers to the fragment pointers. 176736270Swpaul */ 1768102336Salfredstatic int 1769102336Salfredtl_encap(sc, c, m_head) 177036270Swpaul struct tl_softc *sc; 177136270Swpaul struct tl_chain *c; 177236270Swpaul struct mbuf *m_head; 177336270Swpaul{ 177436270Swpaul int frag = 0; 177536270Swpaul struct tl_frag *f = NULL; 177636270Swpaul int total_len; 177736270Swpaul struct mbuf *m; 1778147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 177936270Swpaul 178036270Swpaul /* 178136270Swpaul * Start packing the mbufs in this chain into 178236270Swpaul * the fragment pointers. Stop when we run out 178336270Swpaul * of fragments or hit the end of the mbuf chain. 178436270Swpaul */ 178536270Swpaul m = m_head; 178636270Swpaul total_len = 0; 178736270Swpaul 178836270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 178936270Swpaul if (m->m_len != 0) { 179036270Swpaul if (frag == TL_MAXFRAGS) 179136270Swpaul break; 179236270Swpaul total_len+= m->m_len; 179336270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 179436270Swpaul vtophys(mtod(m, vm_offset_t)); 179536270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 179636270Swpaul frag++; 179736270Swpaul } 179836270Swpaul } 179936270Swpaul 180036270Swpaul /* 180136270Swpaul * Handle special cases. 180236270Swpaul * Special case #1: we used up all 10 fragments, but 180336270Swpaul * we have more mbufs left in the chain. Copy the 180436270Swpaul * data into an mbuf cluster. Note that we don't 180536270Swpaul * bother clearing the values in the other fragment 180636270Swpaul * pointers/counters; it wouldn't gain us anything, 180736270Swpaul * and would waste cycles. 180836270Swpaul */ 180936270Swpaul if (m != NULL) { 181036270Swpaul struct mbuf *m_new = NULL; 181136270Swpaul 1812243857Sglebius MGETHDR(m_new, M_NOWAIT, MT_DATA); 181336270Swpaul if (m_new == NULL) { 1814105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 181536270Swpaul return(1); 181636270Swpaul } 181736270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 1818276750Srwatson if (!(MCLGET(m_new, M_NOWAIT))) { 181936270Swpaul m_freem(m_new); 1820105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 182136270Swpaul return(1); 182236270Swpaul } 182336270Swpaul } 182436270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 182536270Swpaul mtod(m_new, caddr_t)); 182636270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 182736270Swpaul m_freem(m_head); 182836270Swpaul m_head = m_new; 182936270Swpaul f = &c->tl_ptr->tl_frag[0]; 183036270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 183136270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 183236270Swpaul frag = 1; 183336270Swpaul } 183436270Swpaul 183536270Swpaul /* 183636270Swpaul * Special case #2: the frame is smaller than the minimum 183736270Swpaul * frame size. We have to pad it to make the chip happy. 183836270Swpaul */ 183936270Swpaul if (total_len < TL_MIN_FRAMELEN) { 184036270Swpaul if (frag == TL_MAXFRAGS) 1841105599Sbrooks if_printf(ifp, 1842105599Sbrooks "all frags filled but frame still to small!\n"); 184336270Swpaul f = &c->tl_ptr->tl_frag[frag]; 184436270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 184536270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 184636270Swpaul total_len += f->tlist_dcnt; 184736270Swpaul frag++; 184836270Swpaul } 184936270Swpaul 185036270Swpaul c->tl_mbuf = m_head; 185136270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 185236270Swpaul c->tl_ptr->tlist_frsize = total_len; 185336270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 185436270Swpaul c->tl_ptr->tlist_fptr = 0; 185536270Swpaul 185636270Swpaul return(0); 185736270Swpaul} 185836270Swpaul 185936270Swpaul/* 186036270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 186136270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 186236270Swpaul * copy of the pointers since the transmit list fragment pointers are 186336270Swpaul * physical addresses. 186436270Swpaul */ 1865102336Salfredstatic void 1866102336Salfredtl_start(ifp) 186736270Swpaul struct ifnet *ifp; 186836270Swpaul{ 186936270Swpaul struct tl_softc *sc; 1870150171Sjhb 1871150171Sjhb sc = ifp->if_softc; 1872150171Sjhb TL_LOCK(sc); 1873150171Sjhb tl_start_locked(ifp); 1874150171Sjhb TL_UNLOCK(sc); 1875150171Sjhb} 1876150171Sjhb 1877150171Sjhbstatic void 1878150171Sjhbtl_start_locked(ifp) 1879150171Sjhb struct ifnet *ifp; 1880150171Sjhb{ 1881150171Sjhb struct tl_softc *sc; 188236270Swpaul struct mbuf *m_head = NULL; 188336270Swpaul u_int32_t cmd; 188436270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 188536270Swpaul 188636270Swpaul sc = ifp->if_softc; 1887150171Sjhb TL_LOCK_ASSERT(sc); 188836270Swpaul 188936270Swpaul /* 189036270Swpaul * Check for an available queue slot. If there are none, 189136270Swpaul * punt. 189236270Swpaul */ 189336270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 1894148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 189536270Swpaul return; 189636270Swpaul } 189736270Swpaul 189836270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 189936270Swpaul 190036270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 190136270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 190236270Swpaul if (m_head == NULL) 190336270Swpaul break; 190436270Swpaul 190536270Swpaul /* Pick a chain member off the free list. */ 190636270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 190736270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 190836270Swpaul 190936270Swpaul cur_tx->tl_next = NULL; 191036270Swpaul 191136270Swpaul /* Pack the data into the list. */ 191236270Swpaul tl_encap(sc, cur_tx, m_head); 191336270Swpaul 191436270Swpaul /* Chain it together */ 191536270Swpaul if (prev != NULL) { 191636270Swpaul prev->tl_next = cur_tx; 191736270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 191836270Swpaul } 191936270Swpaul prev = cur_tx; 192036270Swpaul 192136270Swpaul /* 192236270Swpaul * If there's a BPF listener, bounce a copy of this frame 192336270Swpaul * to him. 192436270Swpaul */ 1925106936Ssam BPF_MTAP(ifp, cur_tx->tl_mbuf); 192636270Swpaul } 192736270Swpaul 192836270Swpaul /* 192941526Swpaul * If there are no packets queued, bail. 193041526Swpaul */ 1931150171Sjhb if (cur_tx == NULL) 193241526Swpaul return; 193341526Swpaul 193441526Swpaul /* 193536270Swpaul * That's all we can stands, we can't stands no more. 193636270Swpaul * If there are no other transfers pending, then issue the 193736270Swpaul * TX GO command to the adapter to start things moving. 193836270Swpaul * Otherwise, just leave the data in the queue and let 193936270Swpaul * the EOF/EOC interrupt handler send. 194036270Swpaul */ 194136270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 194236270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 194336270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 194439583Swpaul 194536270Swpaul if (sc->tl_txeoc) { 194636270Swpaul sc->tl_txeoc = 0; 194739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 194839583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 194936270Swpaul cmd &= ~TL_CMD_RT; 195036270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 195139583Swpaul CMD_PUT(sc, cmd); 195236270Swpaul } 195336270Swpaul } else { 195436270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 195542146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 195636270Swpaul } 195736270Swpaul 195836270Swpaul /* 195936270Swpaul * Set a timeout in case the chip goes out to lunch. 196036270Swpaul */ 1961199560Sjhb sc->tl_timer = 5; 196236270Swpaul} 196336270Swpaul 1964102336Salfredstatic void 1965102336Salfredtl_init(xsc) 196636270Swpaul void *xsc; 196736270Swpaul{ 196836270Swpaul struct tl_softc *sc = xsc; 1969150171Sjhb 1970150171Sjhb TL_LOCK(sc); 1971150171Sjhb tl_init_locked(sc); 1972150171Sjhb TL_UNLOCK(sc); 1973150171Sjhb} 1974150171Sjhb 1975150171Sjhbstatic void 1976150171Sjhbtl_init_locked(sc) 1977150171Sjhb struct tl_softc *sc; 1978150171Sjhb{ 1979147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 198050462Swpaul struct mii_data *mii; 198136270Swpaul 1982150171Sjhb TL_LOCK_ASSERT(sc); 198336270Swpaul 1984147256Sbrooks ifp = sc->tl_ifp; 198536270Swpaul 198636270Swpaul /* 198736270Swpaul * Cancel pending I/O. 198836270Swpaul */ 198936270Swpaul tl_stop(sc); 199036270Swpaul 199151439Swpaul /* Initialize TX FIFO threshold */ 199251439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 199351439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 199451439Swpaul 199551439Swpaul /* Set PCI burst size */ 199651439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 199751439Swpaul 199836270Swpaul /* 199936270Swpaul * Set 'capture all frames' bit for promiscuous mode. 200036270Swpaul */ 200139583Swpaul if (ifp->if_flags & IFF_PROMISC) 200239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 200339583Swpaul else 200439583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 200536270Swpaul 200636270Swpaul /* 200736270Swpaul * Set capture broadcast bit to capture broadcast frames. 200836270Swpaul */ 200939583Swpaul if (ifp->if_flags & IFF_BROADCAST) 201039583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 201139583Swpaul else 201239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 201336270Swpaul 201450468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 201550468Swpaul 201636270Swpaul /* Init our MAC address */ 2017152315Sru tl_setfilt(sc, IF_LLADDR(sc->tl_ifp), 0); 201836270Swpaul 201939583Swpaul /* Init multicast filter, if needed. */ 202039583Swpaul tl_setmulti(sc); 202139583Swpaul 202236270Swpaul /* Init circular RX list. */ 202339583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2024162315Sglebius device_printf(sc->tl_dev, 2025105599Sbrooks "initialization failed: no memory for rx buffers\n"); 202639583Swpaul tl_stop(sc); 202736270Swpaul return; 202836270Swpaul } 202936270Swpaul 203036270Swpaul /* Init TX pointers. */ 203136270Swpaul tl_list_tx_init(sc); 203236270Swpaul 203339583Swpaul /* Enable PCI interrupts. */ 203439583Swpaul CMD_SET(sc, TL_CMD_INTSON); 203536270Swpaul 203636270Swpaul /* Load the address of the rx list */ 203739583Swpaul CMD_SET(sc, TL_CMD_RT); 203839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 203936270Swpaul 204050462Swpaul if (!sc->tl_bitrate) { 204150462Swpaul if (sc->tl_miibus != NULL) { 204250462Swpaul mii = device_get_softc(sc->tl_miibus); 204350462Swpaul mii_mediachg(mii); 204450462Swpaul } 2045113548Smdodd } else { 2046113548Smdodd tl_ifmedia_upd(ifp); 204750462Swpaul } 204838030Swpaul 204936270Swpaul /* Send the RX go command */ 205050468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 205136270Swpaul 2052148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 2053148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 205436270Swpaul 205536270Swpaul /* Start the stats update counter */ 2056150171Sjhb callout_reset(&sc->tl_stat_callout, hz, tl_stats_update, sc); 205736270Swpaul} 205836270Swpaul 205936270Swpaul/* 206036270Swpaul * Set media options. 206136270Swpaul */ 2062102336Salfredstatic int 2063102336Salfredtl_ifmedia_upd(ifp) 206436270Swpaul struct ifnet *ifp; 206536270Swpaul{ 206636270Swpaul struct tl_softc *sc; 206750462Swpaul struct mii_data *mii = NULL; 206836270Swpaul 206936270Swpaul sc = ifp->if_softc; 207036270Swpaul 2071150171Sjhb TL_LOCK(sc); 207250462Swpaul if (sc->tl_bitrate) 207350462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 207450462Swpaul else { 207550462Swpaul mii = device_get_softc(sc->tl_miibus); 207650462Swpaul mii_mediachg(mii); 207750462Swpaul } 2078150171Sjhb TL_UNLOCK(sc); 207936270Swpaul 208036270Swpaul return(0); 208136270Swpaul} 208236270Swpaul 208336270Swpaul/* 208436270Swpaul * Report current media status. 208536270Swpaul */ 2086102336Salfredstatic void 2087102336Salfredtl_ifmedia_sts(ifp, ifmr) 208836270Swpaul struct ifnet *ifp; 208936270Swpaul struct ifmediareq *ifmr; 209036270Swpaul{ 209136270Swpaul struct tl_softc *sc; 209250462Swpaul struct mii_data *mii; 209336270Swpaul 209436270Swpaul sc = ifp->if_softc; 209536270Swpaul 2096150171Sjhb TL_LOCK(sc); 209736270Swpaul ifmr->ifm_active = IFM_ETHER; 209836270Swpaul 209945155Swpaul if (sc->tl_bitrate) { 210045155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 210145155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 210245155Swpaul else 210345155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 210445155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 210545155Swpaul ifmr->ifm_active |= IFM_HDX; 210645155Swpaul else 210745155Swpaul ifmr->ifm_active |= IFM_FDX; 210845155Swpaul return; 210936270Swpaul } else { 211050462Swpaul mii = device_get_softc(sc->tl_miibus); 211150462Swpaul mii_pollstat(mii); 211250462Swpaul ifmr->ifm_active = mii->mii_media_active; 211350462Swpaul ifmr->ifm_status = mii->mii_media_status; 211436270Swpaul } 2115150171Sjhb TL_UNLOCK(sc); 211636270Swpaul} 211736270Swpaul 2118102336Salfredstatic int 2119102336Salfredtl_ioctl(ifp, command, data) 212036270Swpaul struct ifnet *ifp; 212136735Sdfr u_long command; 212236270Swpaul caddr_t data; 212336270Swpaul{ 212436270Swpaul struct tl_softc *sc = ifp->if_softc; 212536270Swpaul struct ifreq *ifr = (struct ifreq *) data; 2126150171Sjhb int error = 0; 212736270Swpaul 212836270Swpaul switch(command) { 212936270Swpaul case SIOCSIFFLAGS: 2130150171Sjhb TL_LOCK(sc); 213136270Swpaul if (ifp->if_flags & IFF_UP) { 2132148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING && 213350462Swpaul ifp->if_flags & IFF_PROMISC && 213450462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 213550462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 213650462Swpaul tl_setmulti(sc); 2137148887Srwatson } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 213850462Swpaul !(ifp->if_flags & IFF_PROMISC) && 213950462Swpaul sc->tl_if_flags & IFF_PROMISC) { 214050462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 214150462Swpaul tl_setmulti(sc); 214250462Swpaul } else 2143150171Sjhb tl_init_locked(sc); 214436270Swpaul } else { 2145148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 214636270Swpaul tl_stop(sc); 214736270Swpaul } 214836270Swpaul } 214950462Swpaul sc->tl_if_flags = ifp->if_flags; 2150150171Sjhb TL_UNLOCK(sc); 215136270Swpaul error = 0; 215236270Swpaul break; 215336270Swpaul case SIOCADDMULTI: 215436270Swpaul case SIOCDELMULTI: 2155150171Sjhb TL_LOCK(sc); 215636270Swpaul tl_setmulti(sc); 2157150171Sjhb TL_UNLOCK(sc); 215836270Swpaul error = 0; 215936270Swpaul break; 216036270Swpaul case SIOCSIFMEDIA: 216136270Swpaul case SIOCGIFMEDIA: 216250462Swpaul if (sc->tl_bitrate) 216350462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 216450462Swpaul else { 216550462Swpaul struct mii_data *mii; 216650462Swpaul mii = device_get_softc(sc->tl_miibus); 216750462Swpaul error = ifmedia_ioctl(ifp, ifr, 216850462Swpaul &mii->mii_media, command); 216950462Swpaul } 217036270Swpaul break; 217136270Swpaul default: 2172106936Ssam error = ether_ioctl(ifp, command, data); 217336270Swpaul break; 217436270Swpaul } 217536270Swpaul 217636270Swpaul return(error); 217736270Swpaul} 217836270Swpaul 2179102336Salfredstatic void 2180199560Sjhbtl_watchdog(sc) 2181199560Sjhb struct tl_softc *sc; 2182199560Sjhb{ 218336270Swpaul struct ifnet *ifp; 218436270Swpaul 2185199560Sjhb TL_LOCK_ASSERT(sc); 2186199560Sjhb ifp = sc->tl_ifp; 218736270Swpaul 2188105599Sbrooks if_printf(ifp, "device timeout\n"); 218936270Swpaul 2190271803Sglebius if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 219136270Swpaul 219250468Swpaul tl_softreset(sc, 1); 2193150171Sjhb tl_init_locked(sc); 219436270Swpaul} 219536270Swpaul 219636270Swpaul/* 219736270Swpaul * Stop the adapter and free any mbufs allocated to the 219836270Swpaul * RX and TX lists. 219936270Swpaul */ 2200102336Salfredstatic void 2201102336Salfredtl_stop(sc) 220236270Swpaul struct tl_softc *sc; 220336270Swpaul{ 2204331643Sdim int i; 220536270Swpaul struct ifnet *ifp; 220636270Swpaul 2207150171Sjhb TL_LOCK_ASSERT(sc); 220867087Swpaul 2209147256Sbrooks ifp = sc->tl_ifp; 221036270Swpaul 221136270Swpaul /* Stop the stats updater. */ 2212150171Sjhb callout_stop(&sc->tl_stat_callout); 221336270Swpaul 221436270Swpaul /* Stop the transmitter */ 221539583Swpaul CMD_CLR(sc, TL_CMD_RT); 221639583Swpaul CMD_SET(sc, TL_CMD_STOP); 221739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 221836270Swpaul 221936270Swpaul /* Stop the receiver */ 222039583Swpaul CMD_SET(sc, TL_CMD_RT); 222139583Swpaul CMD_SET(sc, TL_CMD_STOP); 222239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 222336270Swpaul 222436270Swpaul /* 222536270Swpaul * Disable host interrupts. 222636270Swpaul */ 222739583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 222836270Swpaul 222936270Swpaul /* 223036270Swpaul * Clear list pointer. 223136270Swpaul */ 223239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 223336270Swpaul 223436270Swpaul /* 223536270Swpaul * Free the RX lists. 223636270Swpaul */ 223736270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 223836270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 223936270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 224036270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 224136270Swpaul } 224236270Swpaul } 224336270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 224436270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 224536270Swpaul 224636270Swpaul /* 224736270Swpaul * Free the TX list buffers. 224836270Swpaul */ 224936270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 225036270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 225136270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 225236270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 225336270Swpaul } 225436270Swpaul } 225536270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 225636270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 225736270Swpaul 2258148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 225936270Swpaul} 226036270Swpaul 226136270Swpaul/* 226236270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 226336270Swpaul * get confused by errant DMAs when rebooting. 226436270Swpaul */ 2265188463Simpstatic int 2266102336Salfredtl_shutdown(dev) 226748992Swpaul device_t dev; 226836270Swpaul{ 226939583Swpaul struct tl_softc *sc; 227036270Swpaul 227148992Swpaul sc = device_get_softc(dev); 227236270Swpaul 2273150171Sjhb TL_LOCK(sc); 227439583Swpaul tl_stop(sc); 2275150171Sjhb TL_UNLOCK(sc); 227636270Swpaul 2277188463Simp return (0); 227836270Swpaul} 2279