if_tl.c revision 113506
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 3336270Swpaul/* 3436270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3536270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3636270Swpaul * the National Semiconductor DP83840A physical interface and the 3736270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 3836270Swpaul * 3939583Swpaul * Written using the following four documents: 4036270Swpaul * 4136270Swpaul * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) 4236270Swpaul * National Semiconductor DP83840A data sheet (www.national.com) 4336270Swpaul * Microchip Technology 24C02C data sheet (www.microchip.com) 4439583Swpaul * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) 4536270Swpaul * 4636270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4736270Swpaul * Electrical Engineering Department 4836270Swpaul * Columbia University, New York City 4936270Swpaul */ 5036270Swpaul 5136270Swpaul/* 5236270Swpaul * Some notes about the ThunderLAN: 5336270Swpaul * 5436270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5536270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5639583Swpaul * independent interface (MII) bus. The MII allows the ThunderLAN chip to 5736270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 5836270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 5936270Swpaul * to act as a complete ethernet interface. 6036270Swpaul * 6136270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6236270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6336270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6439583Swpaul * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters 6539583Swpaul * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in 6639583Swpaul * concert with the ThunderLAN's internal PHY to provide full 10/100 6739583Swpaul * support. This is cheaper than using a standalone external PHY for both 6839583Swpaul * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. 6939583Swpaul * A serial EEPROM is also attached to the ThunderLAN chip to provide 7039583Swpaul * power-up default register settings and for storing the adapter's 7139583Swpaul * station address. Although not supported by this driver, the ThunderLAN 7239583Swpaul * chip can also be connected to token ring PHYs. 7336270Swpaul * 7436270Swpaul * The ThunderLAN has a set of registers which can be used to issue 7539583Swpaul * commands, acknowledge interrupts, and to manipulate other internal 7636270Swpaul * registers on its DIO bus. The primary registers can be accessed 7736270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 7836270Swpaul * depending on how the card is configured during the PCI probing 7936270Swpaul * phase. It is even possible to have both PIO and memory mapped 8036270Swpaul * access turned on at the same time. 8136270Swpaul * 8236270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 8336270Swpaul * using frame 'lists.' A list structure looks more or less like this: 8436270Swpaul * 8536270Swpaul * struct tl_frag { 8636270Swpaul * u_int32_t fragment_address; 8736270Swpaul * u_int32_t fragment_size; 8836270Swpaul * }; 8936270Swpaul * struct tl_list { 9036270Swpaul * u_int32_t forward_pointer; 9136270Swpaul * u_int16_t cstat; 9236270Swpaul * u_int16_t frame_size; 9336270Swpaul * struct tl_frag fragments[10]; 9436270Swpaul * }; 9536270Swpaul * 9636270Swpaul * The forward pointer in the list header can be either a 0 or the address 9736270Swpaul * of another list, which allows several lists to be linked together. Each 9836270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 9936270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 10036270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 10136270Swpaul * addresses are physical memory addresses, not virtual. Note also that 10236270Swpaul * a single ethernet frame can not span lists: if the host wants to 10336270Swpaul * transmit a frame and the frame data is split up over more than 10 10436270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 10536270Swpaul * 10636270Swpaul * To receive frames, the driver sets up a number of lists and populates 10736270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 10836270Swpaul * When a frame is received, the chip will DMA it into the memory regions 10936270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 11036270Swpaul * frame interrupt' when done. The driver may choose to use only one 11136270Swpaul * fragment per list; this may result is slighltly less efficient use 11236270Swpaul * of memory in exchange for improving performance. 11336270Swpaul * 11436270Swpaul * To transmit frames, the driver again sets up lists and fragment 11536270Swpaul * descriptors, only this time the buffers contain frame data that 11636270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 11736270Swpaul * has transfered the data into its on-board SRAM, it will trigger a 11836270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 11936270Swpaul * interrupt when it reaches the end of the list. 12036270Swpaul */ 12136270Swpaul 12236270Swpaul/* 12336270Swpaul * Some notes about this driver: 12436270Swpaul * 12536270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 12636270Swpaul * reception, transmission and interrupt handling. The simplest approach 12736270Swpaul * is to use one list each for transmission and reception. In this mode, 12836270Swpaul * the ThunderLAN will generate two interrupts for every received frame 12936270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 13036270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 13136270Swpaul * performance to have to handle so many interrupts. 13236270Swpaul * 13336270Swpaul * Initially I wanted to create a circular list of receive buffers so 13436270Swpaul * that the ThunderLAN chip would think there was an infinitely long 13536270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 13636270Swpaul * doesn't work correctly under heavy load: while the manual says the 13736270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 13836270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 13936270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 14036270Swpaul * is that the chip might traverse the entire circular list and then wrap 14136270Swpaul * around before you have a chance to do anything about it. Consequently, 14236270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 14336270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 14436270Swpaul * is shifted to the end of the list. This gives the appearance of an 14536270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 14636270Swpaul * the chip and allow all of the lists to be filled up. 14736270Swpaul * 14836270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 14936270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 15036270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 15136270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 15236270Swpaul * 15336270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 15436270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 15536270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 15636270Swpaul * queue several frames in one shot, and only have to handle a total 15736270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 15836270Swpaul * are transmitted. Frame transmission is done directly out of the 15936270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 16036270Swpaul * The driver simply sets up the fragment descriptors in the transmit 16136270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 16236270Swpaul * 16336270Swpaul * Note that since the RX and TX lists themselves are always used 16436270Swpaul * only by the driver, the are malloc()ed once at driver initialization 16536270Swpaul * time and never free()ed. 16636270Swpaul * 16736270Swpaul * Also, in order to remain as platform independent as possible, this 16836270Swpaul * driver uses memory mapped register access to manipulate the card 16936270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 17036270Swpaul * (and related) instructions which are specific to the i386 platform. 17136270Swpaul * 17236270Swpaul * Using these techniques, this driver achieves very high performance 17336270Swpaul * by minimizing the amount of interrupts generated during large 17436270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 17536270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 17636270Swpaul * itself thereby reducing the load on the host CPU. 17736270Swpaul */ 17836270Swpaul 179113038Sobrien#include <sys/cdefs.h> 180113038Sobrien__FBSDID("$FreeBSD: head/sys/pci/if_tl.c 113506 2003-04-15 06:37:30Z mdodd $"); 181113038Sobrien 18236270Swpaul#include <sys/param.h> 18336270Swpaul#include <sys/systm.h> 18436270Swpaul#include <sys/sockio.h> 18536270Swpaul#include <sys/mbuf.h> 18636270Swpaul#include <sys/malloc.h> 18736270Swpaul#include <sys/kernel.h> 18836270Swpaul#include <sys/socket.h> 18936270Swpaul 19036270Swpaul#include <net/if.h> 19136270Swpaul#include <net/if_arp.h> 19236270Swpaul#include <net/ethernet.h> 19336270Swpaul#include <net/if_dl.h> 19436270Swpaul#include <net/if_media.h> 19536270Swpaul 19636270Swpaul#include <net/bpf.h> 19736270Swpaul 19836270Swpaul#include <vm/vm.h> /* for vtophys */ 19936270Swpaul#include <vm/pmap.h> /* for vtophys */ 20045155Swpaul#include <machine/bus_memio.h> 20145155Swpaul#include <machine/bus_pio.h> 20245155Swpaul#include <machine/bus.h> 20348992Swpaul#include <machine/resource.h> 20448992Swpaul#include <sys/bus.h> 20548992Swpaul#include <sys/rman.h> 20636270Swpaul 20750462Swpaul#include <dev/mii/mii.h> 20850462Swpaul#include <dev/mii/miivar.h> 20950462Swpaul 21036270Swpaul#include <pci/pcireg.h> 21136270Swpaul#include <pci/pcivar.h> 21236270Swpaul 21339957Swpaul/* 21439957Swpaul * Default to using PIO register access mode to pacify certain 21539957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21639957Swpaul * don't seem to handle memory mapped mode properly. 21739957Swpaul */ 21839957Swpaul#define TL_USEIOSPACE 21939957Swpaul 22036270Swpaul#include <pci/if_tlreg.h> 22136270Swpaul 222113506SmdoddMODULE_DEPEND(tl, pci, 1, 1, 1); 223113506SmdoddMODULE_DEPEND(tl, ether, 1, 1, 1); 22459758SpeterMODULE_DEPEND(tl, miibus, 1, 1, 1); 22559758Speter 22651089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 22750462Swpaul#include "miibus_if.h" 22850462Swpaul 22936270Swpaul/* 23036270Swpaul * Various supported device vendors/types and their names. 23136270Swpaul */ 23236270Swpaul 23336270Swpaulstatic struct tl_type tl_devs[] = { 23436270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23536270Swpaul "Texas Instruments ThunderLAN" }, 23636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23736270Swpaul "Compaq Netelligent 10" }, 23836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 23936270Swpaul "Compaq Netelligent 10/100" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24136270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24336270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24536270Swpaul "Compaq NetFlex-3/P Integrated" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24736270Swpaul "Compaq NetFlex-3/P" }, 24836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 24936270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25037626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25137626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25237626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25337626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25437626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25537626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25637626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25737626Swpaul "Olicom OC-2183/2185" }, 25837626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 25937626Swpaul "Olicom OC-2325" }, 26037626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26137626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26236270Swpaul { 0, 0, NULL } 26336270Swpaul}; 26436270Swpaul 26592739Salfredstatic int tl_probe (device_t); 26692739Salfredstatic int tl_attach (device_t); 26792739Salfredstatic int tl_detach (device_t); 26892739Salfredstatic int tl_intvec_rxeoc (void *, u_int32_t); 26992739Salfredstatic int tl_intvec_txeoc (void *, u_int32_t); 27092739Salfredstatic int tl_intvec_txeof (void *, u_int32_t); 27192739Salfredstatic int tl_intvec_rxeof (void *, u_int32_t); 27292739Salfredstatic int tl_intvec_adchk (void *, u_int32_t); 27392739Salfredstatic int tl_intvec_netsts (void *, u_int32_t); 27436270Swpaul 27592739Salfredstatic int tl_newbuf (struct tl_softc *, struct tl_chain_onefrag *); 27692739Salfredstatic void tl_stats_update (void *); 27792739Salfredstatic int tl_encap (struct tl_softc *, struct tl_chain *, 27892739Salfred struct mbuf *); 27936270Swpaul 28092739Salfredstatic void tl_intr (void *); 28192739Salfredstatic void tl_start (struct ifnet *); 28292739Salfredstatic int tl_ioctl (struct ifnet *, u_long, caddr_t); 28392739Salfredstatic void tl_init (void *); 28492739Salfredstatic void tl_stop (struct tl_softc *); 28592739Salfredstatic void tl_watchdog (struct ifnet *); 28692739Salfredstatic void tl_shutdown (device_t); 28792739Salfredstatic int tl_ifmedia_upd (struct ifnet *); 28892739Salfredstatic void tl_ifmedia_sts (struct ifnet *, struct ifmediareq *); 28936270Swpaul 29092739Salfredstatic u_int8_t tl_eeprom_putbyte (struct tl_softc *, int); 29192739Salfredstatic u_int8_t tl_eeprom_getbyte (struct tl_softc *, int, u_int8_t *); 29292739Salfredstatic int tl_read_eeprom (struct tl_softc *, caddr_t, int, int); 29336270Swpaul 29492739Salfredstatic void tl_mii_sync (struct tl_softc *); 29592739Salfredstatic void tl_mii_send (struct tl_softc *, u_int32_t, int); 29692739Salfredstatic int tl_mii_readreg (struct tl_softc *, struct tl_mii_frame *); 29792739Salfredstatic int tl_mii_writereg (struct tl_softc *, struct tl_mii_frame *); 29892739Salfredstatic int tl_miibus_readreg (device_t, int, int); 29992739Salfredstatic int tl_miibus_writereg (device_t, int, int, int); 30092739Salfredstatic void tl_miibus_statchg (device_t); 30136270Swpaul 30292739Salfredstatic void tl_setmode (struct tl_softc *, int); 30392739Salfredstatic int tl_calchash (caddr_t); 30492739Salfredstatic void tl_setmulti (struct tl_softc *); 30592739Salfredstatic void tl_setfilt (struct tl_softc *, caddr_t, int); 30692739Salfredstatic void tl_softreset (struct tl_softc *, int); 30792739Salfredstatic void tl_hardreset (device_t); 30892739Salfredstatic int tl_list_rx_init (struct tl_softc *); 30992739Salfredstatic int tl_list_tx_init (struct tl_softc *); 31036270Swpaul 31192739Salfredstatic u_int8_t tl_dio_read8 (struct tl_softc *, int); 31292739Salfredstatic u_int16_t tl_dio_read16 (struct tl_softc *, int); 31392739Salfredstatic u_int32_t tl_dio_read32 (struct tl_softc *, int); 31492739Salfredstatic void tl_dio_write8 (struct tl_softc *, int, int); 31592739Salfredstatic void tl_dio_write16 (struct tl_softc *, int, int); 31692739Salfredstatic void tl_dio_write32 (struct tl_softc *, int, int); 31792739Salfredstatic void tl_dio_setbit (struct tl_softc *, int, int); 31892739Salfredstatic void tl_dio_clrbit (struct tl_softc *, int, int); 31992739Salfredstatic void tl_dio_setbit16 (struct tl_softc *, int, int); 32092739Salfredstatic void tl_dio_clrbit16 (struct tl_softc *, int, int); 32139583Swpaul 32249010Swpaul#ifdef TL_USEIOSPACE 32349010Swpaul#define TL_RES SYS_RES_IOPORT 32449010Swpaul#define TL_RID TL_PCI_LOIO 32549010Swpaul#else 32649010Swpaul#define TL_RES SYS_RES_MEMORY 32749010Swpaul#define TL_RID TL_PCI_LOMEM 32849010Swpaul#endif 32949010Swpaul 33048992Swpaulstatic device_method_t tl_methods[] = { 33148992Swpaul /* Device interface */ 33248992Swpaul DEVMETHOD(device_probe, tl_probe), 33348992Swpaul DEVMETHOD(device_attach, tl_attach), 33448992Swpaul DEVMETHOD(device_detach, tl_detach), 33548992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33650462Swpaul 33750462Swpaul /* bus interface */ 33850462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 33950462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 34050462Swpaul 34150462Swpaul /* MII interface */ 34250462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34350462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34450462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34550462Swpaul 34648992Swpaul { 0, 0 } 34748992Swpaul}; 34848992Swpaul 34948992Swpaulstatic driver_t tl_driver = { 35051455Swpaul "tl", 35148992Swpaul tl_methods, 35248992Swpaul sizeof(struct tl_softc) 35348992Swpaul}; 35448992Swpaul 35548992Swpaulstatic devclass_t tl_devclass; 35648992Swpaul 357113506SmdoddDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 35851473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 35948992Swpaul 36039583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 36141656Swpaul struct tl_softc *sc; 36241656Swpaul int reg; 36339583Swpaul{ 36439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36539583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36639583Swpaul} 36739583Swpaul 36839583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 36941656Swpaul struct tl_softc *sc; 37041656Swpaul int reg; 37139583Swpaul{ 37239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37339583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37439583Swpaul} 37539583Swpaul 37639583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 37741656Swpaul struct tl_softc *sc; 37841656Swpaul int reg; 37939583Swpaul{ 38039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38139583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38239583Swpaul} 38339583Swpaul 38439583Swpaulstatic void tl_dio_write8(sc, reg, val) 38541656Swpaul struct tl_softc *sc; 38641656Swpaul int reg; 38741656Swpaul int val; 38839583Swpaul{ 38939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39039583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 39139583Swpaul return; 39239583Swpaul} 39339583Swpaul 39439583Swpaulstatic void tl_dio_write16(sc, reg, val) 39541656Swpaul struct tl_softc *sc; 39641656Swpaul int reg; 39741656Swpaul int val; 39839583Swpaul{ 39939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40039583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 40139583Swpaul return; 40239583Swpaul} 40339583Swpaul 40439583Swpaulstatic void tl_dio_write32(sc, reg, val) 40541656Swpaul struct tl_softc *sc; 40641656Swpaul int reg; 40741656Swpaul int val; 40839583Swpaul{ 40939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 41039583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 41139583Swpaul return; 41239583Swpaul} 41339583Swpaul 414102336Salfredstatic void 415102336Salfredtl_dio_setbit(sc, reg, bit) 41641656Swpaul struct tl_softc *sc; 41741656Swpaul int reg; 41841656Swpaul int bit; 41939583Swpaul{ 42039583Swpaul u_int8_t f; 42139583Swpaul 42239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42339583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42439583Swpaul f |= bit; 42539583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 42639583Swpaul 42739583Swpaul return; 42839583Swpaul} 42939583Swpaul 430102336Salfredstatic void 431102336Salfredtl_dio_clrbit(sc, reg, bit) 43241656Swpaul struct tl_softc *sc; 43341656Swpaul int reg; 43441656Swpaul int bit; 43539583Swpaul{ 43639583Swpaul u_int8_t f; 43739583Swpaul 43839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 43939583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 44039583Swpaul f &= ~bit; 44139583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44239583Swpaul 44339583Swpaul return; 44439583Swpaul} 44539583Swpaul 44639583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44741656Swpaul struct tl_softc *sc; 44841656Swpaul int reg; 44941656Swpaul int bit; 45039583Swpaul{ 45139583Swpaul u_int16_t f; 45239583Swpaul 45339583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45439583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45539583Swpaul f |= bit; 45639583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45739583Swpaul 45839583Swpaul return; 45939583Swpaul} 46039583Swpaul 46139583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46241656Swpaul struct tl_softc *sc; 46341656Swpaul int reg; 46441656Swpaul int bit; 46539583Swpaul{ 46639583Swpaul u_int16_t f; 46739583Swpaul 46839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 46939583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 47039583Swpaul f &= ~bit; 47139583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47239583Swpaul 47339583Swpaul return; 47439583Swpaul} 47539583Swpaul 47636270Swpaul/* 47736270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 47836270Swpaul */ 47939583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 48039583Swpaul struct tl_softc *sc; 48141656Swpaul int byte; 48236270Swpaul{ 48336270Swpaul register int i, ack = 0; 48436270Swpaul 48536270Swpaul /* 48636270Swpaul * Make sure we're in TX mode. 48736270Swpaul */ 48839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 48936270Swpaul 49036270Swpaul /* 49136270Swpaul * Feed in each bit and stobe the clock. 49236270Swpaul */ 49336270Swpaul for (i = 0x80; i; i >>= 1) { 49436270Swpaul if (byte & i) { 49539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49636270Swpaul } else { 49739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 49836270Swpaul } 49939583Swpaul DELAY(1); 50039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50139583Swpaul DELAY(1); 50239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50336270Swpaul } 50436270Swpaul 50536270Swpaul /* 50636270Swpaul * Turn off TX mode. 50736270Swpaul */ 50839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 50936270Swpaul 51036270Swpaul /* 51136270Swpaul * Check for ack. 51236270Swpaul */ 51339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51439583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51636270Swpaul 51736270Swpaul return(ack); 51836270Swpaul} 51936270Swpaul 52036270Swpaul/* 52136270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52236270Swpaul */ 52339583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52439583Swpaul struct tl_softc *sc; 52541656Swpaul int addr; 52636270Swpaul u_int8_t *dest; 52736270Swpaul{ 52836270Swpaul register int i; 52936270Swpaul u_int8_t byte = 0; 530105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 53136270Swpaul 53239583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53339583Swpaul 53436270Swpaul EEPROM_START; 53539583Swpaul 53636270Swpaul /* 53736270Swpaul * Send write control code to EEPROM. 53836270Swpaul */ 53939583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 540105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 541105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54236270Swpaul return(1); 54339583Swpaul } 54436270Swpaul 54536270Swpaul /* 54636270Swpaul * Send address of byte we want to read. 54736270Swpaul */ 54839583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 549105599Sbrooks if_printf(ifp, "failed to send address, status: %x\n", 550105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 55136270Swpaul return(1); 55239583Swpaul } 55336270Swpaul 55436270Swpaul EEPROM_STOP; 55536270Swpaul EEPROM_START; 55636270Swpaul /* 55736270Swpaul * Send read control code to EEPROM. 55836270Swpaul */ 55939583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 560105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 561105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 56236270Swpaul return(1); 56339583Swpaul } 56436270Swpaul 56536270Swpaul /* 56636270Swpaul * Start reading bits from EEPROM. 56736270Swpaul */ 56839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56936270Swpaul for (i = 0x80; i; i >>= 1) { 57039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57139583Swpaul DELAY(1); 57239583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57336270Swpaul byte |= i; 57439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57536501Swpaul DELAY(1); 57636270Swpaul } 57736270Swpaul 57836270Swpaul EEPROM_STOP; 57936270Swpaul 58036270Swpaul /* 58136270Swpaul * No ACK generated for read, so just return byte. 58236270Swpaul */ 58336270Swpaul 58436270Swpaul *dest = byte; 58536270Swpaul 58636270Swpaul return(0); 58736270Swpaul} 58836270Swpaul 58939583Swpaul/* 59039583Swpaul * Read a sequence of bytes from the EEPROM. 59139583Swpaul */ 592102336Salfredstatic int 593102336Salfredtl_read_eeprom(sc, dest, off, cnt) 59439583Swpaul struct tl_softc *sc; 59539583Swpaul caddr_t dest; 59639583Swpaul int off; 59739583Swpaul int cnt; 59836270Swpaul{ 59939583Swpaul int err = 0, i; 60039583Swpaul u_int8_t byte = 0; 60139583Swpaul 60239583Swpaul for (i = 0; i < cnt; i++) { 60339583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60439583Swpaul if (err) 60539583Swpaul break; 60639583Swpaul *(dest + i) = byte; 60739583Swpaul } 60839583Swpaul 60939583Swpaul return(err ? 1 : 0); 61039583Swpaul} 61139583Swpaul 612102336Salfredstatic void 613102336Salfredtl_mii_sync(sc) 61439583Swpaul struct tl_softc *sc; 61539583Swpaul{ 61636270Swpaul register int i; 61736270Swpaul 61839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 61936270Swpaul 62036270Swpaul for (i = 0; i < 32; i++) { 62139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62336270Swpaul } 62436270Swpaul 62536270Swpaul return; 62636270Swpaul} 62736270Swpaul 628102336Salfredstatic void 629102336Salfredtl_mii_send(sc, bits, cnt) 63039583Swpaul struct tl_softc *sc; 63136270Swpaul u_int32_t bits; 63236270Swpaul int cnt; 63336270Swpaul{ 63436270Swpaul int i; 63536270Swpaul 63636270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63836270Swpaul if (bits & i) { 63939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 64036270Swpaul } else { 64139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64236270Swpaul } 64339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64436270Swpaul } 64536270Swpaul} 64636270Swpaul 647102336Salfredstatic int 648102336Salfredtl_mii_readreg(sc, frame) 64939583Swpaul struct tl_softc *sc; 65036270Swpaul struct tl_mii_frame *frame; 65136270Swpaul 65236270Swpaul{ 65367087Swpaul int i, ack; 65436270Swpaul int minten = 0; 65536270Swpaul 65667087Swpaul TL_LOCK(sc); 65736270Swpaul 65839583Swpaul tl_mii_sync(sc); 65936270Swpaul 66036270Swpaul /* 66136270Swpaul * Set up frame for RX. 66236270Swpaul */ 66336270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66436270Swpaul frame->mii_opcode = TL_MII_READOP; 66536270Swpaul frame->mii_turnaround = 0; 66636270Swpaul frame->mii_data = 0; 66736270Swpaul 66836270Swpaul /* 66936270Swpaul * Turn off MII interrupt by forcing MINTEN low. 67036270Swpaul */ 67139583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 67236270Swpaul if (minten) { 67339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67436270Swpaul } 67536270Swpaul 67636270Swpaul /* 67736270Swpaul * Turn on data xmit. 67836270Swpaul */ 67939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 68036270Swpaul 68136270Swpaul /* 68236270Swpaul * Send command/address info. 68336270Swpaul */ 68439583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68539583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68639583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68739583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 68836270Swpaul 68936270Swpaul /* 69036270Swpaul * Turn off xmit. 69136270Swpaul */ 69239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69336270Swpaul 69436270Swpaul /* Idle bit */ 69539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69736270Swpaul 69836270Swpaul /* Check for ack */ 69939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 70039583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 70136270Swpaul 70236270Swpaul /* Complete the cycle */ 70339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70436270Swpaul 70536270Swpaul /* 70636270Swpaul * Now try reading data bits. If the ack failed, we still 70736270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 70836270Swpaul */ 70936270Swpaul if (ack) { 71036270Swpaul for(i = 0; i < 16; i++) { 71139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71336270Swpaul } 71436270Swpaul goto fail; 71536270Swpaul } 71636270Swpaul 71736270Swpaul for (i = 0x8000; i; i >>= 1) { 71839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71936270Swpaul if (!ack) { 72039583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 72136270Swpaul frame->mii_data |= i; 72236270Swpaul } 72339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72436270Swpaul } 72536270Swpaul 72636270Swpaulfail: 72736270Swpaul 72839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 73036270Swpaul 73136270Swpaul /* Reenable interrupts */ 73236270Swpaul if (minten) { 73339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73436270Swpaul } 73536270Swpaul 73667087Swpaul TL_UNLOCK(sc); 73736270Swpaul 73836270Swpaul if (ack) 73936270Swpaul return(1); 74036270Swpaul return(0); 74136270Swpaul} 74236270Swpaul 743102336Salfredstatic int 744102336Salfredtl_mii_writereg(sc, frame) 74539583Swpaul struct tl_softc *sc; 74636270Swpaul struct tl_mii_frame *frame; 74736270Swpaul 74836270Swpaul{ 74936270Swpaul int minten; 75036270Swpaul 75167087Swpaul TL_LOCK(sc); 75267087Swpaul 75339583Swpaul tl_mii_sync(sc); 75436270Swpaul 75536270Swpaul /* 75636270Swpaul * Set up frame for TX. 75736270Swpaul */ 75836270Swpaul 75936270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 76036270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 76136270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 76236270Swpaul 76336270Swpaul /* 76436270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76536270Swpaul */ 76639583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76736270Swpaul if (minten) { 76839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76936270Swpaul } 77036270Swpaul 77136270Swpaul /* 77236270Swpaul * Turn on data output. 77336270Swpaul */ 77439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77536270Swpaul 77639583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77739583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77839583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77939583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 78039583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 78139583Swpaul tl_mii_send(sc, frame->mii_data, 16); 78236270Swpaul 78339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78536270Swpaul 78636270Swpaul /* 78736270Swpaul * Turn off xmit. 78836270Swpaul */ 78939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 79036270Swpaul 79136270Swpaul /* Reenable interrupts */ 79236270Swpaul if (minten) 79339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79436270Swpaul 79567087Swpaul TL_UNLOCK(sc); 79636270Swpaul 79736270Swpaul return(0); 79836270Swpaul} 79936270Swpaul 800102336Salfredstatic int 801102336Salfredtl_miibus_readreg(dev, phy, reg) 80250462Swpaul device_t dev; 80350462Swpaul int phy, reg; 80450462Swpaul{ 80536270Swpaul struct tl_softc *sc; 80636270Swpaul struct tl_mii_frame frame; 80736270Swpaul 80850462Swpaul sc = device_get_softc(dev); 80936270Swpaul bzero((char *)&frame, sizeof(frame)); 81036270Swpaul 81150462Swpaul frame.mii_phyaddr = phy; 81236270Swpaul frame.mii_regaddr = reg; 81339583Swpaul tl_mii_readreg(sc, &frame); 81436270Swpaul 81536270Swpaul return(frame.mii_data); 81636270Swpaul} 81736270Swpaul 818102336Salfredstatic int 819102336Salfredtl_miibus_writereg(dev, phy, reg, data) 82050462Swpaul device_t dev; 82150462Swpaul int phy, reg, data; 82250462Swpaul{ 82336270Swpaul struct tl_softc *sc; 82436270Swpaul struct tl_mii_frame frame; 82536270Swpaul 82650462Swpaul sc = device_get_softc(dev); 82736270Swpaul bzero((char *)&frame, sizeof(frame)); 82836270Swpaul 82950462Swpaul frame.mii_phyaddr = phy; 83036270Swpaul frame.mii_regaddr = reg; 83136270Swpaul frame.mii_data = data; 83236270Swpaul 83339583Swpaul tl_mii_writereg(sc, &frame); 83436270Swpaul 83550462Swpaul return(0); 83636270Swpaul} 83736270Swpaul 838102336Salfredstatic void 839102336Salfredtl_miibus_statchg(dev) 84050462Swpaul device_t dev; 84150462Swpaul{ 84236270Swpaul struct tl_softc *sc; 84350462Swpaul struct mii_data *mii; 84436270Swpaul 84550462Swpaul sc = device_get_softc(dev); 84667087Swpaul TL_LOCK(sc); 84750462Swpaul mii = device_get_softc(sc->tl_miibus); 84836270Swpaul 84950462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 85050462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85136270Swpaul } else { 85250462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85336270Swpaul } 85467087Swpaul TL_UNLOCK(sc); 85536270Swpaul 85636270Swpaul return; 85736270Swpaul} 85836270Swpaul 85936270Swpaul/* 86050462Swpaul * Set modes for bitrate devices. 86136270Swpaul */ 862102336Salfredstatic void 863102336Salfredtl_setmode(sc, media) 86436270Swpaul struct tl_softc *sc; 86536270Swpaul int media; 86636270Swpaul{ 86750462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 86850462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86936270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 87050462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 87136270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 87250462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87339583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87436270Swpaul } else { 87550462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87639583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87736270Swpaul } 87836270Swpaul } 87936270Swpaul 88036270Swpaul return; 88136270Swpaul} 88236270Swpaul 88336464Swpaul/* 88436464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 88536464Swpaul * table. This hash is simply the address split into 6-bit chunks 88636464Swpaul * XOR'd, e.g. 88736464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 88836464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 88936464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 89036464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 89136464Swpaul */ 892102336Salfredstatic int 893102336Salfredtl_calchash(addr) 89441656Swpaul caddr_t addr; 89536270Swpaul{ 89637626Swpaul int t; 89736270Swpaul 89836464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 89936464Swpaul (addr[2] ^ addr[5]); 90036464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 90136270Swpaul} 90236270Swpaul 90339583Swpaul/* 90439583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 90539583Swpaul * the multicast hash filter. The perfect filter can be programmed 90639583Swpaul * with up to four MAC addresses. The first one is always used to 90739583Swpaul * hold the station address, which leaves us free to use the other 90839583Swpaul * three for multicast addresses. 90939583Swpaul */ 910102336Salfredstatic void 911102336Salfredtl_setfilt(sc, addr, slot) 91239583Swpaul struct tl_softc *sc; 91341656Swpaul caddr_t addr; 91439583Swpaul int slot; 91539583Swpaul{ 91639583Swpaul int i; 91739583Swpaul u_int16_t regaddr; 91839583Swpaul 91939583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 92039583Swpaul 92139583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 92239583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 92339583Swpaul 92439583Swpaul return; 92539583Swpaul} 92639583Swpaul 92739583Swpaul/* 92839583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 92939583Swpaul * linked list. This is fine, except addresses are added from the head 93039583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 93139583Swpaul * group to always be in the perfect filter, but as more groups are added, 93239583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 93339583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 93439583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 93539583Swpaul * and into the hash table. 93639583Swpaul * 93739583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 93839583Swpaul * circular queue, we don't have the ability to just grab the tail of 93939583Swpaul * the list and traverse it backwards. Instead, we have to traverse 94039583Swpaul * the list once to find the tail, then traverse it again backwards to 94139583Swpaul * update the multicast filter. 94239583Swpaul */ 943102336Salfredstatic void 944102336Salfredtl_setmulti(sc) 94536270Swpaul struct tl_softc *sc; 94636270Swpaul{ 94736270Swpaul struct ifnet *ifp; 94836270Swpaul u_int32_t hashes[2] = { 0, 0 }; 94939583Swpaul int h, i; 95036270Swpaul struct ifmultiaddr *ifma; 95139583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 95236270Swpaul ifp = &sc->arpcom.ac_if; 95336270Swpaul 95439583Swpaul /* First, zot all the existing filters. */ 95539583Swpaul for (i = 1; i < 4; i++) 95641656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 95739583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 95839583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 95939583Swpaul 96039583Swpaul /* Now program new ones. */ 96139583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 96236270Swpaul hashes[0] = 0xFFFFFFFF; 96336270Swpaul hashes[1] = 0xFFFFFFFF; 96436270Swpaul } else { 96539583Swpaul i = 1; 96672084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 96736270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 96836270Swpaul continue; 96939583Swpaul /* 97039583Swpaul * Program the first three multicast groups 97139583Swpaul * into the perfect filter. For all others, 97239583Swpaul * use the hash table. 97339583Swpaul */ 97439583Swpaul if (i < 4) { 97539583Swpaul tl_setfilt(sc, 97639583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97739583Swpaul i++; 97839583Swpaul continue; 97939583Swpaul } 98039583Swpaul 98136270Swpaul h = tl_calchash( 98236270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 98336270Swpaul if (h < 32) 98436270Swpaul hashes[0] |= (1 << h); 98536270Swpaul else 98636317Swpaul hashes[1] |= (1 << (h - 32)); 98736270Swpaul } 98836270Swpaul } 98936270Swpaul 99039583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 99139583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 99236270Swpaul 99336270Swpaul return; 99436270Swpaul} 99536270Swpaul 99639583Swpaul/* 99739583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 99839583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99939583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 100039583Swpaul * experience this isn't necessary. 100139583Swpaul */ 1002102336Salfredstatic void 1003102336Salfredtl_hardreset(dev) 100450468Swpaul device_t dev; 100550468Swpaul{ 100639583Swpaul struct tl_softc *sc; 100739583Swpaul int i; 100850468Swpaul u_int16_t flags; 100939583Swpaul 101050468Swpaul sc = device_get_softc(dev); 101139583Swpaul 101250468Swpaul tl_mii_sync(sc); 101339583Swpaul 101450468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 101539583Swpaul 101650468Swpaul for (i = 0; i < MII_NPHY; i++) 101750468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 101839583Swpaul 101950468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 102039583Swpaul DELAY(50000); 102150468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 102239583Swpaul tl_mii_sync(sc); 102350468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 102439583Swpaul 102550468Swpaul DELAY(50000); 102639583Swpaul return; 102739583Swpaul} 102839583Swpaul 1029102336Salfredstatic void 1030102336Salfredtl_softreset(sc, internal) 103139583Swpaul struct tl_softc *sc; 103236270Swpaul int internal; 103336270Swpaul{ 103439583Swpaul u_int32_t cmd, dummy, i; 103536270Swpaul 103636270Swpaul /* Assert the adapter reset bit. */ 103739583Swpaul CMD_SET(sc, TL_CMD_ADRST); 103850468Swpaul 103936270Swpaul /* Turn off interrupts */ 104039583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 104136270Swpaul 104236270Swpaul /* First, clear the stats registers. */ 104339583Swpaul for (i = 0; i < 5; i++) 104439583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 104536270Swpaul 104636270Swpaul /* Clear Areg and Hash registers */ 104739583Swpaul for (i = 0; i < 8; i++) 104839583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104936270Swpaul 105036270Swpaul /* 105136270Swpaul * Set up Netconfig register. Enable one channel and 105236270Swpaul * one fragment mode. 105336270Swpaul */ 105439583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 105545155Swpaul if (internal && !sc->tl_bitrate) { 105639583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105736270Swpaul } else { 105839583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105936270Swpaul } 106036270Swpaul 106145155Swpaul /* Handle cards with bitrate devices. */ 106245155Swpaul if (sc->tl_bitrate) 106345155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 106445155Swpaul 106536270Swpaul /* 106636270Swpaul * Load adapter irq pacing timer and tx threshold. 106736270Swpaul * We make the transmit threshold 1 initially but we may 106836270Swpaul * change that later. 106936270Swpaul */ 107039583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 107136270Swpaul cmd |= TL_CMD_NES; 107236270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 107339583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 107439583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 107536270Swpaul 107636270Swpaul /* Unreset the MII */ 107739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 107836270Swpaul 107936270Swpaul /* Take the adapter out of reset */ 108039583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 108136270Swpaul 108236270Swpaul /* Wait for things to settle down a little. */ 108336270Swpaul DELAY(500); 108436270Swpaul 108536270Swpaul return; 108636270Swpaul} 108736270Swpaul 108836270Swpaul/* 108936270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 109039583Swpaul * against our list and return its name if we find a match. 109136270Swpaul */ 1092102336Salfredstatic int 1093102336Salfredtl_probe(dev) 109448992Swpaul device_t dev; 109536270Swpaul{ 109636270Swpaul struct tl_type *t; 109736270Swpaul 109836270Swpaul t = tl_devs; 109936270Swpaul 110036270Swpaul while(t->tl_name != NULL) { 110148992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 110248992Swpaul (pci_get_device(dev) == t->tl_did)) { 110348992Swpaul device_set_desc(dev, t->tl_name); 110448992Swpaul return(0); 110548992Swpaul } 110636270Swpaul t++; 110736270Swpaul } 110836270Swpaul 110948992Swpaul return(ENXIO); 111036270Swpaul} 111136270Swpaul 1112102336Salfredstatic int 1113102336Salfredtl_attach(dev) 111448992Swpaul device_t dev; 111536270Swpaul{ 111667087Swpaul int i; 111736270Swpaul u_int32_t command; 111839583Swpaul u_int16_t did, vid; 111939583Swpaul struct tl_type *t; 112039583Swpaul struct ifnet *ifp; 112139583Swpaul struct tl_softc *sc; 112248992Swpaul int unit, error = 0, rid; 112336270Swpaul 112448992Swpaul vid = pci_get_vendor(dev); 112548992Swpaul did = pci_get_device(dev); 112648992Swpaul sc = device_get_softc(dev); 112748992Swpaul unit = device_get_unit(dev); 112839583Swpaul 112939583Swpaul t = tl_devs; 113039583Swpaul while(t->tl_name != NULL) { 113139583Swpaul if (vid == t->tl_vid && did == t->tl_did) 113236270Swpaul break; 113339583Swpaul t++; 113439583Swpaul } 113536270Swpaul 113639583Swpaul if (t->tl_name == NULL) { 1137105599Sbrooks device_printf(dev, "unknown device!?\n"); 1138112878Sjhb return (ENXIO); 113936270Swpaul } 114036270Swpaul 114193818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 114293818Sjhb MTX_DEF | MTX_RECURSE); 114369583Swpaul 114436270Swpaul /* 114536270Swpaul * Map control/status registers. 114636270Swpaul */ 114772813Swpaul pci_enable_busmaster(dev); 114879472Swpaul pci_enable_io(dev, SYS_RES_IOPORT); 114979472Swpaul pci_enable_io(dev, SYS_RES_MEMORY); 115061041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 115136270Swpaul 115239583Swpaul#ifdef TL_USEIOSPACE 115339583Swpaul if (!(command & PCIM_CMD_PORTEN)) { 1154105599Sbrooks device_printf(dev, "failed to enable I/O ports!\n"); 115548992Swpaul error = ENXIO; 115639583Swpaul goto fail; 115739583Swpaul } 115839583Swpaul 115948992Swpaul rid = TL_PCI_LOIO; 116048992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 116148992Swpaul 0, ~0, 1, RF_ACTIVE); 116248992Swpaul 116348992Swpaul /* 116448992Swpaul * Some cards have the I/O and memory mapped address registers 116548992Swpaul * reversed. Try both combinations before giving up. 116648992Swpaul */ 116748992Swpaul if (sc->tl_res == NULL) { 116848992Swpaul rid = TL_PCI_LOMEM; 116948992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 117048992Swpaul 0, ~0, 1, RF_ACTIVE); 117145155Swpaul } 117239583Swpaul#else 117336270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 1174105599Sbrooks device_printf(dev, "failed to enable memory mapping!\n"); 117548992Swpaul error = ENXIO; 117636270Swpaul goto fail; 117736270Swpaul } 117836270Swpaul 117948992Swpaul rid = TL_PCI_LOMEM; 118048992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 118148992Swpaul 0, ~0, 1, RF_ACTIVE); 118248992Swpaul if (sc->tl_res == NULL) { 118348992Swpaul rid = TL_PCI_LOIO; 118448992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 118548992Swpaul 0, ~0, 1, RF_ACTIVE); 118636270Swpaul } 118739583Swpaul#endif 118836270Swpaul 118948992Swpaul if (sc->tl_res == NULL) { 1190105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 119148992Swpaul error = ENXIO; 119248992Swpaul goto fail; 119348992Swpaul } 119448992Swpaul 119548992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 119648992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 119748992Swpaul 119839583Swpaul#ifdef notdef 119939583Swpaul /* 120039583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 120139583Swpaul * timer all the way up to its maximum value. I'm not sure 120239583Swpaul * if this is really necessary, but what the manual wants, 120339583Swpaul * the manual gets. 120439583Swpaul */ 120548992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 120639583Swpaul command |= 0x0000FF00; 120748992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 120839583Swpaul#endif 120936270Swpaul 121036270Swpaul /* Allocate interrupt */ 121148992Swpaul rid = 0; 121248992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 121348992Swpaul RF_SHAREABLE | RF_ACTIVE); 121448992Swpaul 121548992Swpaul if (sc->tl_irq == NULL) { 1216105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 121748992Swpaul error = ENXIO; 121836270Swpaul goto fail; 121936270Swpaul } 122036270Swpaul 122136270Swpaul /* 122251439Swpaul * Now allocate memory for the TX and RX lists. 122336270Swpaul */ 122451439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 122551657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 122639583Swpaul 122751439Swpaul if (sc->tl_ldata == NULL) { 1228105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 122948992Swpaul error = ENXIO; 123036270Swpaul goto fail; 123136270Swpaul } 123236270Swpaul 123339583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 123439583Swpaul 123539583Swpaul sc->tl_dinfo = t; 123643235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 123739583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 123839583Swpaul if (t->tl_vid == OLICOM_VENDORID) 123939583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 124039583Swpaul 124139583Swpaul /* Reset the adapter. */ 124239583Swpaul tl_softreset(sc, 1); 124350468Swpaul tl_hardreset(dev); 124439583Swpaul tl_softreset(sc, 1); 124539583Swpaul 124638030Swpaul /* 124739583Swpaul * Get station address from the EEPROM. 124839583Swpaul */ 124939583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 125039583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 1251105599Sbrooks device_printf(dev, "failed to read station address\n"); 125248992Swpaul error = ENXIO; 125339583Swpaul goto fail; 125439583Swpaul } 125539583Swpaul 125639583Swpaul /* 125739583Swpaul * XXX Olicom, in its desire to be different from the 125839583Swpaul * rest of the world, has done strange things with the 125939583Swpaul * encoding of the station address in the EEPROM. First 126039583Swpaul * of all, they store the address at offset 0xF8 rather 126139583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 126239583Swpaul * Second, they store the address in three 16-bit words in 126339583Swpaul * network byte order, as opposed to storing it sequentially 126439583Swpaul * like all the other ThunderLAN cards. In order to get 126539583Swpaul * the station address in a form that matches what the Olicom 126639583Swpaul * diagnostic utility specifies, we have to byte-swap each 126739583Swpaul * word. To make things even more confusing, neither 00:00:28 126839583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 126939583Swpaul */ 127039583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 127139583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 127239583Swpaul u_int16_t *p; 127339583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 127439583Swpaul *p = ntohs(*p); 127539583Swpaul } 127639583Swpaul } 127739583Swpaul 127839583Swpaul /* 127936270Swpaul * A ThunderLAN chip was detected. Inform the world. 128036270Swpaul */ 1281105599Sbrooks device_printf(dev, "Ethernet address: %6D\n", 128239583Swpaul sc->arpcom.ac_enaddr, ":"); 128336270Swpaul 128439583Swpaul ifp = &sc->arpcom.ac_if; 128539583Swpaul ifp->if_softc = sc; 1286105599Sbrooks ifp->if_unit = unit; 128739583Swpaul ifp->if_name = "tl"; 128839583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 128939583Swpaul ifp->if_ioctl = tl_ioctl; 129039583Swpaul ifp->if_output = ether_output; 129139583Swpaul ifp->if_start = tl_start; 129239583Swpaul ifp->if_watchdog = tl_watchdog; 129339583Swpaul ifp->if_init = tl_init; 129439583Swpaul ifp->if_mtu = ETHERMTU; 129551439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 129639583Swpaul callout_handle_init(&sc->tl_stat_ch); 129739583Swpaul 129839583Swpaul /* Reset the adapter again. */ 129939583Swpaul tl_softreset(sc, 1); 130050468Swpaul tl_hardreset(dev); 130139583Swpaul tl_softreset(sc, 1); 130239583Swpaul 130336270Swpaul /* 130450462Swpaul * Do MII setup. If no PHYs are found, then this is a 130550462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 130650462Swpaul * and AUI/BNC. 130736270Swpaul */ 130850462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 130950462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 131045155Swpaul struct ifmedia *ifm; 131145155Swpaul sc->tl_bitrate = 1; 131245155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 131345155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 131445155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 131545155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 131645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 131745166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 131845155Swpaul /* Reset again, this time setting bitrate mode. */ 131945155Swpaul tl_softreset(sc, 1); 132045155Swpaul ifm = &sc->ifmedia; 132145155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 132245155Swpaul tl_ifmedia_upd(ifp); 132336270Swpaul } 132436270Swpaul 132539583Swpaul /* 132663090Sarchie * Call MI attach routine. 132739583Swpaul */ 1328106936Ssam ether_ifattach(ifp, sc->arpcom.ac_enaddr); 132938030Swpaul 1330112872Snjl error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 1331112872Snjl tl_intr, sc, &sc->tl_intrhand); 1332112872Snjl 1333112872Snjl if (error) { 1334112872Snjl device_printf(dev, "couldn't set up irq\n"); 1335112872Snjl goto fail; 1336112872Snjl } 1337112872Snjl 133836270Swpaulfail: 1339112872Snjl if (error) 1340112872Snjl tl_detach(dev); 1341112872Snjl 134248992Swpaul return(error); 134336270Swpaul} 134436270Swpaul 1345102336Salfredstatic int 1346102336Salfredtl_detach(dev) 134748992Swpaul device_t dev; 134848992Swpaul{ 134948992Swpaul struct tl_softc *sc; 135048992Swpaul struct ifnet *ifp; 135148992Swpaul 135248992Swpaul sc = device_get_softc(dev); 1353112880Sjhb KASSERT(mtx_initialized(&sc->tl_mtx), ("tl mutex not initialized")); 135467087Swpaul TL_LOCK(sc); 135548992Swpaul ifp = &sc->arpcom.ac_if; 135648992Swpaul 1357112872Snjl if (device_is_alive(dev)) { 1358112872Snjl if (bus_child_present(dev)) 1359112872Snjl tl_stop(sc); 1360112872Snjl ether_ifdetach(ifp); 1361112872Snjl device_delete_child(dev, sc->tl_miibus); 1362112872Snjl bus_generic_detach(dev); 1363112872Snjl } 136448992Swpaul 1365112872Snjl if (sc->tl_ldata) 1366112872Snjl contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 136750462Swpaul if (sc->tl_bitrate) 136850462Swpaul ifmedia_removeall(&sc->ifmedia); 136948992Swpaul 1370112872Snjl if (sc->tl_intrhand) 1371112872Snjl bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 1372112872Snjl if (sc->tl_irq) 1373112872Snjl bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 1374112872Snjl if (sc->tl_res) 1375112872Snjl bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 137648992Swpaul 137767087Swpaul TL_UNLOCK(sc); 137867087Swpaul mtx_destroy(&sc->tl_mtx); 137948992Swpaul 138048992Swpaul return(0); 138148992Swpaul} 138248992Swpaul 138336270Swpaul/* 138436270Swpaul * Initialize the transmit lists. 138536270Swpaul */ 1386102336Salfredstatic int 1387102336Salfredtl_list_tx_init(sc) 138836270Swpaul struct tl_softc *sc; 138936270Swpaul{ 139036270Swpaul struct tl_chain_data *cd; 139136270Swpaul struct tl_list_data *ld; 139236270Swpaul int i; 139336270Swpaul 139436270Swpaul cd = &sc->tl_cdata; 139536270Swpaul ld = sc->tl_ldata; 139636270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 139736270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 139836270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 139936270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 140036270Swpaul else 140136270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 140236270Swpaul } 140336270Swpaul 140436270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 140536270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 140636270Swpaul sc->tl_txeoc = 1; 140736270Swpaul 140836270Swpaul return(0); 140936270Swpaul} 141036270Swpaul 141136270Swpaul/* 141236270Swpaul * Initialize the RX lists and allocate mbufs for them. 141336270Swpaul */ 1414102336Salfredstatic int 1415102336Salfredtl_list_rx_init(sc) 141636270Swpaul struct tl_softc *sc; 141736270Swpaul{ 141836270Swpaul struct tl_chain_data *cd; 141936270Swpaul struct tl_list_data *ld; 142036270Swpaul int i; 142136270Swpaul 142236270Swpaul cd = &sc->tl_cdata; 142336270Swpaul ld = sc->tl_ldata; 142436270Swpaul 142540795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 142636270Swpaul cd->tl_rx_chain[i].tl_ptr = 142737626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 142839583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 142939583Swpaul return(ENOBUFS); 143040795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 143136270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 143236270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 143336270Swpaul } else { 143436270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 143536270Swpaul ld->tl_rx_list[i].tlist_fptr = 143636270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 143736270Swpaul } 143836270Swpaul } 143936270Swpaul 144036270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 144136270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 144236270Swpaul 144336270Swpaul return(0); 144436270Swpaul} 144536270Swpaul 1446102336Salfredstatic int 1447102336Salfredtl_newbuf(sc, c) 144836270Swpaul struct tl_softc *sc; 144937626Swpaul struct tl_chain_onefrag *c; 145036270Swpaul{ 145136270Swpaul struct mbuf *m_new = NULL; 145236270Swpaul 1453111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 145487846Sluigi if (m_new == NULL) 145536270Swpaul return(ENOBUFS); 145636270Swpaul 1457111119Simp MCLGET(m_new, M_DONTWAIT); 145836270Swpaul if (!(m_new->m_flags & M_EXT)) { 145936270Swpaul m_freem(m_new); 146036270Swpaul return(ENOBUFS); 146136270Swpaul } 146236270Swpaul 146345155Swpaul#ifdef __alpha__ 146445155Swpaul m_new->m_data += 2; 146545155Swpaul#endif 146645155Swpaul 146736270Swpaul c->tl_mbuf = m_new; 146836270Swpaul c->tl_next = NULL; 146936270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 147036270Swpaul c->tl_ptr->tlist_fptr = 0; 147137626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 147237626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 147356060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 147436270Swpaul 147536270Swpaul return(0); 147636270Swpaul} 147736270Swpaul/* 147836270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 147936270Swpaul * tells us that a full ethernet frame has been captured and we need 148036270Swpaul * to handle it. 148136270Swpaul * 148236270Swpaul * Reception is done using 'lists' which consist of a header and a 148336270Swpaul * series of 10 data count/data address pairs that point to buffers. 148436270Swpaul * Initially you're supposed to create a list, populate it with pointers 148536270Swpaul * to buffers, then load the physical address of the list into the 148636270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 148736270Swpaul * frame into the buffers for you. 148836270Swpaul * 148936270Swpaul * To make things as fast as possible, we have the chip DMA directly 149036270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 149136270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 149236270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 149336270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 149436270Swpaul * the chip, it will always think it has an endless receive channel. 149536270Swpaul * 149636270Swpaul * If we happen to fall behind and the chip manages to fill up all of 149736270Swpaul * the buffers, it will generate an end of channel interrupt and wait 149836270Swpaul * for us to empty the chain and restart the receiver. 149936270Swpaul */ 1500102336Salfredstatic int 1501102336Salfredtl_intvec_rxeof(xsc, type) 150236270Swpaul void *xsc; 150336270Swpaul u_int32_t type; 150436270Swpaul{ 150536270Swpaul struct tl_softc *sc; 150636270Swpaul int r = 0, total_len = 0; 150736270Swpaul struct ether_header *eh; 150836270Swpaul struct mbuf *m; 150936270Swpaul struct ifnet *ifp; 151037626Swpaul struct tl_chain_onefrag *cur_rx; 151136270Swpaul 151236270Swpaul sc = xsc; 151336270Swpaul ifp = &sc->arpcom.ac_if; 151436270Swpaul 151556060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 151656060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 151756060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 151856060Swpaul break; 151936270Swpaul r++; 152036270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 152136270Swpaul m = cur_rx->tl_mbuf; 152236270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 152336270Swpaul 152439583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 152539583Swpaul ifp->if_ierrors++; 152639583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 152739583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 152839583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 152939583Swpaul continue; 153039583Swpaul } 153136270Swpaul 153236270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 153336270Swpaul vtophys(cur_rx->tl_ptr); 153436270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 153536270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 153636270Swpaul 153737626Swpaul /* 153837626Swpaul * Note: when the ThunderLAN chip is in 'capture all 153937626Swpaul * frames' mode, it will receive its own transmissions. 154037626Swpaul * We drop don't need to process our own transmissions, 154137626Swpaul * so we drop them here and continue. 154237626Swpaul */ 1543106936Ssam eh = mtod(m, struct ether_header *); 154439583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 154539583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 154637626Swpaul ETHER_ADDR_LEN)) { 154737626Swpaul m_freem(m); 154837626Swpaul continue; 154937626Swpaul } 155037626Swpaul 1551106936Ssam m->m_pkthdr.rcvif = ifp; 1552106936Ssam m->m_pkthdr.len = m->m_len = total_len; 1553106936Ssam 1554106936Ssam (*ifp->if_input)(ifp, m); 155536270Swpaul } 155636270Swpaul 155736270Swpaul return(r); 155836270Swpaul} 155936270Swpaul 156036270Swpaul/* 156136270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 156236270Swpaul * initialized or the adapter reached a list with a forward pointer 156336270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 156436270Swpaul * the card has hit the end of the receive buffer chain and we need to 156536270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 156636270Swpaul */ 1567102336Salfredstatic int 1568102336Salfredtl_intvec_rxeoc(xsc, type) 156936270Swpaul void *xsc; 157036270Swpaul u_int32_t type; 157136270Swpaul{ 157236270Swpaul struct tl_softc *sc; 157336270Swpaul int r; 157456060Swpaul struct tl_chain_data *cd; 157536270Swpaul 157656060Swpaul 157736270Swpaul sc = xsc; 157856060Swpaul cd = &sc->tl_cdata; 157936270Swpaul 158036270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 158136270Swpaul r = tl_intvec_rxeof(xsc, type); 158239583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 158336270Swpaul r = 1; 158456060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 158556060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 158639583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 158736270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 158836270Swpaul return(r); 158936270Swpaul} 159036270Swpaul 1591102336Salfredstatic int 1592102336Salfredtl_intvec_txeof(xsc, type) 159336270Swpaul void *xsc; 159436270Swpaul u_int32_t type; 159536270Swpaul{ 159636270Swpaul struct tl_softc *sc; 159736270Swpaul int r = 0; 159836270Swpaul struct tl_chain *cur_tx; 159936270Swpaul 160036270Swpaul sc = xsc; 160136270Swpaul 160236270Swpaul /* 160336270Swpaul * Go through our tx list and free mbufs for those 160436270Swpaul * frames that have been sent. 160536270Swpaul */ 160636270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 160736270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 160836270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 160936270Swpaul break; 161036270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 161136270Swpaul 161236270Swpaul r++; 161336270Swpaul m_freem(cur_tx->tl_mbuf); 161436270Swpaul cur_tx->tl_mbuf = NULL; 161536270Swpaul 161636270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 161736270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 161837626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 161937626Swpaul break; 162036270Swpaul } 162136270Swpaul 162236270Swpaul return(r); 162336270Swpaul} 162436270Swpaul 162536270Swpaul/* 162636270Swpaul * The transmit end of channel interrupt. The adapter triggers this 162736270Swpaul * interrupt to tell us it hit the end of the current transmit list. 162836270Swpaul * 162936270Swpaul * A note about this: it's possible for a condition to arise where 163036270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 163136270Swpaul * You have to avoid this since the chip expects things to go in a 163236270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 163336270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 163436270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 163536270Swpaul * interrupt should be received and acknowledged before any more frames 163636270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 163736270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 163836270Swpaul * it could attempt to issue a transmit command prematurely. 163936270Swpaul * 164036270Swpaul * To guard against this, tl_start() will only issue transmit commands 164136270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 164236270Swpaul * can set this flag once tl_start() has cleared it. 164336270Swpaul */ 1644102336Salfredstatic int 1645102336Salfredtl_intvec_txeoc(xsc, type) 164636270Swpaul void *xsc; 164736270Swpaul u_int32_t type; 164836270Swpaul{ 164936270Swpaul struct tl_softc *sc; 165036270Swpaul struct ifnet *ifp; 165136270Swpaul u_int32_t cmd; 165236270Swpaul 165336270Swpaul sc = xsc; 165436270Swpaul ifp = &sc->arpcom.ac_if; 165536270Swpaul 165636270Swpaul /* Clear the timeout timer. */ 165736270Swpaul ifp->if_timer = 0; 165836270Swpaul 165936270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 166036270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 166136270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 166236270Swpaul sc->tl_txeoc = 1; 166336270Swpaul } else { 166436270Swpaul sc->tl_txeoc = 0; 166536270Swpaul /* First we have to ack the EOC interrupt. */ 166639583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 166736270Swpaul /* Then load the address of the next TX list. */ 166839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 166951439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 167036270Swpaul /* Restart TX channel. */ 167139583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 167236270Swpaul cmd &= ~TL_CMD_RT; 167336270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 167439583Swpaul CMD_PUT(sc, cmd); 167536270Swpaul return(0); 167636270Swpaul } 167736270Swpaul 167836270Swpaul return(1); 167936270Swpaul} 168036270Swpaul 1681102336Salfredstatic int 1682102336Salfredtl_intvec_adchk(xsc, type) 168336270Swpaul void *xsc; 168436270Swpaul u_int32_t type; 168536270Swpaul{ 168636270Swpaul struct tl_softc *sc; 168736270Swpaul 168836270Swpaul sc = xsc; 168936270Swpaul 169039627Swpaul if (type) 1691105599Sbrooks if_printf(&sc->arpcom.ac_if, "adapter check: %x\n", 169241656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 169336270Swpaul 169439583Swpaul tl_softreset(sc, 1); 169537626Swpaul tl_stop(sc); 169636270Swpaul tl_init(sc); 169739583Swpaul CMD_SET(sc, TL_CMD_INTSON); 169836270Swpaul 169936270Swpaul return(0); 170036270Swpaul} 170136270Swpaul 1702102336Salfredstatic int 1703102336Salfredtl_intvec_netsts(xsc, type) 170436270Swpaul void *xsc; 170536270Swpaul u_int32_t type; 170636270Swpaul{ 170736270Swpaul struct tl_softc *sc; 170836270Swpaul u_int16_t netsts; 170936270Swpaul 171036270Swpaul sc = xsc; 171136270Swpaul 171239583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 171339583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 171436270Swpaul 1715105599Sbrooks if_printf(&sc->arpcom.ac_if, "network status: %x\n", netsts); 171636270Swpaul 171736270Swpaul return(1); 171836270Swpaul} 171936270Swpaul 1720102336Salfredstatic void 1721102336Salfredtl_intr(xsc) 172239583Swpaul void *xsc; 172336270Swpaul{ 172436270Swpaul struct tl_softc *sc; 172536270Swpaul struct ifnet *ifp; 172636270Swpaul int r = 0; 172736270Swpaul u_int32_t type = 0; 172836270Swpaul u_int16_t ints = 0; 172936270Swpaul u_int8_t ivec = 0; 173036270Swpaul 173139583Swpaul sc = xsc; 173267087Swpaul TL_LOCK(sc); 173336270Swpaul 173436270Swpaul /* Disable interrupts */ 173539583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 173639583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 173736270Swpaul type = (ints << 16) & 0xFFFF0000; 173836270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 173936270Swpaul ints = (ints & TL_INT_MASK) >> 2; 174036270Swpaul 174136270Swpaul ifp = &sc->arpcom.ac_if; 174236270Swpaul 174336270Swpaul switch(ints) { 174436270Swpaul case (TL_INTR_INVALID): 174539583Swpaul#ifdef DIAGNOSTIC 1746105599Sbrooks if_printf(ifp, "got an invalid interrupt!\n"); 174739583Swpaul#endif 174839583Swpaul /* Re-enable interrupts but don't ack this one. */ 174939583Swpaul CMD_PUT(sc, type); 175039583Swpaul r = 0; 175136270Swpaul break; 175236270Swpaul case (TL_INTR_TXEOF): 175336270Swpaul r = tl_intvec_txeof((void *)sc, type); 175436270Swpaul break; 175536270Swpaul case (TL_INTR_TXEOC): 175636270Swpaul r = tl_intvec_txeoc((void *)sc, type); 175736270Swpaul break; 175836270Swpaul case (TL_INTR_STATOFLOW): 175939583Swpaul tl_stats_update(sc); 176039583Swpaul r = 1; 176136270Swpaul break; 176236270Swpaul case (TL_INTR_RXEOF): 176336270Swpaul r = tl_intvec_rxeof((void *)sc, type); 176436270Swpaul break; 176536270Swpaul case (TL_INTR_DUMMY): 1766105599Sbrooks if_printf(ifp, "got a dummy interrupt\n"); 176739583Swpaul r = 1; 176836270Swpaul break; 176936270Swpaul case (TL_INTR_ADCHK): 177036270Swpaul if (ivec) 177136270Swpaul r = tl_intvec_adchk((void *)sc, type); 177236270Swpaul else 177336270Swpaul r = tl_intvec_netsts((void *)sc, type); 177436270Swpaul break; 177536270Swpaul case (TL_INTR_RXEOC): 177636270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 177736270Swpaul break; 177836270Swpaul default: 1779105599Sbrooks if_printf(ifp, "bogus interrupt type\n"); 178036270Swpaul break; 178136270Swpaul } 178236270Swpaul 178336270Swpaul /* Re-enable interrupts */ 178437626Swpaul if (r) { 178539583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 178637626Swpaul } 178736270Swpaul 178837626Swpaul if (ifp->if_snd.ifq_head != NULL) 178937626Swpaul tl_start(ifp); 179037626Swpaul 179167087Swpaul TL_UNLOCK(sc); 179267087Swpaul 179336270Swpaul return; 179436270Swpaul} 179536270Swpaul 1796102336Salfredstatic void 1797102336Salfredtl_stats_update(xsc) 179836270Swpaul void *xsc; 179936270Swpaul{ 180036270Swpaul struct tl_softc *sc; 180136270Swpaul struct ifnet *ifp; 180236270Swpaul struct tl_stats tl_stats; 180350462Swpaul struct mii_data *mii; 180436270Swpaul u_int32_t *p; 180536270Swpaul 180636270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 180736270Swpaul 180836270Swpaul sc = xsc; 180967087Swpaul TL_LOCK(sc); 181036270Swpaul ifp = &sc->arpcom.ac_if; 181136270Swpaul 181236270Swpaul p = (u_int32_t *)&tl_stats; 181336270Swpaul 181439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 181539583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181639583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 182036270Swpaul 182136270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 182236270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 182336270Swpaul tl_stats.tl_tx_multi_collision; 182436270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 182536270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 182636270Swpaul tl_rx_overrun(tl_stats); 182736270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 182836270Swpaul 182951439Swpaul if (tl_tx_underrun(tl_stats)) { 183051439Swpaul u_int8_t tx_thresh; 183151439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 183251439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 183351439Swpaul tx_thresh >>= 4; 183451439Swpaul tx_thresh++; 1835105599Sbrooks if_printf(ifp, "tx underrun -- increasing " 1836105599Sbrooks "tx threshold to %d bytes\n", 183751439Swpaul (64 * (tx_thresh * 4))); 183851439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 183951439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 184051439Swpaul } 184151439Swpaul } 184251439Swpaul 184336270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 184436302Swpaul 184550462Swpaul if (!sc->tl_bitrate) { 184650462Swpaul mii = device_get_softc(sc->tl_miibus); 184750462Swpaul mii_tick(mii); 184850462Swpaul } 184950462Swpaul 185067087Swpaul TL_UNLOCK(sc); 185148992Swpaul 185236302Swpaul return; 185336270Swpaul} 185436270Swpaul 185536270Swpaul/* 185636270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 185736270Swpaul * pointers to the fragment pointers. 185836270Swpaul */ 1859102336Salfredstatic int 1860102336Salfredtl_encap(sc, c, m_head) 186136270Swpaul struct tl_softc *sc; 186236270Swpaul struct tl_chain *c; 186336270Swpaul struct mbuf *m_head; 186436270Swpaul{ 186536270Swpaul int frag = 0; 186636270Swpaul struct tl_frag *f = NULL; 186736270Swpaul int total_len; 186836270Swpaul struct mbuf *m; 1869105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 187036270Swpaul 187136270Swpaul /* 187236270Swpaul * Start packing the mbufs in this chain into 187336270Swpaul * the fragment pointers. Stop when we run out 187436270Swpaul * of fragments or hit the end of the mbuf chain. 187536270Swpaul */ 187636270Swpaul m = m_head; 187736270Swpaul total_len = 0; 187836270Swpaul 187936270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 188036270Swpaul if (m->m_len != 0) { 188136270Swpaul if (frag == TL_MAXFRAGS) 188236270Swpaul break; 188336270Swpaul total_len+= m->m_len; 188436270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 188536270Swpaul vtophys(mtod(m, vm_offset_t)); 188636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 188736270Swpaul frag++; 188836270Swpaul } 188936270Swpaul } 189036270Swpaul 189136270Swpaul /* 189236270Swpaul * Handle special cases. 189336270Swpaul * Special case #1: we used up all 10 fragments, but 189436270Swpaul * we have more mbufs left in the chain. Copy the 189536270Swpaul * data into an mbuf cluster. Note that we don't 189636270Swpaul * bother clearing the values in the other fragment 189736270Swpaul * pointers/counters; it wouldn't gain us anything, 189836270Swpaul * and would waste cycles. 189936270Swpaul */ 190036270Swpaul if (m != NULL) { 190136270Swpaul struct mbuf *m_new = NULL; 190236270Swpaul 1903111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 190436270Swpaul if (m_new == NULL) { 1905105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 190636270Swpaul return(1); 190736270Swpaul } 190836270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 1909111119Simp MCLGET(m_new, M_DONTWAIT); 191036270Swpaul if (!(m_new->m_flags & M_EXT)) { 191136270Swpaul m_freem(m_new); 1912105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 191336270Swpaul return(1); 191436270Swpaul } 191536270Swpaul } 191636270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 191736270Swpaul mtod(m_new, caddr_t)); 191836270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 191936270Swpaul m_freem(m_head); 192036270Swpaul m_head = m_new; 192136270Swpaul f = &c->tl_ptr->tl_frag[0]; 192236270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 192336270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 192436270Swpaul frag = 1; 192536270Swpaul } 192636270Swpaul 192736270Swpaul /* 192836270Swpaul * Special case #2: the frame is smaller than the minimum 192936270Swpaul * frame size. We have to pad it to make the chip happy. 193036270Swpaul */ 193136270Swpaul if (total_len < TL_MIN_FRAMELEN) { 193236270Swpaul if (frag == TL_MAXFRAGS) 1933105599Sbrooks if_printf(ifp, 1934105599Sbrooks "all frags filled but frame still to small!\n"); 193536270Swpaul f = &c->tl_ptr->tl_frag[frag]; 193636270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 193736270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 193836270Swpaul total_len += f->tlist_dcnt; 193936270Swpaul frag++; 194036270Swpaul } 194136270Swpaul 194236270Swpaul c->tl_mbuf = m_head; 194336270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 194436270Swpaul c->tl_ptr->tlist_frsize = total_len; 194536270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 194636270Swpaul c->tl_ptr->tlist_fptr = 0; 194736270Swpaul 194836270Swpaul return(0); 194936270Swpaul} 195036270Swpaul 195136270Swpaul/* 195236270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 195336270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 195436270Swpaul * copy of the pointers since the transmit list fragment pointers are 195536270Swpaul * physical addresses. 195636270Swpaul */ 1957102336Salfredstatic void 1958102336Salfredtl_start(ifp) 195936270Swpaul struct ifnet *ifp; 196036270Swpaul{ 196136270Swpaul struct tl_softc *sc; 196236270Swpaul struct mbuf *m_head = NULL; 196336270Swpaul u_int32_t cmd; 196436270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 196536270Swpaul 196636270Swpaul sc = ifp->if_softc; 196767087Swpaul TL_LOCK(sc); 196836270Swpaul 196936270Swpaul /* 197036270Swpaul * Check for an available queue slot. If there are none, 197136270Swpaul * punt. 197236270Swpaul */ 197336270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 197436270Swpaul ifp->if_flags |= IFF_OACTIVE; 197567087Swpaul TL_UNLOCK(sc); 197636270Swpaul return; 197736270Swpaul } 197836270Swpaul 197936270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 198036270Swpaul 198136270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 198236270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 198336270Swpaul if (m_head == NULL) 198436270Swpaul break; 198536270Swpaul 198636270Swpaul /* Pick a chain member off the free list. */ 198736270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 198836270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 198936270Swpaul 199036270Swpaul cur_tx->tl_next = NULL; 199136270Swpaul 199236270Swpaul /* Pack the data into the list. */ 199336270Swpaul tl_encap(sc, cur_tx, m_head); 199436270Swpaul 199536270Swpaul /* Chain it together */ 199636270Swpaul if (prev != NULL) { 199736270Swpaul prev->tl_next = cur_tx; 199836270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 199936270Swpaul } 200036270Swpaul prev = cur_tx; 200136270Swpaul 200236270Swpaul /* 200336270Swpaul * If there's a BPF listener, bounce a copy of this frame 200436270Swpaul * to him. 200536270Swpaul */ 2006106936Ssam BPF_MTAP(ifp, cur_tx->tl_mbuf); 200736270Swpaul } 200836270Swpaul 200936270Swpaul /* 201041526Swpaul * If there are no packets queued, bail. 201141526Swpaul */ 201267087Swpaul if (cur_tx == NULL) { 201367087Swpaul TL_UNLOCK(sc); 201441526Swpaul return; 201567087Swpaul } 201641526Swpaul 201741526Swpaul /* 201836270Swpaul * That's all we can stands, we can't stands no more. 201936270Swpaul * If there are no other transfers pending, then issue the 202036270Swpaul * TX GO command to the adapter to start things moving. 202136270Swpaul * Otherwise, just leave the data in the queue and let 202236270Swpaul * the EOF/EOC interrupt handler send. 202336270Swpaul */ 202436270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 202536270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 202636270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 202739583Swpaul 202836270Swpaul if (sc->tl_txeoc) { 202936270Swpaul sc->tl_txeoc = 0; 203039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 203139583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 203236270Swpaul cmd &= ~TL_CMD_RT; 203336270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 203439583Swpaul CMD_PUT(sc, cmd); 203536270Swpaul } 203636270Swpaul } else { 203736270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 203842146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 203936270Swpaul } 204036270Swpaul 204136270Swpaul /* 204236270Swpaul * Set a timeout in case the chip goes out to lunch. 204336270Swpaul */ 204436270Swpaul ifp->if_timer = 5; 204567087Swpaul TL_UNLOCK(sc); 204636270Swpaul 204736270Swpaul return; 204836270Swpaul} 204936270Swpaul 2050102336Salfredstatic void 2051102336Salfredtl_init(xsc) 205236270Swpaul void *xsc; 205336270Swpaul{ 205436270Swpaul struct tl_softc *sc = xsc; 205536270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 205650462Swpaul struct mii_data *mii; 205736270Swpaul 205867087Swpaul TL_LOCK(sc); 205936270Swpaul 206036270Swpaul ifp = &sc->arpcom.ac_if; 206136270Swpaul 206236270Swpaul /* 206336270Swpaul * Cancel pending I/O. 206436270Swpaul */ 206536270Swpaul tl_stop(sc); 206636270Swpaul 206751439Swpaul /* Initialize TX FIFO threshold */ 206851439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 206951439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 207051439Swpaul 207151439Swpaul /* Set PCI burst size */ 207251439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 207351439Swpaul 207436270Swpaul /* 207536270Swpaul * Set 'capture all frames' bit for promiscuous mode. 207636270Swpaul */ 207739583Swpaul if (ifp->if_flags & IFF_PROMISC) 207839583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 207939583Swpaul else 208039583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 208136270Swpaul 208236270Swpaul /* 208336270Swpaul * Set capture broadcast bit to capture broadcast frames. 208436270Swpaul */ 208539583Swpaul if (ifp->if_flags & IFF_BROADCAST) 208639583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 208739583Swpaul else 208839583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 208936270Swpaul 209050468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 209150468Swpaul 209236270Swpaul /* Init our MAC address */ 209341656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 209436270Swpaul 209539583Swpaul /* Init multicast filter, if needed. */ 209639583Swpaul tl_setmulti(sc); 209739583Swpaul 209836270Swpaul /* Init circular RX list. */ 209939583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2100105599Sbrooks if_printf(ifp, 2101105599Sbrooks "initialization failed: no memory for rx buffers\n"); 210239583Swpaul tl_stop(sc); 210367087Swpaul TL_UNLOCK(sc); 210436270Swpaul return; 210536270Swpaul } 210636270Swpaul 210736270Swpaul /* Init TX pointers. */ 210836270Swpaul tl_list_tx_init(sc); 210936270Swpaul 211039583Swpaul /* Enable PCI interrupts. */ 211139583Swpaul CMD_SET(sc, TL_CMD_INTSON); 211236270Swpaul 211336270Swpaul /* Load the address of the rx list */ 211439583Swpaul CMD_SET(sc, TL_CMD_RT); 211539583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 211636270Swpaul 211750462Swpaul if (!sc->tl_bitrate) { 211850462Swpaul if (sc->tl_miibus != NULL) { 211950462Swpaul mii = device_get_softc(sc->tl_miibus); 212050462Swpaul mii_mediachg(mii); 212150462Swpaul } 212250462Swpaul } 212338030Swpaul 212436270Swpaul /* Send the RX go command */ 212550468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 212636270Swpaul 212736270Swpaul ifp->if_flags |= IFF_RUNNING; 212836270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 212936270Swpaul 213036270Swpaul /* Start the stats update counter */ 213136270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 213267087Swpaul TL_UNLOCK(sc); 213336270Swpaul 213436270Swpaul return; 213536270Swpaul} 213636270Swpaul 213736270Swpaul/* 213836270Swpaul * Set media options. 213936270Swpaul */ 2140102336Salfredstatic int 2141102336Salfredtl_ifmedia_upd(ifp) 214236270Swpaul struct ifnet *ifp; 214336270Swpaul{ 214436270Swpaul struct tl_softc *sc; 214550462Swpaul struct mii_data *mii = NULL; 214636270Swpaul 214736270Swpaul sc = ifp->if_softc; 214836270Swpaul 214950462Swpaul if (sc->tl_bitrate) 215050462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 215150462Swpaul else { 215250462Swpaul mii = device_get_softc(sc->tl_miibus); 215350462Swpaul mii_mediachg(mii); 215450462Swpaul } 215536270Swpaul 215636270Swpaul return(0); 215736270Swpaul} 215836270Swpaul 215936270Swpaul/* 216036270Swpaul * Report current media status. 216136270Swpaul */ 2162102336Salfredstatic void 2163102336Salfredtl_ifmedia_sts(ifp, ifmr) 216436270Swpaul struct ifnet *ifp; 216536270Swpaul struct ifmediareq *ifmr; 216636270Swpaul{ 216736270Swpaul struct tl_softc *sc; 216850462Swpaul struct mii_data *mii; 216936270Swpaul 217036270Swpaul sc = ifp->if_softc; 217136270Swpaul 217236270Swpaul ifmr->ifm_active = IFM_ETHER; 217336270Swpaul 217445155Swpaul if (sc->tl_bitrate) { 217545155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 217645155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 217745155Swpaul else 217845155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 217945155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 218045155Swpaul ifmr->ifm_active |= IFM_HDX; 218145155Swpaul else 218245155Swpaul ifmr->ifm_active |= IFM_FDX; 218345155Swpaul return; 218436270Swpaul } else { 218550462Swpaul mii = device_get_softc(sc->tl_miibus); 218650462Swpaul mii_pollstat(mii); 218750462Swpaul ifmr->ifm_active = mii->mii_media_active; 218850462Swpaul ifmr->ifm_status = mii->mii_media_status; 218936270Swpaul } 219036270Swpaul 219136270Swpaul return; 219236270Swpaul} 219336270Swpaul 2194102336Salfredstatic int 2195102336Salfredtl_ioctl(ifp, command, data) 219636270Swpaul struct ifnet *ifp; 219736735Sdfr u_long command; 219836270Swpaul caddr_t data; 219936270Swpaul{ 220036270Swpaul struct tl_softc *sc = ifp->if_softc; 220136270Swpaul struct ifreq *ifr = (struct ifreq *) data; 220236270Swpaul int s, error = 0; 220336270Swpaul 220436270Swpaul s = splimp(); 220536270Swpaul 220636270Swpaul switch(command) { 220736270Swpaul case SIOCSIFFLAGS: 220836270Swpaul if (ifp->if_flags & IFF_UP) { 220950462Swpaul if (ifp->if_flags & IFF_RUNNING && 221050462Swpaul ifp->if_flags & IFF_PROMISC && 221150462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 221250462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 221350462Swpaul tl_setmulti(sc); 221450462Swpaul } else if (ifp->if_flags & IFF_RUNNING && 221550462Swpaul !(ifp->if_flags & IFF_PROMISC) && 221650462Swpaul sc->tl_if_flags & IFF_PROMISC) { 221750462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 221850462Swpaul tl_setmulti(sc); 221950462Swpaul } else 222050462Swpaul tl_init(sc); 222136270Swpaul } else { 222236270Swpaul if (ifp->if_flags & IFF_RUNNING) { 222336270Swpaul tl_stop(sc); 222436270Swpaul } 222536270Swpaul } 222650462Swpaul sc->tl_if_flags = ifp->if_flags; 222736270Swpaul error = 0; 222836270Swpaul break; 222936270Swpaul case SIOCADDMULTI: 223036270Swpaul case SIOCDELMULTI: 223136270Swpaul tl_setmulti(sc); 223236270Swpaul error = 0; 223336270Swpaul break; 223436270Swpaul case SIOCSIFMEDIA: 223536270Swpaul case SIOCGIFMEDIA: 223650462Swpaul if (sc->tl_bitrate) 223750462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 223850462Swpaul else { 223950462Swpaul struct mii_data *mii; 224050462Swpaul mii = device_get_softc(sc->tl_miibus); 224150462Swpaul error = ifmedia_ioctl(ifp, ifr, 224250462Swpaul &mii->mii_media, command); 224350462Swpaul } 224436270Swpaul break; 224536270Swpaul default: 2246106936Ssam error = ether_ioctl(ifp, command, data); 224736270Swpaul break; 224836270Swpaul } 224936270Swpaul 225036270Swpaul (void)splx(s); 225136270Swpaul 225236270Swpaul return(error); 225336270Swpaul} 225436270Swpaul 2255102336Salfredstatic void 2256102336Salfredtl_watchdog(ifp) 225736270Swpaul struct ifnet *ifp; 225836270Swpaul{ 225936270Swpaul struct tl_softc *sc; 226036270Swpaul 226136270Swpaul sc = ifp->if_softc; 226236270Swpaul 2263105599Sbrooks if_printf(ifp, "device timeout\n"); 226436270Swpaul 226536270Swpaul ifp->if_oerrors++; 226636270Swpaul 226750468Swpaul tl_softreset(sc, 1); 226836270Swpaul tl_init(sc); 226936270Swpaul 227036270Swpaul return; 227136270Swpaul} 227236270Swpaul 227336270Swpaul/* 227436270Swpaul * Stop the adapter and free any mbufs allocated to the 227536270Swpaul * RX and TX lists. 227636270Swpaul */ 2277102336Salfredstatic void 2278102336Salfredtl_stop(sc) 227936270Swpaul struct tl_softc *sc; 228036270Swpaul{ 228136270Swpaul register int i; 228236270Swpaul struct ifnet *ifp; 228336270Swpaul 228467087Swpaul TL_LOCK(sc); 228567087Swpaul 228636270Swpaul ifp = &sc->arpcom.ac_if; 228736270Swpaul 228836270Swpaul /* Stop the stats updater. */ 228936270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 229036270Swpaul 229136270Swpaul /* Stop the transmitter */ 229239583Swpaul CMD_CLR(sc, TL_CMD_RT); 229339583Swpaul CMD_SET(sc, TL_CMD_STOP); 229439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 229536270Swpaul 229636270Swpaul /* Stop the receiver */ 229739583Swpaul CMD_SET(sc, TL_CMD_RT); 229839583Swpaul CMD_SET(sc, TL_CMD_STOP); 229939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 230036270Swpaul 230136270Swpaul /* 230236270Swpaul * Disable host interrupts. 230336270Swpaul */ 230439583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 230536270Swpaul 230636270Swpaul /* 230736270Swpaul * Clear list pointer. 230836270Swpaul */ 230939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231036270Swpaul 231136270Swpaul /* 231236270Swpaul * Free the RX lists. 231336270Swpaul */ 231436270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 231536270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 231636270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 231736270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 231836270Swpaul } 231936270Swpaul } 232036270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 232136270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 232236270Swpaul 232336270Swpaul /* 232436270Swpaul * Free the TX list buffers. 232536270Swpaul */ 232636270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 232736270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 232836270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 232936270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 233036270Swpaul } 233136270Swpaul } 233236270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 233336270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 233436270Swpaul 233536270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 233667087Swpaul TL_UNLOCK(sc); 233736270Swpaul 233836270Swpaul return; 233936270Swpaul} 234036270Swpaul 234136270Swpaul/* 234236270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 234336270Swpaul * get confused by errant DMAs when rebooting. 234436270Swpaul */ 2345102336Salfredstatic void 2346102336Salfredtl_shutdown(dev) 234748992Swpaul device_t dev; 234836270Swpaul{ 234939583Swpaul struct tl_softc *sc; 235036270Swpaul 235148992Swpaul sc = device_get_softc(dev); 235236270Swpaul 235339583Swpaul tl_stop(sc); 235436270Swpaul 235536270Swpaul return; 235636270Swpaul} 2357