if_tl.c revision 122678
136270Swpaul/* 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: head/sys/pci/if_tl.c 122678 2003-11-14 17:16:58Z obrien $"); 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 11936270Swpaul * has transfered 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> 18636270Swpaul#include <sys/socket.h> 18736270Swpaul 18836270Swpaul#include <net/if.h> 18936270Swpaul#include <net/if_arp.h> 19036270Swpaul#include <net/ethernet.h> 19136270Swpaul#include <net/if_dl.h> 19236270Swpaul#include <net/if_media.h> 19336270Swpaul 19436270Swpaul#include <net/bpf.h> 19536270Swpaul 19636270Swpaul#include <vm/vm.h> /* for vtophys */ 19736270Swpaul#include <vm/pmap.h> /* for vtophys */ 19845155Swpaul#include <machine/bus_memio.h> 19945155Swpaul#include <machine/bus_pio.h> 20045155Swpaul#include <machine/bus.h> 20148992Swpaul#include <machine/resource.h> 20248992Swpaul#include <sys/bus.h> 20348992Swpaul#include <sys/rman.h> 20436270Swpaul 20550462Swpaul#include <dev/mii/mii.h> 20650462Swpaul#include <dev/mii/miivar.h> 20750462Swpaul 208119288Simp#include <dev/pci/pcireg.h> 209119288Simp#include <dev/pci/pcivar.h> 21036270Swpaul 21139957Swpaul/* 21239957Swpaul * Default to using PIO register access mode to pacify certain 21339957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21439957Swpaul * don't seem to handle memory mapped mode properly. 21539957Swpaul */ 21639957Swpaul#define TL_USEIOSPACE 21739957Swpaul 21836270Swpaul#include <pci/if_tlreg.h> 21936270Swpaul 220113506SmdoddMODULE_DEPEND(tl, pci, 1, 1, 1); 221113506SmdoddMODULE_DEPEND(tl, ether, 1, 1, 1); 22259758SpeterMODULE_DEPEND(tl, miibus, 1, 1, 1); 22359758Speter 22451089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 22550462Swpaul#include "miibus_if.h" 22650462Swpaul 22736270Swpaul/* 22836270Swpaul * Various supported device vendors/types and their names. 22936270Swpaul */ 23036270Swpaul 23136270Swpaulstatic struct tl_type tl_devs[] = { 23236270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23336270Swpaul "Texas Instruments ThunderLAN" }, 23436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23536270Swpaul "Compaq Netelligent 10" }, 23636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 23736270Swpaul "Compaq Netelligent 10/100" }, 23836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 23936270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24136270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24336270Swpaul "Compaq NetFlex-3/P Integrated" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24536270Swpaul "Compaq NetFlex-3/P" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 24736270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 24837626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 24937626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25037626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25137626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25237626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25337626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25437626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25537626Swpaul "Olicom OC-2183/2185" }, 25637626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 25737626Swpaul "Olicom OC-2325" }, 25837626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 25937626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26036270Swpaul { 0, 0, NULL } 26136270Swpaul}; 26236270Swpaul 26392739Salfredstatic int tl_probe (device_t); 26492739Salfredstatic int tl_attach (device_t); 26592739Salfredstatic int tl_detach (device_t); 26692739Salfredstatic int tl_intvec_rxeoc (void *, u_int32_t); 26792739Salfredstatic int tl_intvec_txeoc (void *, u_int32_t); 26892739Salfredstatic int tl_intvec_txeof (void *, u_int32_t); 26992739Salfredstatic int tl_intvec_rxeof (void *, u_int32_t); 27092739Salfredstatic int tl_intvec_adchk (void *, u_int32_t); 27192739Salfredstatic int tl_intvec_netsts (void *, u_int32_t); 27236270Swpaul 27392739Salfredstatic int tl_newbuf (struct tl_softc *, struct tl_chain_onefrag *); 27492739Salfredstatic void tl_stats_update (void *); 27592739Salfredstatic int tl_encap (struct tl_softc *, struct tl_chain *, 27692739Salfred struct mbuf *); 27736270Swpaul 27892739Salfredstatic void tl_intr (void *); 27992739Salfredstatic void tl_start (struct ifnet *); 28092739Salfredstatic int tl_ioctl (struct ifnet *, u_long, caddr_t); 28192739Salfredstatic void tl_init (void *); 28292739Salfredstatic void tl_stop (struct tl_softc *); 28392739Salfredstatic void tl_watchdog (struct ifnet *); 28492739Salfredstatic void tl_shutdown (device_t); 28592739Salfredstatic int tl_ifmedia_upd (struct ifnet *); 28692739Salfredstatic void tl_ifmedia_sts (struct ifnet *, struct ifmediareq *); 28736270Swpaul 28892739Salfredstatic u_int8_t tl_eeprom_putbyte (struct tl_softc *, int); 28992739Salfredstatic u_int8_t tl_eeprom_getbyte (struct tl_softc *, int, u_int8_t *); 29092739Salfredstatic int tl_read_eeprom (struct tl_softc *, caddr_t, int, int); 29136270Swpaul 29292739Salfredstatic void tl_mii_sync (struct tl_softc *); 29392739Salfredstatic void tl_mii_send (struct tl_softc *, u_int32_t, int); 29492739Salfredstatic int tl_mii_readreg (struct tl_softc *, struct tl_mii_frame *); 29592739Salfredstatic int tl_mii_writereg (struct tl_softc *, struct tl_mii_frame *); 29692739Salfredstatic int tl_miibus_readreg (device_t, int, int); 29792739Salfredstatic int tl_miibus_writereg (device_t, int, int, int); 29892739Salfredstatic void tl_miibus_statchg (device_t); 29936270Swpaul 30092739Salfredstatic void tl_setmode (struct tl_softc *, int); 301122625Sobrienstatic u_int32_t tl_mchash (caddr_t); 30292739Salfredstatic void tl_setmulti (struct tl_softc *); 30392739Salfredstatic void tl_setfilt (struct tl_softc *, caddr_t, int); 30492739Salfredstatic void tl_softreset (struct tl_softc *, int); 30592739Salfredstatic void tl_hardreset (device_t); 30692739Salfredstatic int tl_list_rx_init (struct tl_softc *); 30792739Salfredstatic int tl_list_tx_init (struct tl_softc *); 30836270Swpaul 30992739Salfredstatic u_int8_t tl_dio_read8 (struct tl_softc *, int); 31092739Salfredstatic u_int16_t tl_dio_read16 (struct tl_softc *, int); 31192739Salfredstatic u_int32_t tl_dio_read32 (struct tl_softc *, int); 31292739Salfredstatic void tl_dio_write8 (struct tl_softc *, int, int); 31392739Salfredstatic void tl_dio_write16 (struct tl_softc *, int, int); 31492739Salfredstatic void tl_dio_write32 (struct tl_softc *, int, int); 31592739Salfredstatic void tl_dio_setbit (struct tl_softc *, int, int); 31692739Salfredstatic void tl_dio_clrbit (struct tl_softc *, int, int); 31792739Salfredstatic void tl_dio_setbit16 (struct tl_softc *, int, int); 31892739Salfredstatic void tl_dio_clrbit16 (struct tl_softc *, int, int); 31939583Swpaul 32049010Swpaul#ifdef TL_USEIOSPACE 32149010Swpaul#define TL_RES SYS_RES_IOPORT 32249010Swpaul#define TL_RID TL_PCI_LOIO 32349010Swpaul#else 32449010Swpaul#define TL_RES SYS_RES_MEMORY 32549010Swpaul#define TL_RID TL_PCI_LOMEM 32649010Swpaul#endif 32749010Swpaul 32848992Swpaulstatic device_method_t tl_methods[] = { 32948992Swpaul /* Device interface */ 33048992Swpaul DEVMETHOD(device_probe, tl_probe), 33148992Swpaul DEVMETHOD(device_attach, tl_attach), 33248992Swpaul DEVMETHOD(device_detach, tl_detach), 33348992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33450462Swpaul 33550462Swpaul /* bus interface */ 33650462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 33750462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 33850462Swpaul 33950462Swpaul /* MII interface */ 34050462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34150462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34250462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34350462Swpaul 34448992Swpaul { 0, 0 } 34548992Swpaul}; 34648992Swpaul 34748992Swpaulstatic driver_t tl_driver = { 34851455Swpaul "tl", 34948992Swpaul tl_methods, 35048992Swpaul sizeof(struct tl_softc) 35148992Swpaul}; 35248992Swpaul 35348992Swpaulstatic devclass_t tl_devclass; 35448992Swpaul 355113506SmdoddDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 35651473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 35748992Swpaul 35839583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 35941656Swpaul struct tl_softc *sc; 36041656Swpaul int reg; 36139583Swpaul{ 36239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36339583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36439583Swpaul} 36539583Swpaul 36639583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 36741656Swpaul struct tl_softc *sc; 36841656Swpaul int reg; 36939583Swpaul{ 37039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37139583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37239583Swpaul} 37339583Swpaul 37439583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 37541656Swpaul struct tl_softc *sc; 37641656Swpaul int reg; 37739583Swpaul{ 37839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37939583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38039583Swpaul} 38139583Swpaul 38239583Swpaulstatic void tl_dio_write8(sc, reg, val) 38341656Swpaul struct tl_softc *sc; 38441656Swpaul int reg; 38541656Swpaul int val; 38639583Swpaul{ 38739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38839583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 38939583Swpaul return; 39039583Swpaul} 39139583Swpaul 39239583Swpaulstatic void tl_dio_write16(sc, reg, val) 39341656Swpaul struct tl_softc *sc; 39441656Swpaul int reg; 39541656Swpaul int val; 39639583Swpaul{ 39739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39839583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 39939583Swpaul return; 40039583Swpaul} 40139583Swpaul 40239583Swpaulstatic void tl_dio_write32(sc, reg, val) 40341656Swpaul struct tl_softc *sc; 40441656Swpaul int reg; 40541656Swpaul int val; 40639583Swpaul{ 40739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40839583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 40939583Swpaul return; 41039583Swpaul} 41139583Swpaul 412102336Salfredstatic void 413102336Salfredtl_dio_setbit(sc, reg, bit) 41441656Swpaul struct tl_softc *sc; 41541656Swpaul int reg; 41641656Swpaul int bit; 41739583Swpaul{ 41839583Swpaul u_int8_t f; 41939583Swpaul 42039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42139583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42239583Swpaul f |= bit; 42339583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 42439583Swpaul 42539583Swpaul return; 42639583Swpaul} 42739583Swpaul 428102336Salfredstatic void 429102336Salfredtl_dio_clrbit(sc, reg, bit) 43041656Swpaul struct tl_softc *sc; 43141656Swpaul int reg; 43241656Swpaul int bit; 43339583Swpaul{ 43439583Swpaul u_int8_t f; 43539583Swpaul 43639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 43739583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 43839583Swpaul f &= ~bit; 43939583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44039583Swpaul 44139583Swpaul return; 44239583Swpaul} 44339583Swpaul 44439583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44541656Swpaul struct tl_softc *sc; 44641656Swpaul int reg; 44741656Swpaul int bit; 44839583Swpaul{ 44939583Swpaul u_int16_t f; 45039583Swpaul 45139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45239583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45339583Swpaul f |= bit; 45439583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45539583Swpaul 45639583Swpaul return; 45739583Swpaul} 45839583Swpaul 45939583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46041656Swpaul struct tl_softc *sc; 46141656Swpaul int reg; 46241656Swpaul int bit; 46339583Swpaul{ 46439583Swpaul u_int16_t f; 46539583Swpaul 46639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 46739583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 46839583Swpaul f &= ~bit; 46939583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47039583Swpaul 47139583Swpaul return; 47239583Swpaul} 47339583Swpaul 47436270Swpaul/* 47536270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 47636270Swpaul */ 47739583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 47839583Swpaul struct tl_softc *sc; 47941656Swpaul int byte; 48036270Swpaul{ 48136270Swpaul register int i, ack = 0; 48236270Swpaul 48336270Swpaul /* 48436270Swpaul * Make sure we're in TX mode. 48536270Swpaul */ 48639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 48736270Swpaul 48836270Swpaul /* 48936270Swpaul * Feed in each bit and stobe the clock. 49036270Swpaul */ 49136270Swpaul for (i = 0x80; i; i >>= 1) { 49236270Swpaul if (byte & i) { 49339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49436270Swpaul } else { 49539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 49636270Swpaul } 49739583Swpaul DELAY(1); 49839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 49939583Swpaul DELAY(1); 50039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50136270Swpaul } 50236270Swpaul 50336270Swpaul /* 50436270Swpaul * Turn off TX mode. 50536270Swpaul */ 50639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 50736270Swpaul 50836270Swpaul /* 50936270Swpaul * Check for ack. 51036270Swpaul */ 51139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51239583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51436270Swpaul 51536270Swpaul return(ack); 51636270Swpaul} 51736270Swpaul 51836270Swpaul/* 51936270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52036270Swpaul */ 52139583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52239583Swpaul struct tl_softc *sc; 52341656Swpaul int addr; 52436270Swpaul u_int8_t *dest; 52536270Swpaul{ 52636270Swpaul register int i; 52736270Swpaul u_int8_t byte = 0; 528105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 52936270Swpaul 53039583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53139583Swpaul 53236270Swpaul EEPROM_START; 53339583Swpaul 53436270Swpaul /* 53536270Swpaul * Send write control code to EEPROM. 53636270Swpaul */ 53739583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 538105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 539105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54036270Swpaul return(1); 54139583Swpaul } 54236270Swpaul 54336270Swpaul /* 54436270Swpaul * Send address of byte we want to read. 54536270Swpaul */ 54639583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 547105599Sbrooks if_printf(ifp, "failed to send address, status: %x\n", 548105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54936270Swpaul return(1); 55039583Swpaul } 55136270Swpaul 55236270Swpaul EEPROM_STOP; 55336270Swpaul EEPROM_START; 55436270Swpaul /* 55536270Swpaul * Send read control code to EEPROM. 55636270Swpaul */ 55739583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 558105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 559105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 56036270Swpaul return(1); 56139583Swpaul } 56236270Swpaul 56336270Swpaul /* 56436270Swpaul * Start reading bits from EEPROM. 56536270Swpaul */ 56639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56736270Swpaul for (i = 0x80; i; i >>= 1) { 56839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 56939583Swpaul DELAY(1); 57039583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57136270Swpaul byte |= i; 57239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57336501Swpaul DELAY(1); 57436270Swpaul } 57536270Swpaul 57636270Swpaul EEPROM_STOP; 57736270Swpaul 57836270Swpaul /* 57936270Swpaul * No ACK generated for read, so just return byte. 58036270Swpaul */ 58136270Swpaul 58236270Swpaul *dest = byte; 58336270Swpaul 58436270Swpaul return(0); 58536270Swpaul} 58636270Swpaul 58739583Swpaul/* 58839583Swpaul * Read a sequence of bytes from the EEPROM. 58939583Swpaul */ 590102336Salfredstatic int 591102336Salfredtl_read_eeprom(sc, dest, off, cnt) 59239583Swpaul struct tl_softc *sc; 59339583Swpaul caddr_t dest; 59439583Swpaul int off; 59539583Swpaul int cnt; 59636270Swpaul{ 59739583Swpaul int err = 0, i; 59839583Swpaul u_int8_t byte = 0; 59939583Swpaul 60039583Swpaul for (i = 0; i < cnt; i++) { 60139583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60239583Swpaul if (err) 60339583Swpaul break; 60439583Swpaul *(dest + i) = byte; 60539583Swpaul } 60639583Swpaul 60739583Swpaul return(err ? 1 : 0); 60839583Swpaul} 60939583Swpaul 610102336Salfredstatic void 611102336Salfredtl_mii_sync(sc) 61239583Swpaul struct tl_softc *sc; 61339583Swpaul{ 61436270Swpaul register int i; 61536270Swpaul 61639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 61736270Swpaul 61836270Swpaul for (i = 0; i < 32; i++) { 61939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62136270Swpaul } 62236270Swpaul 62336270Swpaul return; 62436270Swpaul} 62536270Swpaul 626102336Salfredstatic void 627102336Salfredtl_mii_send(sc, bits, cnt) 62839583Swpaul struct tl_softc *sc; 62936270Swpaul u_int32_t bits; 63036270Swpaul int cnt; 63136270Swpaul{ 63236270Swpaul int i; 63336270Swpaul 63436270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63636270Swpaul if (bits & i) { 63739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 63836270Swpaul } else { 63939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64036270Swpaul } 64139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64236270Swpaul } 64336270Swpaul} 64436270Swpaul 645102336Salfredstatic int 646102336Salfredtl_mii_readreg(sc, frame) 64739583Swpaul struct tl_softc *sc; 64836270Swpaul struct tl_mii_frame *frame; 64936270Swpaul 65036270Swpaul{ 65167087Swpaul int i, ack; 65236270Swpaul int minten = 0; 65336270Swpaul 65467087Swpaul TL_LOCK(sc); 65536270Swpaul 65639583Swpaul tl_mii_sync(sc); 65736270Swpaul 65836270Swpaul /* 65936270Swpaul * Set up frame for RX. 66036270Swpaul */ 66136270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66236270Swpaul frame->mii_opcode = TL_MII_READOP; 66336270Swpaul frame->mii_turnaround = 0; 66436270Swpaul frame->mii_data = 0; 66536270Swpaul 66636270Swpaul /* 66736270Swpaul * Turn off MII interrupt by forcing MINTEN low. 66836270Swpaul */ 66939583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 67036270Swpaul if (minten) { 67139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67236270Swpaul } 67336270Swpaul 67436270Swpaul /* 67536270Swpaul * Turn on data xmit. 67636270Swpaul */ 67739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 67836270Swpaul 67936270Swpaul /* 68036270Swpaul * Send command/address info. 68136270Swpaul */ 68239583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68339583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68439583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68539583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 68636270Swpaul 68736270Swpaul /* 68836270Swpaul * Turn off xmit. 68936270Swpaul */ 69039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69136270Swpaul 69236270Swpaul /* Idle bit */ 69339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69536270Swpaul 69636270Swpaul /* Check for ack */ 69739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69839583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 69936270Swpaul 70036270Swpaul /* Complete the cycle */ 70139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70236270Swpaul 70336270Swpaul /* 70436270Swpaul * Now try reading data bits. If the ack failed, we still 70536270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 70636270Swpaul */ 70736270Swpaul if (ack) { 70836270Swpaul for(i = 0; i < 16; i++) { 70939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71136270Swpaul } 71236270Swpaul goto fail; 71336270Swpaul } 71436270Swpaul 71536270Swpaul for (i = 0x8000; i; i >>= 1) { 71639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71736270Swpaul if (!ack) { 71839583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 71936270Swpaul frame->mii_data |= i; 72036270Swpaul } 72139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72236270Swpaul } 72336270Swpaul 72436270Swpaulfail: 72536270Swpaul 72639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72836270Swpaul 72936270Swpaul /* Reenable interrupts */ 73036270Swpaul if (minten) { 73139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73236270Swpaul } 73336270Swpaul 73467087Swpaul TL_UNLOCK(sc); 73536270Swpaul 73636270Swpaul if (ack) 73736270Swpaul return(1); 73836270Swpaul return(0); 73936270Swpaul} 74036270Swpaul 741102336Salfredstatic int 742102336Salfredtl_mii_writereg(sc, frame) 74339583Swpaul struct tl_softc *sc; 74436270Swpaul struct tl_mii_frame *frame; 74536270Swpaul 74636270Swpaul{ 74736270Swpaul int minten; 74836270Swpaul 74967087Swpaul TL_LOCK(sc); 75067087Swpaul 75139583Swpaul tl_mii_sync(sc); 75236270Swpaul 75336270Swpaul /* 75436270Swpaul * Set up frame for TX. 75536270Swpaul */ 75636270Swpaul 75736270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 75836270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 75936270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 76036270Swpaul 76136270Swpaul /* 76236270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76336270Swpaul */ 76439583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76536270Swpaul if (minten) { 76639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76736270Swpaul } 76836270Swpaul 76936270Swpaul /* 77036270Swpaul * Turn on data output. 77136270Swpaul */ 77239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77336270Swpaul 77439583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77539583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77639583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77739583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 77839583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 77939583Swpaul tl_mii_send(sc, frame->mii_data, 16); 78036270Swpaul 78139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78336270Swpaul 78436270Swpaul /* 78536270Swpaul * Turn off xmit. 78636270Swpaul */ 78739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 78836270Swpaul 78936270Swpaul /* Reenable interrupts */ 79036270Swpaul if (minten) 79139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79236270Swpaul 79367087Swpaul TL_UNLOCK(sc); 79436270Swpaul 79536270Swpaul return(0); 79636270Swpaul} 79736270Swpaul 798102336Salfredstatic int 799102336Salfredtl_miibus_readreg(dev, phy, reg) 80050462Swpaul device_t dev; 80150462Swpaul int phy, reg; 80250462Swpaul{ 80336270Swpaul struct tl_softc *sc; 80436270Swpaul struct tl_mii_frame frame; 80536270Swpaul 80650462Swpaul sc = device_get_softc(dev); 80736270Swpaul bzero((char *)&frame, sizeof(frame)); 80836270Swpaul 80950462Swpaul frame.mii_phyaddr = phy; 81036270Swpaul frame.mii_regaddr = reg; 81139583Swpaul tl_mii_readreg(sc, &frame); 81236270Swpaul 81336270Swpaul return(frame.mii_data); 81436270Swpaul} 81536270Swpaul 816102336Salfredstatic int 817102336Salfredtl_miibus_writereg(dev, phy, reg, data) 81850462Swpaul device_t dev; 81950462Swpaul int phy, reg, data; 82050462Swpaul{ 82136270Swpaul struct tl_softc *sc; 82236270Swpaul struct tl_mii_frame frame; 82336270Swpaul 82450462Swpaul sc = device_get_softc(dev); 82536270Swpaul bzero((char *)&frame, sizeof(frame)); 82636270Swpaul 82750462Swpaul frame.mii_phyaddr = phy; 82836270Swpaul frame.mii_regaddr = reg; 82936270Swpaul frame.mii_data = data; 83036270Swpaul 83139583Swpaul tl_mii_writereg(sc, &frame); 83236270Swpaul 83350462Swpaul return(0); 83436270Swpaul} 83536270Swpaul 836102336Salfredstatic void 837102336Salfredtl_miibus_statchg(dev) 83850462Swpaul device_t dev; 83950462Swpaul{ 84036270Swpaul struct tl_softc *sc; 84150462Swpaul struct mii_data *mii; 84236270Swpaul 84350462Swpaul sc = device_get_softc(dev); 84467087Swpaul TL_LOCK(sc); 84550462Swpaul mii = device_get_softc(sc->tl_miibus); 84636270Swpaul 84750462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 84850462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84936270Swpaul } else { 85050462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85136270Swpaul } 85267087Swpaul TL_UNLOCK(sc); 85336270Swpaul 85436270Swpaul return; 85536270Swpaul} 85636270Swpaul 85736270Swpaul/* 85850462Swpaul * Set modes for bitrate devices. 85936270Swpaul */ 860102336Salfredstatic void 861102336Salfredtl_setmode(sc, media) 86236270Swpaul struct tl_softc *sc; 86336270Swpaul int media; 86436270Swpaul{ 86550462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 86650462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86736270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 86850462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86936270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 87050462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87236270Swpaul } else { 87350462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87439583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87536270Swpaul } 87636270Swpaul } 87736270Swpaul 87836270Swpaul return; 87936270Swpaul} 88036270Swpaul 88136464Swpaul/* 88236464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 88336464Swpaul * table. This hash is simply the address split into 6-bit chunks 88436464Swpaul * XOR'd, e.g. 88536464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 88636464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 88736464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 88836464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 88936464Swpaul */ 890122625Sobrienstatic u_int32_t 891122625Sobrientl_mchash(addr) 892122625Sobrien caddr_t addr; 89336270Swpaul{ 894122625Sobrien int t; 89536270Swpaul 89636464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 89736464Swpaul (addr[2] ^ addr[5]); 89836464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 89936270Swpaul} 90036270Swpaul 90139583Swpaul/* 90239583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 90339583Swpaul * the multicast hash filter. The perfect filter can be programmed 90439583Swpaul * with up to four MAC addresses. The first one is always used to 90539583Swpaul * hold the station address, which leaves us free to use the other 90639583Swpaul * three for multicast addresses. 90739583Swpaul */ 908102336Salfredstatic void 909102336Salfredtl_setfilt(sc, addr, slot) 91039583Swpaul struct tl_softc *sc; 91141656Swpaul caddr_t addr; 91239583Swpaul int slot; 91339583Swpaul{ 91439583Swpaul int i; 91539583Swpaul u_int16_t regaddr; 91639583Swpaul 91739583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 91839583Swpaul 91939583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 92039583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 92139583Swpaul 92239583Swpaul return; 92339583Swpaul} 92439583Swpaul 92539583Swpaul/* 92639583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 92739583Swpaul * linked list. This is fine, except addresses are added from the head 92839583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 92939583Swpaul * group to always be in the perfect filter, but as more groups are added, 93039583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 93139583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 93239583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 93339583Swpaul * and into the hash table. 93439583Swpaul * 93539583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 93639583Swpaul * circular queue, we don't have the ability to just grab the tail of 93739583Swpaul * the list and traverse it backwards. Instead, we have to traverse 93839583Swpaul * the list once to find the tail, then traverse it again backwards to 93939583Swpaul * update the multicast filter. 94039583Swpaul */ 941102336Salfredstatic void 942102336Salfredtl_setmulti(sc) 94336270Swpaul struct tl_softc *sc; 94436270Swpaul{ 94536270Swpaul struct ifnet *ifp; 94636270Swpaul u_int32_t hashes[2] = { 0, 0 }; 94739583Swpaul int h, i; 94836270Swpaul struct ifmultiaddr *ifma; 94939583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 95036270Swpaul ifp = &sc->arpcom.ac_if; 95136270Swpaul 95239583Swpaul /* First, zot all the existing filters. */ 95339583Swpaul for (i = 1; i < 4; i++) 95441656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 95539583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 95639583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 95739583Swpaul 95839583Swpaul /* Now program new ones. */ 95939583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 96036270Swpaul hashes[0] = 0xFFFFFFFF; 96136270Swpaul hashes[1] = 0xFFFFFFFF; 96236270Swpaul } else { 96339583Swpaul i = 1; 96472084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 96536270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 96636270Swpaul continue; 96739583Swpaul /* 96839583Swpaul * Program the first three multicast groups 96939583Swpaul * into the perfect filter. For all others, 97039583Swpaul * use the hash table. 97139583Swpaul */ 97239583Swpaul if (i < 4) { 97339583Swpaul tl_setfilt(sc, 97439583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97539583Swpaul i++; 97639583Swpaul continue; 97739583Swpaul } 97839583Swpaul 979122625Sobrien h = tl_mchash( 98036270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 98136270Swpaul if (h < 32) 98236270Swpaul hashes[0] |= (1 << h); 98336270Swpaul else 98436317Swpaul hashes[1] |= (1 << (h - 32)); 98536270Swpaul } 98636270Swpaul } 98736270Swpaul 98839583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 98939583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 99036270Swpaul 99136270Swpaul return; 99236270Swpaul} 99336270Swpaul 99439583Swpaul/* 99539583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 99639583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99739583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 99839583Swpaul * experience this isn't necessary. 99939583Swpaul */ 1000102336Salfredstatic void 1001102336Salfredtl_hardreset(dev) 100250468Swpaul device_t dev; 100350468Swpaul{ 100439583Swpaul struct tl_softc *sc; 100539583Swpaul int i; 100650468Swpaul u_int16_t flags; 100739583Swpaul 100850468Swpaul sc = device_get_softc(dev); 100939583Swpaul 101050468Swpaul tl_mii_sync(sc); 101139583Swpaul 101250468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 101339583Swpaul 101450468Swpaul for (i = 0; i < MII_NPHY; i++) 101550468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 101639583Swpaul 101750468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 101839583Swpaul DELAY(50000); 101950468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 102039583Swpaul tl_mii_sync(sc); 102150468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 102239583Swpaul 102350468Swpaul DELAY(50000); 102439583Swpaul return; 102539583Swpaul} 102639583Swpaul 1027102336Salfredstatic void 1028102336Salfredtl_softreset(sc, internal) 102939583Swpaul struct tl_softc *sc; 103036270Swpaul int internal; 103136270Swpaul{ 103239583Swpaul u_int32_t cmd, dummy, i; 103336270Swpaul 103436270Swpaul /* Assert the adapter reset bit. */ 103539583Swpaul CMD_SET(sc, TL_CMD_ADRST); 103650468Swpaul 103736270Swpaul /* Turn off interrupts */ 103839583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 103936270Swpaul 104036270Swpaul /* First, clear the stats registers. */ 104139583Swpaul for (i = 0; i < 5; i++) 104239583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 104336270Swpaul 104436270Swpaul /* Clear Areg and Hash registers */ 104539583Swpaul for (i = 0; i < 8; i++) 104639583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104736270Swpaul 104836270Swpaul /* 104936270Swpaul * Set up Netconfig register. Enable one channel and 105036270Swpaul * one fragment mode. 105136270Swpaul */ 105239583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 105345155Swpaul if (internal && !sc->tl_bitrate) { 105439583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105536270Swpaul } else { 105639583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105736270Swpaul } 105836270Swpaul 105945155Swpaul /* Handle cards with bitrate devices. */ 106045155Swpaul if (sc->tl_bitrate) 106145155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 106245155Swpaul 106336270Swpaul /* 106436270Swpaul * Load adapter irq pacing timer and tx threshold. 106536270Swpaul * We make the transmit threshold 1 initially but we may 106636270Swpaul * change that later. 106736270Swpaul */ 106839583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 106936270Swpaul cmd |= TL_CMD_NES; 107036270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 107139583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 107239583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 107336270Swpaul 107436270Swpaul /* Unreset the MII */ 107539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 107636270Swpaul 107736270Swpaul /* Take the adapter out of reset */ 107839583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 107936270Swpaul 108036270Swpaul /* Wait for things to settle down a little. */ 108136270Swpaul DELAY(500); 108236270Swpaul 108336270Swpaul return; 108436270Swpaul} 108536270Swpaul 108636270Swpaul/* 108736270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 108839583Swpaul * against our list and return its name if we find a match. 108936270Swpaul */ 1090102336Salfredstatic int 1091102336Salfredtl_probe(dev) 109248992Swpaul device_t dev; 109336270Swpaul{ 109436270Swpaul struct tl_type *t; 109536270Swpaul 109636270Swpaul t = tl_devs; 109736270Swpaul 109836270Swpaul while(t->tl_name != NULL) { 109948992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 110048992Swpaul (pci_get_device(dev) == t->tl_did)) { 110148992Swpaul device_set_desc(dev, t->tl_name); 110248992Swpaul return(0); 110348992Swpaul } 110436270Swpaul t++; 110536270Swpaul } 110636270Swpaul 110748992Swpaul return(ENXIO); 110836270Swpaul} 110936270Swpaul 1110102336Salfredstatic int 1111102336Salfredtl_attach(dev) 111248992Swpaul device_t dev; 111336270Swpaul{ 111467087Swpaul int i; 111539583Swpaul u_int16_t did, vid; 111639583Swpaul struct tl_type *t; 111739583Swpaul struct ifnet *ifp; 111839583Swpaul struct tl_softc *sc; 111948992Swpaul int unit, error = 0, rid; 112036270Swpaul 112148992Swpaul vid = pci_get_vendor(dev); 112248992Swpaul did = pci_get_device(dev); 112348992Swpaul sc = device_get_softc(dev); 112448992Swpaul unit = device_get_unit(dev); 112539583Swpaul 112639583Swpaul t = tl_devs; 112739583Swpaul while(t->tl_name != NULL) { 112839583Swpaul if (vid == t->tl_vid && did == t->tl_did) 112936270Swpaul break; 113039583Swpaul t++; 113139583Swpaul } 113236270Swpaul 113339583Swpaul if (t->tl_name == NULL) { 1134105599Sbrooks device_printf(dev, "unknown device!?\n"); 1135112878Sjhb return (ENXIO); 113636270Swpaul } 113736270Swpaul 113893818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 113993818Sjhb MTX_DEF | MTX_RECURSE); 114069583Swpaul 114136270Swpaul /* 114236270Swpaul * Map control/status registers. 114336270Swpaul */ 114472813Swpaul pci_enable_busmaster(dev); 114536270Swpaul 114639583Swpaul#ifdef TL_USEIOSPACE 114739583Swpaul 114848992Swpaul rid = TL_PCI_LOIO; 114948992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 115048992Swpaul 0, ~0, 1, RF_ACTIVE); 115148992Swpaul 115248992Swpaul /* 115348992Swpaul * Some cards have the I/O and memory mapped address registers 115448992Swpaul * reversed. Try both combinations before giving up. 115548992Swpaul */ 115648992Swpaul if (sc->tl_res == NULL) { 115748992Swpaul rid = TL_PCI_LOMEM; 115848992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 115948992Swpaul 0, ~0, 1, RF_ACTIVE); 116045155Swpaul } 116139583Swpaul#else 116248992Swpaul rid = TL_PCI_LOMEM; 116348992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 116448992Swpaul 0, ~0, 1, RF_ACTIVE); 116548992Swpaul if (sc->tl_res == NULL) { 116648992Swpaul rid = TL_PCI_LOIO; 116748992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 116848992Swpaul 0, ~0, 1, RF_ACTIVE); 116936270Swpaul } 117039583Swpaul#endif 117136270Swpaul 117248992Swpaul if (sc->tl_res == NULL) { 1173105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 117448992Swpaul error = ENXIO; 117548992Swpaul goto fail; 117648992Swpaul } 117748992Swpaul 117848992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 117948992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 118048992Swpaul 118139583Swpaul#ifdef notdef 118239583Swpaul /* 118339583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 118439583Swpaul * timer all the way up to its maximum value. I'm not sure 118539583Swpaul * if this is really necessary, but what the manual wants, 118639583Swpaul * the manual gets. 118739583Swpaul */ 118848992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 118939583Swpaul command |= 0x0000FF00; 119048992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 119139583Swpaul#endif 119236270Swpaul 119336270Swpaul /* Allocate interrupt */ 119448992Swpaul rid = 0; 119548992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 119648992Swpaul RF_SHAREABLE | RF_ACTIVE); 119748992Swpaul 119848992Swpaul if (sc->tl_irq == NULL) { 1199105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 120048992Swpaul error = ENXIO; 120136270Swpaul goto fail; 120236270Swpaul } 120336270Swpaul 120436270Swpaul /* 120551439Swpaul * Now allocate memory for the TX and RX lists. 120636270Swpaul */ 120751439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 120851657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 120939583Swpaul 121051439Swpaul if (sc->tl_ldata == NULL) { 1211105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 121248992Swpaul error = ENXIO; 121336270Swpaul goto fail; 121436270Swpaul } 121536270Swpaul 121639583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 121739583Swpaul 121839583Swpaul sc->tl_dinfo = t; 121943235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 122039583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 122139583Swpaul if (t->tl_vid == OLICOM_VENDORID) 122239583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 122339583Swpaul 122439583Swpaul /* Reset the adapter. */ 122539583Swpaul tl_softreset(sc, 1); 122650468Swpaul tl_hardreset(dev); 122739583Swpaul tl_softreset(sc, 1); 122839583Swpaul 122938030Swpaul /* 123039583Swpaul * Get station address from the EEPROM. 123139583Swpaul */ 123239583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 123339583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 1234105599Sbrooks device_printf(dev, "failed to read station address\n"); 123548992Swpaul error = ENXIO; 123639583Swpaul goto fail; 123739583Swpaul } 123839583Swpaul 123939583Swpaul /* 124039583Swpaul * XXX Olicom, in its desire to be different from the 124139583Swpaul * rest of the world, has done strange things with the 124239583Swpaul * encoding of the station address in the EEPROM. First 124339583Swpaul * of all, they store the address at offset 0xF8 rather 124439583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 124539583Swpaul * Second, they store the address in three 16-bit words in 124639583Swpaul * network byte order, as opposed to storing it sequentially 124739583Swpaul * like all the other ThunderLAN cards. In order to get 124839583Swpaul * the station address in a form that matches what the Olicom 124939583Swpaul * diagnostic utility specifies, we have to byte-swap each 125039583Swpaul * word. To make things even more confusing, neither 00:00:28 125139583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 125239583Swpaul */ 125339583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 125439583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 125539583Swpaul u_int16_t *p; 125639583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 125739583Swpaul *p = ntohs(*p); 125839583Swpaul } 125939583Swpaul } 126039583Swpaul 126139583Swpaul /* 126236270Swpaul * A ThunderLAN chip was detected. Inform the world. 126336270Swpaul */ 1264105599Sbrooks device_printf(dev, "Ethernet address: %6D\n", 126539583Swpaul sc->arpcom.ac_enaddr, ":"); 126636270Swpaul 126739583Swpaul ifp = &sc->arpcom.ac_if; 126839583Swpaul ifp->if_softc = sc; 1269121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 127039583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 127139583Swpaul ifp->if_ioctl = tl_ioctl; 127239583Swpaul ifp->if_output = ether_output; 127339583Swpaul ifp->if_start = tl_start; 127439583Swpaul ifp->if_watchdog = tl_watchdog; 127539583Swpaul ifp->if_init = tl_init; 127639583Swpaul ifp->if_mtu = ETHERMTU; 127751439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 127839583Swpaul callout_handle_init(&sc->tl_stat_ch); 127939583Swpaul 128039583Swpaul /* Reset the adapter again. */ 128139583Swpaul tl_softreset(sc, 1); 128250468Swpaul tl_hardreset(dev); 128339583Swpaul tl_softreset(sc, 1); 128439583Swpaul 128536270Swpaul /* 128650462Swpaul * Do MII setup. If no PHYs are found, then this is a 128750462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 128850462Swpaul * and AUI/BNC. 128936270Swpaul */ 129050462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 129150462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 129245155Swpaul struct ifmedia *ifm; 129345155Swpaul sc->tl_bitrate = 1; 129445155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 129545155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 129645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 129745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 129845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 129945166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 130045155Swpaul /* Reset again, this time setting bitrate mode. */ 130145155Swpaul tl_softreset(sc, 1); 130245155Swpaul ifm = &sc->ifmedia; 130345155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 130445155Swpaul tl_ifmedia_upd(ifp); 130536270Swpaul } 130636270Swpaul 130739583Swpaul /* 130863090Sarchie * Call MI attach routine. 130939583Swpaul */ 1310106936Ssam ether_ifattach(ifp, sc->arpcom.ac_enaddr); 131138030Swpaul 1312113609Snjl /* Hook interrupt last to avoid having to lock softc */ 1313112872Snjl error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 1314112872Snjl tl_intr, sc, &sc->tl_intrhand); 1315112872Snjl 1316112872Snjl if (error) { 1317112872Snjl device_printf(dev, "couldn't set up irq\n"); 1318113609Snjl ether_ifdetach(ifp); 1319112872Snjl goto fail; 1320112872Snjl } 1321112872Snjl 132236270Swpaulfail: 1323112872Snjl if (error) 1324112872Snjl tl_detach(dev); 1325112872Snjl 132648992Swpaul return(error); 132736270Swpaul} 132836270Swpaul 1329113609Snjl/* 1330113609Snjl * Shutdown hardware and free up resources. This can be called any 1331113609Snjl * time after the mutex has been initialized. It is called in both 1332113609Snjl * the error case in attach and the normal detach case so it needs 1333113609Snjl * to be careful about only freeing resources that have actually been 1334113609Snjl * allocated. 1335113609Snjl */ 1336102336Salfredstatic int 1337102336Salfredtl_detach(dev) 133848992Swpaul device_t dev; 133948992Swpaul{ 134048992Swpaul struct tl_softc *sc; 134148992Swpaul struct ifnet *ifp; 134248992Swpaul 134348992Swpaul sc = device_get_softc(dev); 1344112880Sjhb KASSERT(mtx_initialized(&sc->tl_mtx), ("tl mutex not initialized")); 134567087Swpaul TL_LOCK(sc); 134648992Swpaul ifp = &sc->arpcom.ac_if; 134748992Swpaul 1348113609Snjl /* These should only be active if attach succeeded */ 1349113812Simp if (device_is_attached(dev)) { 1350113609Snjl tl_stop(sc); 1351112872Snjl ether_ifdetach(ifp); 1352113609Snjl } 1353113609Snjl if (sc->tl_miibus) 1354112872Snjl device_delete_child(dev, sc->tl_miibus); 1355113609Snjl bus_generic_detach(dev); 135648992Swpaul 1357112872Snjl if (sc->tl_ldata) 1358112872Snjl contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 135950462Swpaul if (sc->tl_bitrate) 136050462Swpaul ifmedia_removeall(&sc->ifmedia); 136148992Swpaul 1362112872Snjl if (sc->tl_intrhand) 1363112872Snjl bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 1364112872Snjl if (sc->tl_irq) 1365112872Snjl bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 1366112872Snjl if (sc->tl_res) 1367112872Snjl bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 136848992Swpaul 136967087Swpaul TL_UNLOCK(sc); 137067087Swpaul mtx_destroy(&sc->tl_mtx); 137148992Swpaul 137248992Swpaul return(0); 137348992Swpaul} 137448992Swpaul 137536270Swpaul/* 137636270Swpaul * Initialize the transmit lists. 137736270Swpaul */ 1378102336Salfredstatic int 1379102336Salfredtl_list_tx_init(sc) 138036270Swpaul struct tl_softc *sc; 138136270Swpaul{ 138236270Swpaul struct tl_chain_data *cd; 138336270Swpaul struct tl_list_data *ld; 138436270Swpaul int i; 138536270Swpaul 138636270Swpaul cd = &sc->tl_cdata; 138736270Swpaul ld = sc->tl_ldata; 138836270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 138936270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 139036270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 139136270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 139236270Swpaul else 139336270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 139436270Swpaul } 139536270Swpaul 139636270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 139736270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 139836270Swpaul sc->tl_txeoc = 1; 139936270Swpaul 140036270Swpaul return(0); 140136270Swpaul} 140236270Swpaul 140336270Swpaul/* 140436270Swpaul * Initialize the RX lists and allocate mbufs for them. 140536270Swpaul */ 1406102336Salfredstatic int 1407102336Salfredtl_list_rx_init(sc) 140836270Swpaul struct tl_softc *sc; 140936270Swpaul{ 141036270Swpaul struct tl_chain_data *cd; 141136270Swpaul struct tl_list_data *ld; 141236270Swpaul int i; 141336270Swpaul 141436270Swpaul cd = &sc->tl_cdata; 141536270Swpaul ld = sc->tl_ldata; 141636270Swpaul 141740795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 141836270Swpaul cd->tl_rx_chain[i].tl_ptr = 141937626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 142039583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 142139583Swpaul return(ENOBUFS); 142240795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 142336270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 142436270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 142536270Swpaul } else { 142636270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 142736270Swpaul ld->tl_rx_list[i].tlist_fptr = 142836270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 142936270Swpaul } 143036270Swpaul } 143136270Swpaul 143236270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 143336270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 143436270Swpaul 143536270Swpaul return(0); 143636270Swpaul} 143736270Swpaul 1438102336Salfredstatic int 1439102336Salfredtl_newbuf(sc, c) 144036270Swpaul struct tl_softc *sc; 144137626Swpaul struct tl_chain_onefrag *c; 144236270Swpaul{ 144336270Swpaul struct mbuf *m_new = NULL; 144436270Swpaul 1445111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 144687846Sluigi if (m_new == NULL) 144736270Swpaul return(ENOBUFS); 144836270Swpaul 1449111119Simp MCLGET(m_new, M_DONTWAIT); 145036270Swpaul if (!(m_new->m_flags & M_EXT)) { 145136270Swpaul m_freem(m_new); 145236270Swpaul return(ENOBUFS); 145336270Swpaul } 145436270Swpaul 145545155Swpaul#ifdef __alpha__ 145645155Swpaul m_new->m_data += 2; 145745155Swpaul#endif 145845155Swpaul 145936270Swpaul c->tl_mbuf = m_new; 146036270Swpaul c->tl_next = NULL; 146136270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 146236270Swpaul c->tl_ptr->tlist_fptr = 0; 146337626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 146437626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 146556060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 146636270Swpaul 146736270Swpaul return(0); 146836270Swpaul} 146936270Swpaul/* 147036270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 147136270Swpaul * tells us that a full ethernet frame has been captured and we need 147236270Swpaul * to handle it. 147336270Swpaul * 147436270Swpaul * Reception is done using 'lists' which consist of a header and a 147536270Swpaul * series of 10 data count/data address pairs that point to buffers. 147636270Swpaul * Initially you're supposed to create a list, populate it with pointers 147736270Swpaul * to buffers, then load the physical address of the list into the 147836270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 147936270Swpaul * frame into the buffers for you. 148036270Swpaul * 148136270Swpaul * To make things as fast as possible, we have the chip DMA directly 148236270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 148336270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 148436270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 148536270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 148636270Swpaul * the chip, it will always think it has an endless receive channel. 148736270Swpaul * 148836270Swpaul * If we happen to fall behind and the chip manages to fill up all of 148936270Swpaul * the buffers, it will generate an end of channel interrupt and wait 149036270Swpaul * for us to empty the chain and restart the receiver. 149136270Swpaul */ 1492102336Salfredstatic int 1493102336Salfredtl_intvec_rxeof(xsc, type) 149436270Swpaul void *xsc; 149536270Swpaul u_int32_t type; 149636270Swpaul{ 149736270Swpaul struct tl_softc *sc; 149836270Swpaul int r = 0, total_len = 0; 149936270Swpaul struct ether_header *eh; 150036270Swpaul struct mbuf *m; 150136270Swpaul struct ifnet *ifp; 150237626Swpaul struct tl_chain_onefrag *cur_rx; 150336270Swpaul 150436270Swpaul sc = xsc; 150536270Swpaul ifp = &sc->arpcom.ac_if; 150636270Swpaul 150756060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 150856060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 150956060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 151056060Swpaul break; 151136270Swpaul r++; 151236270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 151336270Swpaul m = cur_rx->tl_mbuf; 151436270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 151536270Swpaul 151639583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 151739583Swpaul ifp->if_ierrors++; 151839583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 151939583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 152039583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 152139583Swpaul continue; 152239583Swpaul } 152336270Swpaul 152436270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 152536270Swpaul vtophys(cur_rx->tl_ptr); 152636270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 152736270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 152836270Swpaul 152937626Swpaul /* 153037626Swpaul * Note: when the ThunderLAN chip is in 'capture all 153137626Swpaul * frames' mode, it will receive its own transmissions. 153237626Swpaul * We drop don't need to process our own transmissions, 153337626Swpaul * so we drop them here and continue. 153437626Swpaul */ 1535106936Ssam eh = mtod(m, struct ether_header *); 153639583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 153739583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 153837626Swpaul ETHER_ADDR_LEN)) { 153937626Swpaul m_freem(m); 154037626Swpaul continue; 154137626Swpaul } 154237626Swpaul 1543106936Ssam m->m_pkthdr.rcvif = ifp; 1544106936Ssam m->m_pkthdr.len = m->m_len = total_len; 1545106936Ssam 1546106936Ssam (*ifp->if_input)(ifp, m); 154736270Swpaul } 154836270Swpaul 154936270Swpaul return(r); 155036270Swpaul} 155136270Swpaul 155236270Swpaul/* 155336270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 155436270Swpaul * initialized or the adapter reached a list with a forward pointer 155536270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 155636270Swpaul * the card has hit the end of the receive buffer chain and we need to 155736270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 155836270Swpaul */ 1559102336Salfredstatic int 1560102336Salfredtl_intvec_rxeoc(xsc, type) 156136270Swpaul void *xsc; 156236270Swpaul u_int32_t type; 156336270Swpaul{ 156436270Swpaul struct tl_softc *sc; 156536270Swpaul int r; 156656060Swpaul struct tl_chain_data *cd; 156736270Swpaul 156856060Swpaul 156936270Swpaul sc = xsc; 157056060Swpaul cd = &sc->tl_cdata; 157136270Swpaul 157236270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 157336270Swpaul r = tl_intvec_rxeof(xsc, type); 157439583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 157536270Swpaul r = 1; 157656060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 157756060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 157839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 157936270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 158036270Swpaul return(r); 158136270Swpaul} 158236270Swpaul 1583102336Salfredstatic int 1584102336Salfredtl_intvec_txeof(xsc, type) 158536270Swpaul void *xsc; 158636270Swpaul u_int32_t type; 158736270Swpaul{ 158836270Swpaul struct tl_softc *sc; 158936270Swpaul int r = 0; 159036270Swpaul struct tl_chain *cur_tx; 159136270Swpaul 159236270Swpaul sc = xsc; 159336270Swpaul 159436270Swpaul /* 159536270Swpaul * Go through our tx list and free mbufs for those 159636270Swpaul * frames that have been sent. 159736270Swpaul */ 159836270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 159936270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 160036270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 160136270Swpaul break; 160236270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 160336270Swpaul 160436270Swpaul r++; 160536270Swpaul m_freem(cur_tx->tl_mbuf); 160636270Swpaul cur_tx->tl_mbuf = NULL; 160736270Swpaul 160836270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 160936270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 161037626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 161137626Swpaul break; 161236270Swpaul } 161336270Swpaul 161436270Swpaul return(r); 161536270Swpaul} 161636270Swpaul 161736270Swpaul/* 161836270Swpaul * The transmit end of channel interrupt. The adapter triggers this 161936270Swpaul * interrupt to tell us it hit the end of the current transmit list. 162036270Swpaul * 162136270Swpaul * A note about this: it's possible for a condition to arise where 162236270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 162336270Swpaul * You have to avoid this since the chip expects things to go in a 162436270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 162536270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 162636270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 162736270Swpaul * interrupt should be received and acknowledged before any more frames 162836270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 162936270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 163036270Swpaul * it could attempt to issue a transmit command prematurely. 163136270Swpaul * 163236270Swpaul * To guard against this, tl_start() will only issue transmit commands 163336270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 163436270Swpaul * can set this flag once tl_start() has cleared it. 163536270Swpaul */ 1636102336Salfredstatic int 1637102336Salfredtl_intvec_txeoc(xsc, type) 163836270Swpaul void *xsc; 163936270Swpaul u_int32_t type; 164036270Swpaul{ 164136270Swpaul struct tl_softc *sc; 164236270Swpaul struct ifnet *ifp; 164336270Swpaul u_int32_t cmd; 164436270Swpaul 164536270Swpaul sc = xsc; 164636270Swpaul ifp = &sc->arpcom.ac_if; 164736270Swpaul 164836270Swpaul /* Clear the timeout timer. */ 164936270Swpaul ifp->if_timer = 0; 165036270Swpaul 165136270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 165236270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 165336270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 165436270Swpaul sc->tl_txeoc = 1; 165536270Swpaul } else { 165636270Swpaul sc->tl_txeoc = 0; 165736270Swpaul /* First we have to ack the EOC interrupt. */ 165839583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 165936270Swpaul /* Then load the address of the next TX list. */ 166039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 166151439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 166236270Swpaul /* Restart TX channel. */ 166339583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 166436270Swpaul cmd &= ~TL_CMD_RT; 166536270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 166639583Swpaul CMD_PUT(sc, cmd); 166736270Swpaul return(0); 166836270Swpaul } 166936270Swpaul 167036270Swpaul return(1); 167136270Swpaul} 167236270Swpaul 1673102336Salfredstatic int 1674102336Salfredtl_intvec_adchk(xsc, type) 167536270Swpaul void *xsc; 167636270Swpaul u_int32_t type; 167736270Swpaul{ 167836270Swpaul struct tl_softc *sc; 167936270Swpaul 168036270Swpaul sc = xsc; 168136270Swpaul 168239627Swpaul if (type) 1683105599Sbrooks if_printf(&sc->arpcom.ac_if, "adapter check: %x\n", 168441656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 168536270Swpaul 168639583Swpaul tl_softreset(sc, 1); 168737626Swpaul tl_stop(sc); 168836270Swpaul tl_init(sc); 168939583Swpaul CMD_SET(sc, TL_CMD_INTSON); 169036270Swpaul 169136270Swpaul return(0); 169236270Swpaul} 169336270Swpaul 1694102336Salfredstatic int 1695102336Salfredtl_intvec_netsts(xsc, type) 169636270Swpaul void *xsc; 169736270Swpaul u_int32_t type; 169836270Swpaul{ 169936270Swpaul struct tl_softc *sc; 170036270Swpaul u_int16_t netsts; 170136270Swpaul 170236270Swpaul sc = xsc; 170336270Swpaul 170439583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 170539583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 170636270Swpaul 1707105599Sbrooks if_printf(&sc->arpcom.ac_if, "network status: %x\n", netsts); 170836270Swpaul 170936270Swpaul return(1); 171036270Swpaul} 171136270Swpaul 1712102336Salfredstatic void 1713102336Salfredtl_intr(xsc) 171439583Swpaul void *xsc; 171536270Swpaul{ 171636270Swpaul struct tl_softc *sc; 171736270Swpaul struct ifnet *ifp; 171836270Swpaul int r = 0; 171936270Swpaul u_int32_t type = 0; 172036270Swpaul u_int16_t ints = 0; 172136270Swpaul u_int8_t ivec = 0; 172236270Swpaul 172339583Swpaul sc = xsc; 172467087Swpaul TL_LOCK(sc); 172536270Swpaul 172636270Swpaul /* Disable interrupts */ 172739583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 172839583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 172936270Swpaul type = (ints << 16) & 0xFFFF0000; 173036270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 173136270Swpaul ints = (ints & TL_INT_MASK) >> 2; 173236270Swpaul 173336270Swpaul ifp = &sc->arpcom.ac_if; 173436270Swpaul 173536270Swpaul switch(ints) { 173636270Swpaul case (TL_INTR_INVALID): 173739583Swpaul#ifdef DIAGNOSTIC 1738105599Sbrooks if_printf(ifp, "got an invalid interrupt!\n"); 173939583Swpaul#endif 174039583Swpaul /* Re-enable interrupts but don't ack this one. */ 174139583Swpaul CMD_PUT(sc, type); 174239583Swpaul r = 0; 174336270Swpaul break; 174436270Swpaul case (TL_INTR_TXEOF): 174536270Swpaul r = tl_intvec_txeof((void *)sc, type); 174636270Swpaul break; 174736270Swpaul case (TL_INTR_TXEOC): 174836270Swpaul r = tl_intvec_txeoc((void *)sc, type); 174936270Swpaul break; 175036270Swpaul case (TL_INTR_STATOFLOW): 175139583Swpaul tl_stats_update(sc); 175239583Swpaul r = 1; 175336270Swpaul break; 175436270Swpaul case (TL_INTR_RXEOF): 175536270Swpaul r = tl_intvec_rxeof((void *)sc, type); 175636270Swpaul break; 175736270Swpaul case (TL_INTR_DUMMY): 1758105599Sbrooks if_printf(ifp, "got a dummy interrupt\n"); 175939583Swpaul r = 1; 176036270Swpaul break; 176136270Swpaul case (TL_INTR_ADCHK): 176236270Swpaul if (ivec) 176336270Swpaul r = tl_intvec_adchk((void *)sc, type); 176436270Swpaul else 176536270Swpaul r = tl_intvec_netsts((void *)sc, type); 176636270Swpaul break; 176736270Swpaul case (TL_INTR_RXEOC): 176836270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 176936270Swpaul break; 177036270Swpaul default: 1771105599Sbrooks if_printf(ifp, "bogus interrupt type\n"); 177236270Swpaul break; 177336270Swpaul } 177436270Swpaul 177536270Swpaul /* Re-enable interrupts */ 177637626Swpaul if (r) { 177739583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 177837626Swpaul } 177936270Swpaul 178037626Swpaul if (ifp->if_snd.ifq_head != NULL) 178137626Swpaul tl_start(ifp); 178237626Swpaul 178367087Swpaul TL_UNLOCK(sc); 178467087Swpaul 178536270Swpaul return; 178636270Swpaul} 178736270Swpaul 1788102336Salfredstatic void 1789102336Salfredtl_stats_update(xsc) 179036270Swpaul void *xsc; 179136270Swpaul{ 179236270Swpaul struct tl_softc *sc; 179336270Swpaul struct ifnet *ifp; 179436270Swpaul struct tl_stats tl_stats; 179550462Swpaul struct mii_data *mii; 179636270Swpaul u_int32_t *p; 179736270Swpaul 179836270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 179936270Swpaul 180036270Swpaul sc = xsc; 180167087Swpaul TL_LOCK(sc); 180236270Swpaul ifp = &sc->arpcom.ac_if; 180336270Swpaul 180436270Swpaul p = (u_int32_t *)&tl_stats; 180536270Swpaul 180639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 180739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 180839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 180939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181039583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181139583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181236270Swpaul 181336270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 181436270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 181536270Swpaul tl_stats.tl_tx_multi_collision; 181636270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 181736270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 181836270Swpaul tl_rx_overrun(tl_stats); 181936270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 182036270Swpaul 182151439Swpaul if (tl_tx_underrun(tl_stats)) { 182251439Swpaul u_int8_t tx_thresh; 182351439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 182451439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 182551439Swpaul tx_thresh >>= 4; 182651439Swpaul tx_thresh++; 1827105599Sbrooks if_printf(ifp, "tx underrun -- increasing " 1828105599Sbrooks "tx threshold to %d bytes\n", 182951439Swpaul (64 * (tx_thresh * 4))); 183051439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 183151439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 183251439Swpaul } 183351439Swpaul } 183451439Swpaul 183536270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 183636302Swpaul 183750462Swpaul if (!sc->tl_bitrate) { 183850462Swpaul mii = device_get_softc(sc->tl_miibus); 183950462Swpaul mii_tick(mii); 184050462Swpaul } 184150462Swpaul 184267087Swpaul TL_UNLOCK(sc); 184348992Swpaul 184436302Swpaul return; 184536270Swpaul} 184636270Swpaul 184736270Swpaul/* 184836270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 184936270Swpaul * pointers to the fragment pointers. 185036270Swpaul */ 1851102336Salfredstatic int 1852102336Salfredtl_encap(sc, c, m_head) 185336270Swpaul struct tl_softc *sc; 185436270Swpaul struct tl_chain *c; 185536270Swpaul struct mbuf *m_head; 185636270Swpaul{ 185736270Swpaul int frag = 0; 185836270Swpaul struct tl_frag *f = NULL; 185936270Swpaul int total_len; 186036270Swpaul struct mbuf *m; 1861105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 186236270Swpaul 186336270Swpaul /* 186436270Swpaul * Start packing the mbufs in this chain into 186536270Swpaul * the fragment pointers. Stop when we run out 186636270Swpaul * of fragments or hit the end of the mbuf chain. 186736270Swpaul */ 186836270Swpaul m = m_head; 186936270Swpaul total_len = 0; 187036270Swpaul 187136270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 187236270Swpaul if (m->m_len != 0) { 187336270Swpaul if (frag == TL_MAXFRAGS) 187436270Swpaul break; 187536270Swpaul total_len+= m->m_len; 187636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 187736270Swpaul vtophys(mtod(m, vm_offset_t)); 187836270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 187936270Swpaul frag++; 188036270Swpaul } 188136270Swpaul } 188236270Swpaul 188336270Swpaul /* 188436270Swpaul * Handle special cases. 188536270Swpaul * Special case #1: we used up all 10 fragments, but 188636270Swpaul * we have more mbufs left in the chain. Copy the 188736270Swpaul * data into an mbuf cluster. Note that we don't 188836270Swpaul * bother clearing the values in the other fragment 188936270Swpaul * pointers/counters; it wouldn't gain us anything, 189036270Swpaul * and would waste cycles. 189136270Swpaul */ 189236270Swpaul if (m != NULL) { 189336270Swpaul struct mbuf *m_new = NULL; 189436270Swpaul 1895111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 189636270Swpaul if (m_new == NULL) { 1897105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 189836270Swpaul return(1); 189936270Swpaul } 190036270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 1901111119Simp MCLGET(m_new, M_DONTWAIT); 190236270Swpaul if (!(m_new->m_flags & M_EXT)) { 190336270Swpaul m_freem(m_new); 1904105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 190536270Swpaul return(1); 190636270Swpaul } 190736270Swpaul } 190836270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 190936270Swpaul mtod(m_new, caddr_t)); 191036270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 191136270Swpaul m_freem(m_head); 191236270Swpaul m_head = m_new; 191336270Swpaul f = &c->tl_ptr->tl_frag[0]; 191436270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 191536270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 191636270Swpaul frag = 1; 191736270Swpaul } 191836270Swpaul 191936270Swpaul /* 192036270Swpaul * Special case #2: the frame is smaller than the minimum 192136270Swpaul * frame size. We have to pad it to make the chip happy. 192236270Swpaul */ 192336270Swpaul if (total_len < TL_MIN_FRAMELEN) { 192436270Swpaul if (frag == TL_MAXFRAGS) 1925105599Sbrooks if_printf(ifp, 1926105599Sbrooks "all frags filled but frame still to small!\n"); 192736270Swpaul f = &c->tl_ptr->tl_frag[frag]; 192836270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 192936270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 193036270Swpaul total_len += f->tlist_dcnt; 193136270Swpaul frag++; 193236270Swpaul } 193336270Swpaul 193436270Swpaul c->tl_mbuf = m_head; 193536270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 193636270Swpaul c->tl_ptr->tlist_frsize = total_len; 193736270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 193836270Swpaul c->tl_ptr->tlist_fptr = 0; 193936270Swpaul 194036270Swpaul return(0); 194136270Swpaul} 194236270Swpaul 194336270Swpaul/* 194436270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 194536270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 194636270Swpaul * copy of the pointers since the transmit list fragment pointers are 194736270Swpaul * physical addresses. 194836270Swpaul */ 1949102336Salfredstatic void 1950102336Salfredtl_start(ifp) 195136270Swpaul struct ifnet *ifp; 195236270Swpaul{ 195336270Swpaul struct tl_softc *sc; 195436270Swpaul struct mbuf *m_head = NULL; 195536270Swpaul u_int32_t cmd; 195636270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 195736270Swpaul 195836270Swpaul sc = ifp->if_softc; 195967087Swpaul TL_LOCK(sc); 196036270Swpaul 196136270Swpaul /* 196236270Swpaul * Check for an available queue slot. If there are none, 196336270Swpaul * punt. 196436270Swpaul */ 196536270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 196636270Swpaul ifp->if_flags |= IFF_OACTIVE; 196767087Swpaul TL_UNLOCK(sc); 196836270Swpaul return; 196936270Swpaul } 197036270Swpaul 197136270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 197236270Swpaul 197336270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 197436270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 197536270Swpaul if (m_head == NULL) 197636270Swpaul break; 197736270Swpaul 197836270Swpaul /* Pick a chain member off the free list. */ 197936270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 198036270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 198136270Swpaul 198236270Swpaul cur_tx->tl_next = NULL; 198336270Swpaul 198436270Swpaul /* Pack the data into the list. */ 198536270Swpaul tl_encap(sc, cur_tx, m_head); 198636270Swpaul 198736270Swpaul /* Chain it together */ 198836270Swpaul if (prev != NULL) { 198936270Swpaul prev->tl_next = cur_tx; 199036270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 199136270Swpaul } 199236270Swpaul prev = cur_tx; 199336270Swpaul 199436270Swpaul /* 199536270Swpaul * If there's a BPF listener, bounce a copy of this frame 199636270Swpaul * to him. 199736270Swpaul */ 1998106936Ssam BPF_MTAP(ifp, cur_tx->tl_mbuf); 199936270Swpaul } 200036270Swpaul 200136270Swpaul /* 200241526Swpaul * If there are no packets queued, bail. 200341526Swpaul */ 200467087Swpaul if (cur_tx == NULL) { 200567087Swpaul TL_UNLOCK(sc); 200641526Swpaul return; 200767087Swpaul } 200841526Swpaul 200941526Swpaul /* 201036270Swpaul * That's all we can stands, we can't stands no more. 201136270Swpaul * If there are no other transfers pending, then issue the 201236270Swpaul * TX GO command to the adapter to start things moving. 201336270Swpaul * Otherwise, just leave the data in the queue and let 201436270Swpaul * the EOF/EOC interrupt handler send. 201536270Swpaul */ 201636270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 201736270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 201836270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 201939583Swpaul 202036270Swpaul if (sc->tl_txeoc) { 202136270Swpaul sc->tl_txeoc = 0; 202239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 202339583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 202436270Swpaul cmd &= ~TL_CMD_RT; 202536270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 202639583Swpaul CMD_PUT(sc, cmd); 202736270Swpaul } 202836270Swpaul } else { 202936270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 203042146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 203136270Swpaul } 203236270Swpaul 203336270Swpaul /* 203436270Swpaul * Set a timeout in case the chip goes out to lunch. 203536270Swpaul */ 203636270Swpaul ifp->if_timer = 5; 203767087Swpaul TL_UNLOCK(sc); 203836270Swpaul 203936270Swpaul return; 204036270Swpaul} 204136270Swpaul 2042102336Salfredstatic void 2043102336Salfredtl_init(xsc) 204436270Swpaul void *xsc; 204536270Swpaul{ 204636270Swpaul struct tl_softc *sc = xsc; 204736270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 204850462Swpaul struct mii_data *mii; 204936270Swpaul 205067087Swpaul TL_LOCK(sc); 205136270Swpaul 205236270Swpaul ifp = &sc->arpcom.ac_if; 205336270Swpaul 205436270Swpaul /* 205536270Swpaul * Cancel pending I/O. 205636270Swpaul */ 205736270Swpaul tl_stop(sc); 205836270Swpaul 205951439Swpaul /* Initialize TX FIFO threshold */ 206051439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 206151439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 206251439Swpaul 206351439Swpaul /* Set PCI burst size */ 206451439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 206551439Swpaul 206636270Swpaul /* 206736270Swpaul * Set 'capture all frames' bit for promiscuous mode. 206836270Swpaul */ 206939583Swpaul if (ifp->if_flags & IFF_PROMISC) 207039583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 207139583Swpaul else 207239583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 207336270Swpaul 207436270Swpaul /* 207536270Swpaul * Set capture broadcast bit to capture broadcast frames. 207636270Swpaul */ 207739583Swpaul if (ifp->if_flags & IFF_BROADCAST) 207839583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 207939583Swpaul else 208039583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 208136270Swpaul 208250468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 208350468Swpaul 208436270Swpaul /* Init our MAC address */ 208541656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 208636270Swpaul 208739583Swpaul /* Init multicast filter, if needed. */ 208839583Swpaul tl_setmulti(sc); 208939583Swpaul 209036270Swpaul /* Init circular RX list. */ 209139583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2092105599Sbrooks if_printf(ifp, 2093105599Sbrooks "initialization failed: no memory for rx buffers\n"); 209439583Swpaul tl_stop(sc); 209567087Swpaul TL_UNLOCK(sc); 209636270Swpaul return; 209736270Swpaul } 209836270Swpaul 209936270Swpaul /* Init TX pointers. */ 210036270Swpaul tl_list_tx_init(sc); 210136270Swpaul 210239583Swpaul /* Enable PCI interrupts. */ 210339583Swpaul CMD_SET(sc, TL_CMD_INTSON); 210436270Swpaul 210536270Swpaul /* Load the address of the rx list */ 210639583Swpaul CMD_SET(sc, TL_CMD_RT); 210739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 210836270Swpaul 210950462Swpaul if (!sc->tl_bitrate) { 211050462Swpaul if (sc->tl_miibus != NULL) { 211150462Swpaul mii = device_get_softc(sc->tl_miibus); 211250462Swpaul mii_mediachg(mii); 211350462Swpaul } 2114113548Smdodd } else { 2115113548Smdodd tl_ifmedia_upd(ifp); 211650462Swpaul } 211738030Swpaul 211836270Swpaul /* Send the RX go command */ 211950468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 212036270Swpaul 212136270Swpaul ifp->if_flags |= IFF_RUNNING; 212236270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 212336270Swpaul 212436270Swpaul /* Start the stats update counter */ 212536270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 212667087Swpaul TL_UNLOCK(sc); 212736270Swpaul 212836270Swpaul return; 212936270Swpaul} 213036270Swpaul 213136270Swpaul/* 213236270Swpaul * Set media options. 213336270Swpaul */ 2134102336Salfredstatic int 2135102336Salfredtl_ifmedia_upd(ifp) 213636270Swpaul struct ifnet *ifp; 213736270Swpaul{ 213836270Swpaul struct tl_softc *sc; 213950462Swpaul struct mii_data *mii = NULL; 214036270Swpaul 214136270Swpaul sc = ifp->if_softc; 214236270Swpaul 214350462Swpaul if (sc->tl_bitrate) 214450462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 214550462Swpaul else { 214650462Swpaul mii = device_get_softc(sc->tl_miibus); 214750462Swpaul mii_mediachg(mii); 214850462Swpaul } 214936270Swpaul 215036270Swpaul return(0); 215136270Swpaul} 215236270Swpaul 215336270Swpaul/* 215436270Swpaul * Report current media status. 215536270Swpaul */ 2156102336Salfredstatic void 2157102336Salfredtl_ifmedia_sts(ifp, ifmr) 215836270Swpaul struct ifnet *ifp; 215936270Swpaul struct ifmediareq *ifmr; 216036270Swpaul{ 216136270Swpaul struct tl_softc *sc; 216250462Swpaul struct mii_data *mii; 216336270Swpaul 216436270Swpaul sc = ifp->if_softc; 216536270Swpaul 216636270Swpaul ifmr->ifm_active = IFM_ETHER; 216736270Swpaul 216845155Swpaul if (sc->tl_bitrate) { 216945155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 217045155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 217145155Swpaul else 217245155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 217345155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 217445155Swpaul ifmr->ifm_active |= IFM_HDX; 217545155Swpaul else 217645155Swpaul ifmr->ifm_active |= IFM_FDX; 217745155Swpaul return; 217836270Swpaul } else { 217950462Swpaul mii = device_get_softc(sc->tl_miibus); 218050462Swpaul mii_pollstat(mii); 218150462Swpaul ifmr->ifm_active = mii->mii_media_active; 218250462Swpaul ifmr->ifm_status = mii->mii_media_status; 218336270Swpaul } 218436270Swpaul 218536270Swpaul return; 218636270Swpaul} 218736270Swpaul 2188102336Salfredstatic int 2189102336Salfredtl_ioctl(ifp, command, data) 219036270Swpaul struct ifnet *ifp; 219136735Sdfr u_long command; 219236270Swpaul caddr_t data; 219336270Swpaul{ 219436270Swpaul struct tl_softc *sc = ifp->if_softc; 219536270Swpaul struct ifreq *ifr = (struct ifreq *) data; 219636270Swpaul int s, error = 0; 219736270Swpaul 219836270Swpaul s = splimp(); 219936270Swpaul 220036270Swpaul switch(command) { 220136270Swpaul case SIOCSIFFLAGS: 220236270Swpaul if (ifp->if_flags & IFF_UP) { 220350462Swpaul if (ifp->if_flags & IFF_RUNNING && 220450462Swpaul ifp->if_flags & IFF_PROMISC && 220550462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 220650462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 220750462Swpaul tl_setmulti(sc); 220850462Swpaul } else if (ifp->if_flags & IFF_RUNNING && 220950462Swpaul !(ifp->if_flags & IFF_PROMISC) && 221050462Swpaul sc->tl_if_flags & IFF_PROMISC) { 221150462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 221250462Swpaul tl_setmulti(sc); 221350462Swpaul } else 221450462Swpaul tl_init(sc); 221536270Swpaul } else { 221636270Swpaul if (ifp->if_flags & IFF_RUNNING) { 221736270Swpaul tl_stop(sc); 221836270Swpaul } 221936270Swpaul } 222050462Swpaul sc->tl_if_flags = ifp->if_flags; 222136270Swpaul error = 0; 222236270Swpaul break; 222336270Swpaul case SIOCADDMULTI: 222436270Swpaul case SIOCDELMULTI: 222536270Swpaul tl_setmulti(sc); 222636270Swpaul error = 0; 222736270Swpaul break; 222836270Swpaul case SIOCSIFMEDIA: 222936270Swpaul case SIOCGIFMEDIA: 223050462Swpaul if (sc->tl_bitrate) 223150462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 223250462Swpaul else { 223350462Swpaul struct mii_data *mii; 223450462Swpaul mii = device_get_softc(sc->tl_miibus); 223550462Swpaul error = ifmedia_ioctl(ifp, ifr, 223650462Swpaul &mii->mii_media, command); 223750462Swpaul } 223836270Swpaul break; 223936270Swpaul default: 2240106936Ssam error = ether_ioctl(ifp, command, data); 224136270Swpaul break; 224236270Swpaul } 224336270Swpaul 224436270Swpaul (void)splx(s); 224536270Swpaul 224636270Swpaul return(error); 224736270Swpaul} 224836270Swpaul 2249102336Salfredstatic void 2250102336Salfredtl_watchdog(ifp) 225136270Swpaul struct ifnet *ifp; 225236270Swpaul{ 225336270Swpaul struct tl_softc *sc; 225436270Swpaul 225536270Swpaul sc = ifp->if_softc; 225636270Swpaul 2257105599Sbrooks if_printf(ifp, "device timeout\n"); 225836270Swpaul 225936270Swpaul ifp->if_oerrors++; 226036270Swpaul 226150468Swpaul tl_softreset(sc, 1); 226236270Swpaul tl_init(sc); 226336270Swpaul 226436270Swpaul return; 226536270Swpaul} 226636270Swpaul 226736270Swpaul/* 226836270Swpaul * Stop the adapter and free any mbufs allocated to the 226936270Swpaul * RX and TX lists. 227036270Swpaul */ 2271102336Salfredstatic void 2272102336Salfredtl_stop(sc) 227336270Swpaul struct tl_softc *sc; 227436270Swpaul{ 227536270Swpaul register int i; 227636270Swpaul struct ifnet *ifp; 227736270Swpaul 227867087Swpaul TL_LOCK(sc); 227967087Swpaul 228036270Swpaul ifp = &sc->arpcom.ac_if; 228136270Swpaul 228236270Swpaul /* Stop the stats updater. */ 228336270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 228436270Swpaul 228536270Swpaul /* Stop the transmitter */ 228639583Swpaul CMD_CLR(sc, TL_CMD_RT); 228739583Swpaul CMD_SET(sc, TL_CMD_STOP); 228839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 228936270Swpaul 229036270Swpaul /* Stop the receiver */ 229139583Swpaul CMD_SET(sc, TL_CMD_RT); 229239583Swpaul CMD_SET(sc, TL_CMD_STOP); 229339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 229436270Swpaul 229536270Swpaul /* 229636270Swpaul * Disable host interrupts. 229736270Swpaul */ 229839583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 229936270Swpaul 230036270Swpaul /* 230136270Swpaul * Clear list pointer. 230236270Swpaul */ 230339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 230436270Swpaul 230536270Swpaul /* 230636270Swpaul * Free the RX lists. 230736270Swpaul */ 230836270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 230936270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 231036270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 231136270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 231236270Swpaul } 231336270Swpaul } 231436270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 231536270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 231636270Swpaul 231736270Swpaul /* 231836270Swpaul * Free the TX list buffers. 231936270Swpaul */ 232036270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 232136270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 232236270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 232336270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 232436270Swpaul } 232536270Swpaul } 232636270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 232736270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 232836270Swpaul 232936270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 233067087Swpaul TL_UNLOCK(sc); 233136270Swpaul 233236270Swpaul return; 233336270Swpaul} 233436270Swpaul 233536270Swpaul/* 233636270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 233736270Swpaul * get confused by errant DMAs when rebooting. 233836270Swpaul */ 2339102336Salfredstatic void 2340102336Salfredtl_shutdown(dev) 234148992Swpaul device_t dev; 234236270Swpaul{ 234339583Swpaul struct tl_softc *sc; 234436270Swpaul 234548992Swpaul sc = device_get_softc(dev); 234636270Swpaul 234739583Swpaul tl_stop(sc); 234836270Swpaul 234936270Swpaul return; 235036270Swpaul} 2351