if_tl.c revision 105599
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 * 3250477Speter * $FreeBSD: head/sys/pci/if_tl.c 105599 2002-10-21 02:54:50Z brooks $ 3336270Swpaul */ 3436270Swpaul 3536270Swpaul/* 3636270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3736270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3836270Swpaul * the National Semiconductor DP83840A physical interface and the 3936270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 4036270Swpaul * 4139583Swpaul * Written using the following four documents: 4236270Swpaul * 4336270Swpaul * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) 4436270Swpaul * National Semiconductor DP83840A data sheet (www.national.com) 4536270Swpaul * Microchip Technology 24C02C data sheet (www.microchip.com) 4639583Swpaul * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) 4736270Swpaul * 4836270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4936270Swpaul * Electrical Engineering Department 5036270Swpaul * Columbia University, New York City 5136270Swpaul */ 5236270Swpaul 5336270Swpaul/* 5436270Swpaul * Some notes about the ThunderLAN: 5536270Swpaul * 5636270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5736270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5839583Swpaul * independent interface (MII) bus. The MII allows the ThunderLAN chip to 5936270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 6036270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 6136270Swpaul * to act as a complete ethernet interface. 6236270Swpaul * 6336270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6436270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6536270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6639583Swpaul * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters 6739583Swpaul * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in 6839583Swpaul * concert with the ThunderLAN's internal PHY to provide full 10/100 6939583Swpaul * support. This is cheaper than using a standalone external PHY for both 7039583Swpaul * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. 7139583Swpaul * A serial EEPROM is also attached to the ThunderLAN chip to provide 7239583Swpaul * power-up default register settings and for storing the adapter's 7339583Swpaul * station address. Although not supported by this driver, the ThunderLAN 7439583Swpaul * chip can also be connected to token ring PHYs. 7536270Swpaul * 7636270Swpaul * The ThunderLAN has a set of registers which can be used to issue 7739583Swpaul * commands, acknowledge interrupts, and to manipulate other internal 7836270Swpaul * registers on its DIO bus. The primary registers can be accessed 7936270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 8036270Swpaul * depending on how the card is configured during the PCI probing 8136270Swpaul * phase. It is even possible to have both PIO and memory mapped 8236270Swpaul * access turned on at the same time. 8336270Swpaul * 8436270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 8536270Swpaul * using frame 'lists.' A list structure looks more or less like this: 8636270Swpaul * 8736270Swpaul * struct tl_frag { 8836270Swpaul * u_int32_t fragment_address; 8936270Swpaul * u_int32_t fragment_size; 9036270Swpaul * }; 9136270Swpaul * struct tl_list { 9236270Swpaul * u_int32_t forward_pointer; 9336270Swpaul * u_int16_t cstat; 9436270Swpaul * u_int16_t frame_size; 9536270Swpaul * struct tl_frag fragments[10]; 9636270Swpaul * }; 9736270Swpaul * 9836270Swpaul * The forward pointer in the list header can be either a 0 or the address 9936270Swpaul * of another list, which allows several lists to be linked together. Each 10036270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 10136270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 10236270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 10336270Swpaul * addresses are physical memory addresses, not virtual. Note also that 10436270Swpaul * a single ethernet frame can not span lists: if the host wants to 10536270Swpaul * transmit a frame and the frame data is split up over more than 10 10636270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 10736270Swpaul * 10836270Swpaul * To receive frames, the driver sets up a number of lists and populates 10936270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 11036270Swpaul * When a frame is received, the chip will DMA it into the memory regions 11136270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 11236270Swpaul * frame interrupt' when done. The driver may choose to use only one 11336270Swpaul * fragment per list; this may result is slighltly less efficient use 11436270Swpaul * of memory in exchange for improving performance. 11536270Swpaul * 11636270Swpaul * To transmit frames, the driver again sets up lists and fragment 11736270Swpaul * descriptors, only this time the buffers contain frame data that 11836270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 11936270Swpaul * has transfered the data into its on-board SRAM, it will trigger a 12036270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 12136270Swpaul * interrupt when it reaches the end of the list. 12236270Swpaul */ 12336270Swpaul 12436270Swpaul/* 12536270Swpaul * Some notes about this driver: 12636270Swpaul * 12736270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 12836270Swpaul * reception, transmission and interrupt handling. The simplest approach 12936270Swpaul * is to use one list each for transmission and reception. In this mode, 13036270Swpaul * the ThunderLAN will generate two interrupts for every received frame 13136270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 13236270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 13336270Swpaul * performance to have to handle so many interrupts. 13436270Swpaul * 13536270Swpaul * Initially I wanted to create a circular list of receive buffers so 13636270Swpaul * that the ThunderLAN chip would think there was an infinitely long 13736270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 13836270Swpaul * doesn't work correctly under heavy load: while the manual says the 13936270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 14036270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 14136270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 14236270Swpaul * is that the chip might traverse the entire circular list and then wrap 14336270Swpaul * around before you have a chance to do anything about it. Consequently, 14436270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 14536270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 14636270Swpaul * is shifted to the end of the list. This gives the appearance of an 14736270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 14836270Swpaul * the chip and allow all of the lists to be filled up. 14936270Swpaul * 15036270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 15136270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 15236270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 15336270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 15436270Swpaul * 15536270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 15636270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 15736270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 15836270Swpaul * queue several frames in one shot, and only have to handle a total 15936270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 16036270Swpaul * are transmitted. Frame transmission is done directly out of the 16136270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 16236270Swpaul * The driver simply sets up the fragment descriptors in the transmit 16336270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 16436270Swpaul * 16536270Swpaul * Note that since the RX and TX lists themselves are always used 16636270Swpaul * only by the driver, the are malloc()ed once at driver initialization 16736270Swpaul * time and never free()ed. 16836270Swpaul * 16936270Swpaul * Also, in order to remain as platform independent as possible, this 17036270Swpaul * driver uses memory mapped register access to manipulate the card 17136270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 17236270Swpaul * (and related) instructions which are specific to the i386 platform. 17336270Swpaul * 17436270Swpaul * Using these techniques, this driver achieves very high performance 17536270Swpaul * by minimizing the amount of interrupts generated during large 17636270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 17736270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 17836270Swpaul * itself thereby reducing the load on the host CPU. 17936270Swpaul */ 18036270Swpaul 18136270Swpaul#include <sys/param.h> 18236270Swpaul#include <sys/systm.h> 18336270Swpaul#include <sys/sockio.h> 18436270Swpaul#include <sys/mbuf.h> 18536270Swpaul#include <sys/malloc.h> 18636270Swpaul#include <sys/kernel.h> 18736270Swpaul#include <sys/socket.h> 18836270Swpaul 18936270Swpaul#include <net/if.h> 19036270Swpaul#include <net/if_arp.h> 19136270Swpaul#include <net/ethernet.h> 19236270Swpaul#include <net/if_dl.h> 19336270Swpaul#include <net/if_media.h> 19436270Swpaul 19536270Swpaul#include <net/bpf.h> 19636270Swpaul 19736270Swpaul#include <vm/vm.h> /* for vtophys */ 19836270Swpaul#include <vm/pmap.h> /* for vtophys */ 19945155Swpaul#include <machine/bus_memio.h> 20045155Swpaul#include <machine/bus_pio.h> 20145155Swpaul#include <machine/bus.h> 20248992Swpaul#include <machine/resource.h> 20348992Swpaul#include <sys/bus.h> 20448992Swpaul#include <sys/rman.h> 20536270Swpaul 20650462Swpaul#include <dev/mii/mii.h> 20750462Swpaul#include <dev/mii/miivar.h> 20850462Swpaul 20936270Swpaul#include <pci/pcireg.h> 21036270Swpaul#include <pci/pcivar.h> 21136270Swpaul 21239957Swpaul/* 21339957Swpaul * Default to using PIO register access mode to pacify certain 21439957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21539957Swpaul * don't seem to handle memory mapped mode properly. 21639957Swpaul */ 21739957Swpaul#define TL_USEIOSPACE 21839957Swpaul 21936270Swpaul#include <pci/if_tlreg.h> 22036270Swpaul 22159758SpeterMODULE_DEPEND(tl, miibus, 1, 1, 1); 22259758Speter 22351089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 22450462Swpaul#include "miibus_if.h" 22550462Swpaul 22641591Sarchie#if !defined(lint) 22741591Sarchiestatic const char rcsid[] = 22850477Speter "$FreeBSD: head/sys/pci/if_tl.c 105599 2002-10-21 02:54:50Z brooks $"; 22936270Swpaul#endif 23036270Swpaul 23136270Swpaul/* 23236270Swpaul * Various supported device vendors/types and their names. 23336270Swpaul */ 23436270Swpaul 23536270Swpaulstatic struct tl_type tl_devs[] = { 23636270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23736270Swpaul "Texas Instruments ThunderLAN" }, 23836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23936270Swpaul "Compaq Netelligent 10" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 24136270Swpaul "Compaq Netelligent 10/100" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24336270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24536270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24736270Swpaul "Compaq NetFlex-3/P Integrated" }, 24836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24936270Swpaul "Compaq NetFlex-3/P" }, 25036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 25136270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25237626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25337626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25437626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25537626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25637626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25737626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25837626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25937626Swpaul "Olicom OC-2183/2185" }, 26037626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 26137626Swpaul "Olicom OC-2325" }, 26237626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26337626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26436270Swpaul { 0, 0, NULL } 26536270Swpaul}; 26636270Swpaul 26792739Salfredstatic int tl_probe (device_t); 26892739Salfredstatic int tl_attach (device_t); 26992739Salfredstatic int tl_detach (device_t); 27092739Salfredstatic int tl_intvec_rxeoc (void *, u_int32_t); 27192739Salfredstatic int tl_intvec_txeoc (void *, u_int32_t); 27292739Salfredstatic int tl_intvec_txeof (void *, u_int32_t); 27392739Salfredstatic int tl_intvec_rxeof (void *, u_int32_t); 27492739Salfredstatic int tl_intvec_adchk (void *, u_int32_t); 27592739Salfredstatic int tl_intvec_netsts (void *, u_int32_t); 27636270Swpaul 27792739Salfredstatic int tl_newbuf (struct tl_softc *, struct tl_chain_onefrag *); 27892739Salfredstatic void tl_stats_update (void *); 27992739Salfredstatic int tl_encap (struct tl_softc *, struct tl_chain *, 28092739Salfred struct mbuf *); 28136270Swpaul 28292739Salfredstatic void tl_intr (void *); 28392739Salfredstatic void tl_start (struct ifnet *); 28492739Salfredstatic int tl_ioctl (struct ifnet *, u_long, caddr_t); 28592739Salfredstatic void tl_init (void *); 28692739Salfredstatic void tl_stop (struct tl_softc *); 28792739Salfredstatic void tl_watchdog (struct ifnet *); 28892739Salfredstatic void tl_shutdown (device_t); 28992739Salfredstatic int tl_ifmedia_upd (struct ifnet *); 29092739Salfredstatic void tl_ifmedia_sts (struct ifnet *, struct ifmediareq *); 29136270Swpaul 29292739Salfredstatic u_int8_t tl_eeprom_putbyte (struct tl_softc *, int); 29392739Salfredstatic u_int8_t tl_eeprom_getbyte (struct tl_softc *, int, u_int8_t *); 29492739Salfredstatic int tl_read_eeprom (struct tl_softc *, caddr_t, int, int); 29536270Swpaul 29692739Salfredstatic void tl_mii_sync (struct tl_softc *); 29792739Salfredstatic void tl_mii_send (struct tl_softc *, u_int32_t, int); 29892739Salfredstatic int tl_mii_readreg (struct tl_softc *, struct tl_mii_frame *); 29992739Salfredstatic int tl_mii_writereg (struct tl_softc *, struct tl_mii_frame *); 30092739Salfredstatic int tl_miibus_readreg (device_t, int, int); 30192739Salfredstatic int tl_miibus_writereg (device_t, int, int, int); 30292739Salfredstatic void tl_miibus_statchg (device_t); 30336270Swpaul 30492739Salfredstatic void tl_setmode (struct tl_softc *, int); 30592739Salfredstatic int tl_calchash (caddr_t); 30692739Salfredstatic void tl_setmulti (struct tl_softc *); 30792739Salfredstatic void tl_setfilt (struct tl_softc *, caddr_t, int); 30892739Salfredstatic void tl_softreset (struct tl_softc *, int); 30992739Salfredstatic void tl_hardreset (device_t); 31092739Salfredstatic int tl_list_rx_init (struct tl_softc *); 31192739Salfredstatic int tl_list_tx_init (struct tl_softc *); 31236270Swpaul 31392739Salfredstatic u_int8_t tl_dio_read8 (struct tl_softc *, int); 31492739Salfredstatic u_int16_t tl_dio_read16 (struct tl_softc *, int); 31592739Salfredstatic u_int32_t tl_dio_read32 (struct tl_softc *, int); 31692739Salfredstatic void tl_dio_write8 (struct tl_softc *, int, int); 31792739Salfredstatic void tl_dio_write16 (struct tl_softc *, int, int); 31892739Salfredstatic void tl_dio_write32 (struct tl_softc *, int, int); 31992739Salfredstatic void tl_dio_setbit (struct tl_softc *, int, int); 32092739Salfredstatic void tl_dio_clrbit (struct tl_softc *, int, int); 32192739Salfredstatic void tl_dio_setbit16 (struct tl_softc *, int, int); 32292739Salfredstatic void tl_dio_clrbit16 (struct tl_softc *, int, int); 32339583Swpaul 32449010Swpaul#ifdef TL_USEIOSPACE 32549010Swpaul#define TL_RES SYS_RES_IOPORT 32649010Swpaul#define TL_RID TL_PCI_LOIO 32749010Swpaul#else 32849010Swpaul#define TL_RES SYS_RES_MEMORY 32949010Swpaul#define TL_RID TL_PCI_LOMEM 33049010Swpaul#endif 33149010Swpaul 33248992Swpaulstatic device_method_t tl_methods[] = { 33348992Swpaul /* Device interface */ 33448992Swpaul DEVMETHOD(device_probe, tl_probe), 33548992Swpaul DEVMETHOD(device_attach, tl_attach), 33648992Swpaul DEVMETHOD(device_detach, tl_detach), 33748992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33850462Swpaul 33950462Swpaul /* bus interface */ 34050462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 34150462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 34250462Swpaul 34350462Swpaul /* MII interface */ 34450462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34550462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34650462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34750462Swpaul 34848992Swpaul { 0, 0 } 34948992Swpaul}; 35048992Swpaul 35148992Swpaulstatic driver_t tl_driver = { 35251455Swpaul "tl", 35348992Swpaul tl_methods, 35448992Swpaul sizeof(struct tl_softc) 35548992Swpaul}; 35648992Swpaul 35748992Swpaulstatic devclass_t tl_devclass; 35848992Swpaul 35951533SwpaulDRIVER_MODULE(if_tl, pci, tl_driver, tl_devclass, 0, 0); 36051473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 36148992Swpaul 36239583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 36341656Swpaul struct tl_softc *sc; 36441656Swpaul int reg; 36539583Swpaul{ 36639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36739583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36839583Swpaul} 36939583Swpaul 37039583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 37141656Swpaul struct tl_softc *sc; 37241656Swpaul int reg; 37339583Swpaul{ 37439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37539583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37639583Swpaul} 37739583Swpaul 37839583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 37941656Swpaul struct tl_softc *sc; 38041656Swpaul int reg; 38139583Swpaul{ 38239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38339583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38439583Swpaul} 38539583Swpaul 38639583Swpaulstatic void tl_dio_write8(sc, reg, val) 38741656Swpaul struct tl_softc *sc; 38841656Swpaul int reg; 38941656Swpaul int val; 39039583Swpaul{ 39139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39239583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 39339583Swpaul return; 39439583Swpaul} 39539583Swpaul 39639583Swpaulstatic void tl_dio_write16(sc, reg, val) 39741656Swpaul struct tl_softc *sc; 39841656Swpaul int reg; 39941656Swpaul int val; 40039583Swpaul{ 40139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40239583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 40339583Swpaul return; 40439583Swpaul} 40539583Swpaul 40639583Swpaulstatic void tl_dio_write32(sc, reg, val) 40741656Swpaul struct tl_softc *sc; 40841656Swpaul int reg; 40941656Swpaul int val; 41039583Swpaul{ 41139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 41239583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 41339583Swpaul return; 41439583Swpaul} 41539583Swpaul 416102336Salfredstatic void 417102336Salfredtl_dio_setbit(sc, reg, bit) 41841656Swpaul struct tl_softc *sc; 41941656Swpaul int reg; 42041656Swpaul int bit; 42139583Swpaul{ 42239583Swpaul u_int8_t f; 42339583Swpaul 42439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42539583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42639583Swpaul f |= bit; 42739583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 42839583Swpaul 42939583Swpaul return; 43039583Swpaul} 43139583Swpaul 432102336Salfredstatic void 433102336Salfredtl_dio_clrbit(sc, reg, bit) 43441656Swpaul struct tl_softc *sc; 43541656Swpaul int reg; 43641656Swpaul int bit; 43739583Swpaul{ 43839583Swpaul u_int8_t f; 43939583Swpaul 44039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 44139583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 44239583Swpaul f &= ~bit; 44339583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44439583Swpaul 44539583Swpaul return; 44639583Swpaul} 44739583Swpaul 44839583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44941656Swpaul struct tl_softc *sc; 45041656Swpaul int reg; 45141656Swpaul int bit; 45239583Swpaul{ 45339583Swpaul u_int16_t f; 45439583Swpaul 45539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45639583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45739583Swpaul f |= bit; 45839583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45939583Swpaul 46039583Swpaul return; 46139583Swpaul} 46239583Swpaul 46339583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46441656Swpaul struct tl_softc *sc; 46541656Swpaul int reg; 46641656Swpaul int bit; 46739583Swpaul{ 46839583Swpaul u_int16_t f; 46939583Swpaul 47039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 47139583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 47239583Swpaul f &= ~bit; 47339583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47439583Swpaul 47539583Swpaul return; 47639583Swpaul} 47739583Swpaul 47836270Swpaul/* 47936270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 48036270Swpaul */ 48139583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 48239583Swpaul struct tl_softc *sc; 48341656Swpaul int byte; 48436270Swpaul{ 48536270Swpaul register int i, ack = 0; 48636270Swpaul 48736270Swpaul /* 48836270Swpaul * Make sure we're in TX mode. 48936270Swpaul */ 49039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 49136270Swpaul 49236270Swpaul /* 49336270Swpaul * Feed in each bit and stobe the clock. 49436270Swpaul */ 49536270Swpaul for (i = 0x80; i; i >>= 1) { 49636270Swpaul if (byte & i) { 49739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49836270Swpaul } else { 49939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 50036270Swpaul } 50139583Swpaul DELAY(1); 50239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50339583Swpaul DELAY(1); 50439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50536270Swpaul } 50636270Swpaul 50736270Swpaul /* 50836270Swpaul * Turn off TX mode. 50936270Swpaul */ 51039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 51136270Swpaul 51236270Swpaul /* 51336270Swpaul * Check for ack. 51436270Swpaul */ 51539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51639583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51836270Swpaul 51936270Swpaul return(ack); 52036270Swpaul} 52136270Swpaul 52236270Swpaul/* 52336270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52436270Swpaul */ 52539583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52639583Swpaul struct tl_softc *sc; 52741656Swpaul int addr; 52836270Swpaul u_int8_t *dest; 52936270Swpaul{ 53036270Swpaul register int i; 53136270Swpaul u_int8_t byte = 0; 532105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 53336270Swpaul 53439583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53539583Swpaul 53636270Swpaul EEPROM_START; 53739583Swpaul 53836270Swpaul /* 53936270Swpaul * Send write control code to EEPROM. 54036270Swpaul */ 54139583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 542105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 543105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54436270Swpaul return(1); 54539583Swpaul } 54636270Swpaul 54736270Swpaul /* 54836270Swpaul * Send address of byte we want to read. 54936270Swpaul */ 55039583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 551105599Sbrooks if_printf(ifp, "failed to send address, status: %x\n", 552105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 55336270Swpaul return(1); 55439583Swpaul } 55536270Swpaul 55636270Swpaul EEPROM_STOP; 55736270Swpaul EEPROM_START; 55836270Swpaul /* 55936270Swpaul * Send read control code to EEPROM. 56036270Swpaul */ 56139583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 562105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 563105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 56436270Swpaul return(1); 56539583Swpaul } 56636270Swpaul 56736270Swpaul /* 56836270Swpaul * Start reading bits from EEPROM. 56936270Swpaul */ 57039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 57136270Swpaul for (i = 0x80; i; i >>= 1) { 57239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57339583Swpaul DELAY(1); 57439583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57536270Swpaul byte |= i; 57639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57736501Swpaul DELAY(1); 57836270Swpaul } 57936270Swpaul 58036270Swpaul EEPROM_STOP; 58136270Swpaul 58236270Swpaul /* 58336270Swpaul * No ACK generated for read, so just return byte. 58436270Swpaul */ 58536270Swpaul 58636270Swpaul *dest = byte; 58736270Swpaul 58836270Swpaul return(0); 58936270Swpaul} 59036270Swpaul 59139583Swpaul/* 59239583Swpaul * Read a sequence of bytes from the EEPROM. 59339583Swpaul */ 594102336Salfredstatic int 595102336Salfredtl_read_eeprom(sc, dest, off, cnt) 59639583Swpaul struct tl_softc *sc; 59739583Swpaul caddr_t dest; 59839583Swpaul int off; 59939583Swpaul int cnt; 60036270Swpaul{ 60139583Swpaul int err = 0, i; 60239583Swpaul u_int8_t byte = 0; 60339583Swpaul 60439583Swpaul for (i = 0; i < cnt; i++) { 60539583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60639583Swpaul if (err) 60739583Swpaul break; 60839583Swpaul *(dest + i) = byte; 60939583Swpaul } 61039583Swpaul 61139583Swpaul return(err ? 1 : 0); 61239583Swpaul} 61339583Swpaul 614102336Salfredstatic void 615102336Salfredtl_mii_sync(sc) 61639583Swpaul struct tl_softc *sc; 61739583Swpaul{ 61836270Swpaul register int i; 61936270Swpaul 62039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 62136270Swpaul 62236270Swpaul for (i = 0; i < 32; i++) { 62339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62536270Swpaul } 62636270Swpaul 62736270Swpaul return; 62836270Swpaul} 62936270Swpaul 630102336Salfredstatic void 631102336Salfredtl_mii_send(sc, bits, cnt) 63239583Swpaul struct tl_softc *sc; 63336270Swpaul u_int32_t bits; 63436270Swpaul int cnt; 63536270Swpaul{ 63636270Swpaul int i; 63736270Swpaul 63836270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 64036270Swpaul if (bits & i) { 64139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 64236270Swpaul } else { 64339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64436270Swpaul } 64539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64636270Swpaul } 64736270Swpaul} 64836270Swpaul 649102336Salfredstatic int 650102336Salfredtl_mii_readreg(sc, frame) 65139583Swpaul struct tl_softc *sc; 65236270Swpaul struct tl_mii_frame *frame; 65336270Swpaul 65436270Swpaul{ 65567087Swpaul int i, ack; 65636270Swpaul int minten = 0; 65736270Swpaul 65867087Swpaul TL_LOCK(sc); 65936270Swpaul 66039583Swpaul tl_mii_sync(sc); 66136270Swpaul 66236270Swpaul /* 66336270Swpaul * Set up frame for RX. 66436270Swpaul */ 66536270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66636270Swpaul frame->mii_opcode = TL_MII_READOP; 66736270Swpaul frame->mii_turnaround = 0; 66836270Swpaul frame->mii_data = 0; 66936270Swpaul 67036270Swpaul /* 67136270Swpaul * Turn off MII interrupt by forcing MINTEN low. 67236270Swpaul */ 67339583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 67436270Swpaul if (minten) { 67539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67636270Swpaul } 67736270Swpaul 67836270Swpaul /* 67936270Swpaul * Turn on data xmit. 68036270Swpaul */ 68139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 68236270Swpaul 68336270Swpaul /* 68436270Swpaul * Send command/address info. 68536270Swpaul */ 68639583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68739583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68839583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68939583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 69036270Swpaul 69136270Swpaul /* 69236270Swpaul * Turn off xmit. 69336270Swpaul */ 69439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69536270Swpaul 69636270Swpaul /* Idle bit */ 69739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69936270Swpaul 70036270Swpaul /* Check for ack */ 70139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 70239583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 70336270Swpaul 70436270Swpaul /* Complete the cycle */ 70539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70636270Swpaul 70736270Swpaul /* 70836270Swpaul * Now try reading data bits. If the ack failed, we still 70936270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 71036270Swpaul */ 71136270Swpaul if (ack) { 71236270Swpaul for(i = 0; i < 16; i++) { 71339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71536270Swpaul } 71636270Swpaul goto fail; 71736270Swpaul } 71836270Swpaul 71936270Swpaul for (i = 0x8000; i; i >>= 1) { 72039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72136270Swpaul if (!ack) { 72239583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 72336270Swpaul frame->mii_data |= i; 72436270Swpaul } 72539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72636270Swpaul } 72736270Swpaul 72836270Swpaulfail: 72936270Swpaul 73039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 73139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 73236270Swpaul 73336270Swpaul /* Reenable interrupts */ 73436270Swpaul if (minten) { 73539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73636270Swpaul } 73736270Swpaul 73867087Swpaul TL_UNLOCK(sc); 73936270Swpaul 74036270Swpaul if (ack) 74136270Swpaul return(1); 74236270Swpaul return(0); 74336270Swpaul} 74436270Swpaul 745102336Salfredstatic int 746102336Salfredtl_mii_writereg(sc, frame) 74739583Swpaul struct tl_softc *sc; 74836270Swpaul struct tl_mii_frame *frame; 74936270Swpaul 75036270Swpaul{ 75136270Swpaul int minten; 75236270Swpaul 75367087Swpaul TL_LOCK(sc); 75467087Swpaul 75539583Swpaul tl_mii_sync(sc); 75636270Swpaul 75736270Swpaul /* 75836270Swpaul * Set up frame for TX. 75936270Swpaul */ 76036270Swpaul 76136270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 76236270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 76336270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 76436270Swpaul 76536270Swpaul /* 76636270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76736270Swpaul */ 76839583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76936270Swpaul if (minten) { 77039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 77136270Swpaul } 77236270Swpaul 77336270Swpaul /* 77436270Swpaul * Turn on data output. 77536270Swpaul */ 77639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77736270Swpaul 77839583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77939583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 78039583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 78139583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 78239583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 78339583Swpaul tl_mii_send(sc, frame->mii_data, 16); 78436270Swpaul 78539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78736270Swpaul 78836270Swpaul /* 78936270Swpaul * Turn off xmit. 79036270Swpaul */ 79139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 79236270Swpaul 79336270Swpaul /* Reenable interrupts */ 79436270Swpaul if (minten) 79539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79636270Swpaul 79767087Swpaul TL_UNLOCK(sc); 79836270Swpaul 79936270Swpaul return(0); 80036270Swpaul} 80136270Swpaul 802102336Salfredstatic int 803102336Salfredtl_miibus_readreg(dev, phy, reg) 80450462Swpaul device_t dev; 80550462Swpaul int phy, reg; 80650462Swpaul{ 80736270Swpaul struct tl_softc *sc; 80836270Swpaul struct tl_mii_frame frame; 80936270Swpaul 81050462Swpaul sc = device_get_softc(dev); 81136270Swpaul bzero((char *)&frame, sizeof(frame)); 81236270Swpaul 81350462Swpaul frame.mii_phyaddr = phy; 81436270Swpaul frame.mii_regaddr = reg; 81539583Swpaul tl_mii_readreg(sc, &frame); 81636270Swpaul 81736270Swpaul return(frame.mii_data); 81836270Swpaul} 81936270Swpaul 820102336Salfredstatic int 821102336Salfredtl_miibus_writereg(dev, phy, reg, data) 82250462Swpaul device_t dev; 82350462Swpaul int phy, reg, data; 82450462Swpaul{ 82536270Swpaul struct tl_softc *sc; 82636270Swpaul struct tl_mii_frame frame; 82736270Swpaul 82850462Swpaul sc = device_get_softc(dev); 82936270Swpaul bzero((char *)&frame, sizeof(frame)); 83036270Swpaul 83150462Swpaul frame.mii_phyaddr = phy; 83236270Swpaul frame.mii_regaddr = reg; 83336270Swpaul frame.mii_data = data; 83436270Swpaul 83539583Swpaul tl_mii_writereg(sc, &frame); 83636270Swpaul 83750462Swpaul return(0); 83836270Swpaul} 83936270Swpaul 840102336Salfredstatic void 841102336Salfredtl_miibus_statchg(dev) 84250462Swpaul device_t dev; 84350462Swpaul{ 84436270Swpaul struct tl_softc *sc; 84550462Swpaul struct mii_data *mii; 84636270Swpaul 84750462Swpaul sc = device_get_softc(dev); 84867087Swpaul TL_LOCK(sc); 84950462Swpaul mii = device_get_softc(sc->tl_miibus); 85036270Swpaul 85150462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 85250462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85336270Swpaul } else { 85450462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85536270Swpaul } 85667087Swpaul TL_UNLOCK(sc); 85736270Swpaul 85836270Swpaul return; 85936270Swpaul} 86036270Swpaul 86136270Swpaul/* 86250462Swpaul * Set modes for bitrate devices. 86336270Swpaul */ 864102336Salfredstatic void 865102336Salfredtl_setmode(sc, media) 86636270Swpaul struct tl_softc *sc; 86736270Swpaul int media; 86836270Swpaul{ 86950462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 87050462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 87136270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 87250462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 87336270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 87450462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87539583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87636270Swpaul } else { 87750462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87839583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87936270Swpaul } 88036270Swpaul } 88136270Swpaul 88236270Swpaul return; 88336270Swpaul} 88436270Swpaul 88536464Swpaul/* 88636464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 88736464Swpaul * table. This hash is simply the address split into 6-bit chunks 88836464Swpaul * XOR'd, e.g. 88936464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 89036464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 89136464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 89236464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 89336464Swpaul */ 894102336Salfredstatic int 895102336Salfredtl_calchash(addr) 89641656Swpaul caddr_t addr; 89736270Swpaul{ 89837626Swpaul int t; 89936270Swpaul 90036464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 90136464Swpaul (addr[2] ^ addr[5]); 90236464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 90336270Swpaul} 90436270Swpaul 90539583Swpaul/* 90639583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 90739583Swpaul * the multicast hash filter. The perfect filter can be programmed 90839583Swpaul * with up to four MAC addresses. The first one is always used to 90939583Swpaul * hold the station address, which leaves us free to use the other 91039583Swpaul * three for multicast addresses. 91139583Swpaul */ 912102336Salfredstatic void 913102336Salfredtl_setfilt(sc, addr, slot) 91439583Swpaul struct tl_softc *sc; 91541656Swpaul caddr_t addr; 91639583Swpaul int slot; 91739583Swpaul{ 91839583Swpaul int i; 91939583Swpaul u_int16_t regaddr; 92039583Swpaul 92139583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 92239583Swpaul 92339583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 92439583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 92539583Swpaul 92639583Swpaul return; 92739583Swpaul} 92839583Swpaul 92939583Swpaul/* 93039583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 93139583Swpaul * linked list. This is fine, except addresses are added from the head 93239583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 93339583Swpaul * group to always be in the perfect filter, but as more groups are added, 93439583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 93539583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 93639583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 93739583Swpaul * and into the hash table. 93839583Swpaul * 93939583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 94039583Swpaul * circular queue, we don't have the ability to just grab the tail of 94139583Swpaul * the list and traverse it backwards. Instead, we have to traverse 94239583Swpaul * the list once to find the tail, then traverse it again backwards to 94339583Swpaul * update the multicast filter. 94439583Swpaul */ 945102336Salfredstatic void 946102336Salfredtl_setmulti(sc) 94736270Swpaul struct tl_softc *sc; 94836270Swpaul{ 94936270Swpaul struct ifnet *ifp; 95036270Swpaul u_int32_t hashes[2] = { 0, 0 }; 95139583Swpaul int h, i; 95236270Swpaul struct ifmultiaddr *ifma; 95339583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 95436270Swpaul ifp = &sc->arpcom.ac_if; 95536270Swpaul 95639583Swpaul /* First, zot all the existing filters. */ 95739583Swpaul for (i = 1; i < 4; i++) 95841656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 95939583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 96039583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 96139583Swpaul 96239583Swpaul /* Now program new ones. */ 96339583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 96436270Swpaul hashes[0] = 0xFFFFFFFF; 96536270Swpaul hashes[1] = 0xFFFFFFFF; 96636270Swpaul } else { 96739583Swpaul i = 1; 96872084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 96936270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 97036270Swpaul continue; 97139583Swpaul /* 97239583Swpaul * Program the first three multicast groups 97339583Swpaul * into the perfect filter. For all others, 97439583Swpaul * use the hash table. 97539583Swpaul */ 97639583Swpaul if (i < 4) { 97739583Swpaul tl_setfilt(sc, 97839583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97939583Swpaul i++; 98039583Swpaul continue; 98139583Swpaul } 98239583Swpaul 98336270Swpaul h = tl_calchash( 98436270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 98536270Swpaul if (h < 32) 98636270Swpaul hashes[0] |= (1 << h); 98736270Swpaul else 98836317Swpaul hashes[1] |= (1 << (h - 32)); 98936270Swpaul } 99036270Swpaul } 99136270Swpaul 99239583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 99339583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 99436270Swpaul 99536270Swpaul return; 99636270Swpaul} 99736270Swpaul 99839583Swpaul/* 99939583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 100039583Swpaul * the internal PHY is powered up correctly. It also recommends a one 100139583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 100239583Swpaul * experience this isn't necessary. 100339583Swpaul */ 1004102336Salfredstatic void 1005102336Salfredtl_hardreset(dev) 100650468Swpaul device_t dev; 100750468Swpaul{ 100839583Swpaul struct tl_softc *sc; 100939583Swpaul int i; 101050468Swpaul u_int16_t flags; 101139583Swpaul 101250468Swpaul sc = device_get_softc(dev); 101339583Swpaul 101450468Swpaul tl_mii_sync(sc); 101539583Swpaul 101650468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 101739583Swpaul 101850468Swpaul for (i = 0; i < MII_NPHY; i++) 101950468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 102039583Swpaul 102150468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 102239583Swpaul DELAY(50000); 102350468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 102439583Swpaul tl_mii_sync(sc); 102550468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 102639583Swpaul 102750468Swpaul DELAY(50000); 102839583Swpaul return; 102939583Swpaul} 103039583Swpaul 1031102336Salfredstatic void 1032102336Salfredtl_softreset(sc, internal) 103339583Swpaul struct tl_softc *sc; 103436270Swpaul int internal; 103536270Swpaul{ 103639583Swpaul u_int32_t cmd, dummy, i; 103736270Swpaul 103836270Swpaul /* Assert the adapter reset bit. */ 103939583Swpaul CMD_SET(sc, TL_CMD_ADRST); 104050468Swpaul 104136270Swpaul /* Turn off interrupts */ 104239583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 104336270Swpaul 104436270Swpaul /* First, clear the stats registers. */ 104539583Swpaul for (i = 0; i < 5; i++) 104639583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 104736270Swpaul 104836270Swpaul /* Clear Areg and Hash registers */ 104939583Swpaul for (i = 0; i < 8; i++) 105039583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 105136270Swpaul 105236270Swpaul /* 105336270Swpaul * Set up Netconfig register. Enable one channel and 105436270Swpaul * one fragment mode. 105536270Swpaul */ 105639583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 105745155Swpaul if (internal && !sc->tl_bitrate) { 105839583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105936270Swpaul } else { 106039583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 106136270Swpaul } 106236270Swpaul 106345155Swpaul /* Handle cards with bitrate devices. */ 106445155Swpaul if (sc->tl_bitrate) 106545155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 106645155Swpaul 106736270Swpaul /* 106836270Swpaul * Load adapter irq pacing timer and tx threshold. 106936270Swpaul * We make the transmit threshold 1 initially but we may 107036270Swpaul * change that later. 107136270Swpaul */ 107239583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 107336270Swpaul cmd |= TL_CMD_NES; 107436270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 107539583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 107639583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 107736270Swpaul 107836270Swpaul /* Unreset the MII */ 107939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 108036270Swpaul 108136270Swpaul /* Take the adapter out of reset */ 108239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 108336270Swpaul 108436270Swpaul /* Wait for things to settle down a little. */ 108536270Swpaul DELAY(500); 108636270Swpaul 108736270Swpaul return; 108836270Swpaul} 108936270Swpaul 109036270Swpaul/* 109136270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 109239583Swpaul * against our list and return its name if we find a match. 109336270Swpaul */ 1094102336Salfredstatic int 1095102336Salfredtl_probe(dev) 109648992Swpaul device_t dev; 109736270Swpaul{ 109836270Swpaul struct tl_type *t; 109936270Swpaul 110036270Swpaul t = tl_devs; 110136270Swpaul 110236270Swpaul while(t->tl_name != NULL) { 110348992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 110448992Swpaul (pci_get_device(dev) == t->tl_did)) { 110548992Swpaul device_set_desc(dev, t->tl_name); 110648992Swpaul return(0); 110748992Swpaul } 110836270Swpaul t++; 110936270Swpaul } 111036270Swpaul 111148992Swpaul return(ENXIO); 111236270Swpaul} 111336270Swpaul 1114102336Salfredstatic int 1115102336Salfredtl_attach(dev) 111648992Swpaul device_t dev; 111736270Swpaul{ 111867087Swpaul int i; 111936270Swpaul u_int32_t command; 112039583Swpaul u_int16_t did, vid; 112139583Swpaul struct tl_type *t; 112239583Swpaul struct ifnet *ifp; 112339583Swpaul struct tl_softc *sc; 112448992Swpaul int unit, error = 0, rid; 112536270Swpaul 112648992Swpaul vid = pci_get_vendor(dev); 112748992Swpaul did = pci_get_device(dev); 112848992Swpaul sc = device_get_softc(dev); 112948992Swpaul unit = device_get_unit(dev); 113048992Swpaul bzero(sc, sizeof(struct tl_softc)); 113139583Swpaul 113239583Swpaul t = tl_devs; 113339583Swpaul while(t->tl_name != NULL) { 113439583Swpaul if (vid == t->tl_vid && did == t->tl_did) 113536270Swpaul break; 113639583Swpaul t++; 113739583Swpaul } 113836270Swpaul 113939583Swpaul if (t->tl_name == NULL) { 1140105599Sbrooks device_printf(dev, "unknown device!?\n"); 114136270Swpaul goto fail; 114236270Swpaul } 114336270Swpaul 114493818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 114593818Sjhb MTX_DEF | MTX_RECURSE); 114669583Swpaul TL_LOCK(sc); 114769583Swpaul 114836270Swpaul /* 114936270Swpaul * Map control/status registers. 115036270Swpaul */ 115172813Swpaul pci_enable_busmaster(dev); 115279472Swpaul pci_enable_io(dev, SYS_RES_IOPORT); 115379472Swpaul pci_enable_io(dev, SYS_RES_MEMORY); 115461041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 115536270Swpaul 115639583Swpaul#ifdef TL_USEIOSPACE 115739583Swpaul if (!(command & PCIM_CMD_PORTEN)) { 1158105599Sbrooks device_printf(dev, "failed to enable I/O ports!\n"); 115948992Swpaul error = ENXIO; 116039583Swpaul goto fail; 116139583Swpaul } 116239583Swpaul 116348992Swpaul rid = TL_PCI_LOIO; 116448992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 116548992Swpaul 0, ~0, 1, RF_ACTIVE); 116648992Swpaul 116748992Swpaul /* 116848992Swpaul * Some cards have the I/O and memory mapped address registers 116948992Swpaul * reversed. Try both combinations before giving up. 117048992Swpaul */ 117148992Swpaul if (sc->tl_res == NULL) { 117248992Swpaul rid = TL_PCI_LOMEM; 117348992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 117448992Swpaul 0, ~0, 1, RF_ACTIVE); 117545155Swpaul } 117639583Swpaul#else 117736270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 1178105599Sbrooks device_printf(dev, "failed to enable memory mapping!\n"); 117948992Swpaul error = ENXIO; 118036270Swpaul goto fail; 118136270Swpaul } 118236270Swpaul 118348992Swpaul rid = TL_PCI_LOMEM; 118448992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 118548992Swpaul 0, ~0, 1, RF_ACTIVE); 118648992Swpaul if (sc->tl_res == NULL) { 118748992Swpaul rid = TL_PCI_LOIO; 118848992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 118948992Swpaul 0, ~0, 1, RF_ACTIVE); 119036270Swpaul } 119139583Swpaul#endif 119236270Swpaul 119348992Swpaul if (sc->tl_res == NULL) { 1194105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 119548992Swpaul error = ENXIO; 119648992Swpaul goto fail; 119748992Swpaul } 119848992Swpaul 119948992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 120048992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 120148992Swpaul 120239583Swpaul#ifdef notdef 120339583Swpaul /* 120439583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 120539583Swpaul * timer all the way up to its maximum value. I'm not sure 120639583Swpaul * if this is really necessary, but what the manual wants, 120739583Swpaul * the manual gets. 120839583Swpaul */ 120948992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 121039583Swpaul command |= 0x0000FF00; 121148992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 121239583Swpaul#endif 121336270Swpaul 121436270Swpaul /* Allocate interrupt */ 121548992Swpaul rid = 0; 121648992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 121748992Swpaul RF_SHAREABLE | RF_ACTIVE); 121848992Swpaul 121948992Swpaul if (sc->tl_irq == NULL) { 122049010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 1221105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 122248992Swpaul error = ENXIO; 122336270Swpaul goto fail; 122436270Swpaul } 122536270Swpaul 122648992Swpaul error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 122748992Swpaul tl_intr, sc, &sc->tl_intrhand); 122848992Swpaul 122948992Swpaul if (error) { 123068216Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 123149010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 1232105599Sbrooks device_printf(dev, "couldn't set up irq\n"); 123348992Swpaul goto fail; 123448992Swpaul } 123548992Swpaul 123636270Swpaul /* 123751439Swpaul * Now allocate memory for the TX and RX lists. 123836270Swpaul */ 123951439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 124051657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 124139583Swpaul 124251439Swpaul if (sc->tl_ldata == NULL) { 124349010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 124448992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 124549010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 1246105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 124748992Swpaul error = ENXIO; 124836270Swpaul goto fail; 124936270Swpaul } 125036270Swpaul 125139583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 125239583Swpaul 125339583Swpaul sc->tl_dinfo = t; 125443235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 125539583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 125639583Swpaul if (t->tl_vid == OLICOM_VENDORID) 125739583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 125839583Swpaul 125939583Swpaul /* Reset the adapter. */ 126039583Swpaul tl_softreset(sc, 1); 126150468Swpaul tl_hardreset(dev); 126239583Swpaul tl_softreset(sc, 1); 126339583Swpaul 126438030Swpaul /* 126539583Swpaul * Get station address from the EEPROM. 126639583Swpaul */ 126739583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 126839583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 126949010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 127048992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 127149010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 127251439Swpaul contigfree(sc->tl_ldata, 127351439Swpaul sizeof(struct tl_list_data), M_DEVBUF); 1274105599Sbrooks device_printf(dev, "failed to read station address\n"); 127548992Swpaul error = ENXIO; 127639583Swpaul goto fail; 127739583Swpaul } 127839583Swpaul 127939583Swpaul /* 128039583Swpaul * XXX Olicom, in its desire to be different from the 128139583Swpaul * rest of the world, has done strange things with the 128239583Swpaul * encoding of the station address in the EEPROM. First 128339583Swpaul * of all, they store the address at offset 0xF8 rather 128439583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 128539583Swpaul * Second, they store the address in three 16-bit words in 128639583Swpaul * network byte order, as opposed to storing it sequentially 128739583Swpaul * like all the other ThunderLAN cards. In order to get 128839583Swpaul * the station address in a form that matches what the Olicom 128939583Swpaul * diagnostic utility specifies, we have to byte-swap each 129039583Swpaul * word. To make things even more confusing, neither 00:00:28 129139583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 129239583Swpaul */ 129339583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 129439583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 129539583Swpaul u_int16_t *p; 129639583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 129739583Swpaul *p = ntohs(*p); 129839583Swpaul } 129939583Swpaul } 130039583Swpaul 130139583Swpaul /* 130236270Swpaul * A ThunderLAN chip was detected. Inform the world. 130336270Swpaul */ 1304105599Sbrooks device_printf(dev, "Ethernet address: %6D\n", 130539583Swpaul sc->arpcom.ac_enaddr, ":"); 130636270Swpaul 130739583Swpaul ifp = &sc->arpcom.ac_if; 130839583Swpaul ifp->if_softc = sc; 1309105599Sbrooks ifp->if_unit = unit; 131039583Swpaul ifp->if_name = "tl"; 131139583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 131239583Swpaul ifp->if_ioctl = tl_ioctl; 131339583Swpaul ifp->if_output = ether_output; 131439583Swpaul ifp->if_start = tl_start; 131539583Swpaul ifp->if_watchdog = tl_watchdog; 131639583Swpaul ifp->if_init = tl_init; 131739583Swpaul ifp->if_mtu = ETHERMTU; 131851439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 131939583Swpaul callout_handle_init(&sc->tl_stat_ch); 132039583Swpaul 132139583Swpaul /* Reset the adapter again. */ 132239583Swpaul tl_softreset(sc, 1); 132350468Swpaul tl_hardreset(dev); 132439583Swpaul tl_softreset(sc, 1); 132539583Swpaul 132636270Swpaul /* 132750462Swpaul * Do MII setup. If no PHYs are found, then this is a 132850462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 132950462Swpaul * and AUI/BNC. 133036270Swpaul */ 133150462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 133250462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 133345155Swpaul struct ifmedia *ifm; 133445155Swpaul sc->tl_bitrate = 1; 133545155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 133645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 133745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 133845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 133945155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 134045166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 134145155Swpaul /* Reset again, this time setting bitrate mode. */ 134245155Swpaul tl_softreset(sc, 1); 134345155Swpaul ifm = &sc->ifmedia; 134445155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 134545155Swpaul tl_ifmedia_upd(ifp); 134636270Swpaul } 134736270Swpaul 134839583Swpaul /* 134963090Sarchie * Call MI attach routine. 135039583Swpaul */ 135163090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 135267087Swpaul TL_UNLOCK(sc); 135367087Swpaul return(0); 135438030Swpaul 135536270Swpaulfail: 135667087Swpaul TL_UNLOCK(sc); 135767087Swpaul mtx_destroy(&sc->tl_mtx); 135848992Swpaul return(error); 135936270Swpaul} 136036270Swpaul 1361102336Salfredstatic int 1362102336Salfredtl_detach(dev) 136348992Swpaul device_t dev; 136448992Swpaul{ 136548992Swpaul struct tl_softc *sc; 136648992Swpaul struct ifnet *ifp; 136748992Swpaul 136848992Swpaul sc = device_get_softc(dev); 136967087Swpaul TL_LOCK(sc); 137048992Swpaul ifp = &sc->arpcom.ac_if; 137148992Swpaul 137248992Swpaul tl_stop(sc); 137363090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 137448992Swpaul 137550462Swpaul bus_generic_detach(dev); 137650462Swpaul device_delete_child(dev, sc->tl_miibus); 137750462Swpaul 137851439Swpaul contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 137950462Swpaul if (sc->tl_bitrate) 138050462Swpaul ifmedia_removeall(&sc->ifmedia); 138148992Swpaul 138248992Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 138348992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 138449010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 138548992Swpaul 138667087Swpaul TL_UNLOCK(sc); 138767087Swpaul mtx_destroy(&sc->tl_mtx); 138848992Swpaul 138948992Swpaul return(0); 139048992Swpaul} 139148992Swpaul 139236270Swpaul/* 139336270Swpaul * Initialize the transmit lists. 139436270Swpaul */ 1395102336Salfredstatic int 1396102336Salfredtl_list_tx_init(sc) 139736270Swpaul struct tl_softc *sc; 139836270Swpaul{ 139936270Swpaul struct tl_chain_data *cd; 140036270Swpaul struct tl_list_data *ld; 140136270Swpaul int i; 140236270Swpaul 140336270Swpaul cd = &sc->tl_cdata; 140436270Swpaul ld = sc->tl_ldata; 140536270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 140636270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 140736270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 140836270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 140936270Swpaul else 141036270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 141136270Swpaul } 141236270Swpaul 141336270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 141436270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 141536270Swpaul sc->tl_txeoc = 1; 141636270Swpaul 141736270Swpaul return(0); 141836270Swpaul} 141936270Swpaul 142036270Swpaul/* 142136270Swpaul * Initialize the RX lists and allocate mbufs for them. 142236270Swpaul */ 1423102336Salfredstatic int 1424102336Salfredtl_list_rx_init(sc) 142536270Swpaul struct tl_softc *sc; 142636270Swpaul{ 142736270Swpaul struct tl_chain_data *cd; 142836270Swpaul struct tl_list_data *ld; 142936270Swpaul int i; 143036270Swpaul 143136270Swpaul cd = &sc->tl_cdata; 143236270Swpaul ld = sc->tl_ldata; 143336270Swpaul 143440795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 143536270Swpaul cd->tl_rx_chain[i].tl_ptr = 143637626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 143739583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 143839583Swpaul return(ENOBUFS); 143940795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 144036270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 144136270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 144236270Swpaul } else { 144336270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 144436270Swpaul ld->tl_rx_list[i].tlist_fptr = 144536270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 144636270Swpaul } 144736270Swpaul } 144836270Swpaul 144936270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 145036270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 145136270Swpaul 145236270Swpaul return(0); 145336270Swpaul} 145436270Swpaul 1455102336Salfredstatic int 1456102336Salfredtl_newbuf(sc, c) 145736270Swpaul struct tl_softc *sc; 145837626Swpaul struct tl_chain_onefrag *c; 145936270Swpaul{ 146036270Swpaul struct mbuf *m_new = NULL; 146136270Swpaul 146236270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 146387846Sluigi if (m_new == NULL) 146436270Swpaul return(ENOBUFS); 146536270Swpaul 146636270Swpaul MCLGET(m_new, M_DONTWAIT); 146736270Swpaul if (!(m_new->m_flags & M_EXT)) { 146836270Swpaul m_freem(m_new); 146936270Swpaul return(ENOBUFS); 147036270Swpaul } 147136270Swpaul 147245155Swpaul#ifdef __alpha__ 147345155Swpaul m_new->m_data += 2; 147445155Swpaul#endif 147545155Swpaul 147636270Swpaul c->tl_mbuf = m_new; 147736270Swpaul c->tl_next = NULL; 147836270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 147936270Swpaul c->tl_ptr->tlist_fptr = 0; 148037626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 148137626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 148256060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 148336270Swpaul 148436270Swpaul return(0); 148536270Swpaul} 148636270Swpaul/* 148736270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 148836270Swpaul * tells us that a full ethernet frame has been captured and we need 148936270Swpaul * to handle it. 149036270Swpaul * 149136270Swpaul * Reception is done using 'lists' which consist of a header and a 149236270Swpaul * series of 10 data count/data address pairs that point to buffers. 149336270Swpaul * Initially you're supposed to create a list, populate it with pointers 149436270Swpaul * to buffers, then load the physical address of the list into the 149536270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 149636270Swpaul * frame into the buffers for you. 149736270Swpaul * 149836270Swpaul * To make things as fast as possible, we have the chip DMA directly 149936270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 150036270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 150136270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 150236270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 150336270Swpaul * the chip, it will always think it has an endless receive channel. 150436270Swpaul * 150536270Swpaul * If we happen to fall behind and the chip manages to fill up all of 150636270Swpaul * the buffers, it will generate an end of channel interrupt and wait 150736270Swpaul * for us to empty the chain and restart the receiver. 150836270Swpaul */ 1509102336Salfredstatic int 1510102336Salfredtl_intvec_rxeof(xsc, type) 151136270Swpaul void *xsc; 151236270Swpaul u_int32_t type; 151336270Swpaul{ 151436270Swpaul struct tl_softc *sc; 151536270Swpaul int r = 0, total_len = 0; 151636270Swpaul struct ether_header *eh; 151736270Swpaul struct mbuf *m; 151836270Swpaul struct ifnet *ifp; 151937626Swpaul struct tl_chain_onefrag *cur_rx; 152036270Swpaul 152136270Swpaul sc = xsc; 152236270Swpaul ifp = &sc->arpcom.ac_if; 152336270Swpaul 152456060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 152556060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 152656060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 152756060Swpaul break; 152836270Swpaul r++; 152936270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 153036270Swpaul m = cur_rx->tl_mbuf; 153136270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 153236270Swpaul 153339583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 153439583Swpaul ifp->if_ierrors++; 153539583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 153639583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 153739583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 153839583Swpaul continue; 153939583Swpaul } 154036270Swpaul 154136270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 154236270Swpaul vtophys(cur_rx->tl_ptr); 154336270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 154436270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 154536270Swpaul 154636270Swpaul eh = mtod(m, struct ether_header *); 154736270Swpaul m->m_pkthdr.rcvif = ifp; 154836270Swpaul 154937626Swpaul /* 155037626Swpaul * Note: when the ThunderLAN chip is in 'capture all 155137626Swpaul * frames' mode, it will receive its own transmissions. 155237626Swpaul * We drop don't need to process our own transmissions, 155337626Swpaul * so we drop them here and continue. 155437626Swpaul */ 155539583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 155639583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 155737626Swpaul ETHER_ADDR_LEN)) { 155837626Swpaul m_freem(m); 155937626Swpaul continue; 156037626Swpaul } 156137626Swpaul 156236270Swpaul /* Remove header from mbuf and pass it on. */ 156336270Swpaul m->m_pkthdr.len = m->m_len = 156436270Swpaul total_len - sizeof(struct ether_header); 156536270Swpaul m->m_data += sizeof(struct ether_header); 156636270Swpaul ether_input(ifp, eh, m); 156736270Swpaul } 156836270Swpaul 156936270Swpaul return(r); 157036270Swpaul} 157136270Swpaul 157236270Swpaul/* 157336270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 157436270Swpaul * initialized or the adapter reached a list with a forward pointer 157536270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 157636270Swpaul * the card has hit the end of the receive buffer chain and we need to 157736270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 157836270Swpaul */ 1579102336Salfredstatic int 1580102336Salfredtl_intvec_rxeoc(xsc, type) 158136270Swpaul void *xsc; 158236270Swpaul u_int32_t type; 158336270Swpaul{ 158436270Swpaul struct tl_softc *sc; 158536270Swpaul int r; 158656060Swpaul struct tl_chain_data *cd; 158736270Swpaul 158856060Swpaul 158936270Swpaul sc = xsc; 159056060Swpaul cd = &sc->tl_cdata; 159136270Swpaul 159236270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 159336270Swpaul r = tl_intvec_rxeof(xsc, type); 159439583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 159536270Swpaul r = 1; 159656060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 159756060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 159839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 159936270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 160036270Swpaul return(r); 160136270Swpaul} 160236270Swpaul 1603102336Salfredstatic int 1604102336Salfredtl_intvec_txeof(xsc, type) 160536270Swpaul void *xsc; 160636270Swpaul u_int32_t type; 160736270Swpaul{ 160836270Swpaul struct tl_softc *sc; 160936270Swpaul int r = 0; 161036270Swpaul struct tl_chain *cur_tx; 161136270Swpaul 161236270Swpaul sc = xsc; 161336270Swpaul 161436270Swpaul /* 161536270Swpaul * Go through our tx list and free mbufs for those 161636270Swpaul * frames that have been sent. 161736270Swpaul */ 161836270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 161936270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 162036270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 162136270Swpaul break; 162236270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 162336270Swpaul 162436270Swpaul r++; 162536270Swpaul m_freem(cur_tx->tl_mbuf); 162636270Swpaul cur_tx->tl_mbuf = NULL; 162736270Swpaul 162836270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 162936270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 163037626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 163137626Swpaul break; 163236270Swpaul } 163336270Swpaul 163436270Swpaul return(r); 163536270Swpaul} 163636270Swpaul 163736270Swpaul/* 163836270Swpaul * The transmit end of channel interrupt. The adapter triggers this 163936270Swpaul * interrupt to tell us it hit the end of the current transmit list. 164036270Swpaul * 164136270Swpaul * A note about this: it's possible for a condition to arise where 164236270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 164336270Swpaul * You have to avoid this since the chip expects things to go in a 164436270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 164536270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 164636270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 164736270Swpaul * interrupt should be received and acknowledged before any more frames 164836270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 164936270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 165036270Swpaul * it could attempt to issue a transmit command prematurely. 165136270Swpaul * 165236270Swpaul * To guard against this, tl_start() will only issue transmit commands 165336270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 165436270Swpaul * can set this flag once tl_start() has cleared it. 165536270Swpaul */ 1656102336Salfredstatic int 1657102336Salfredtl_intvec_txeoc(xsc, type) 165836270Swpaul void *xsc; 165936270Swpaul u_int32_t type; 166036270Swpaul{ 166136270Swpaul struct tl_softc *sc; 166236270Swpaul struct ifnet *ifp; 166336270Swpaul u_int32_t cmd; 166436270Swpaul 166536270Swpaul sc = xsc; 166636270Swpaul ifp = &sc->arpcom.ac_if; 166736270Swpaul 166836270Swpaul /* Clear the timeout timer. */ 166936270Swpaul ifp->if_timer = 0; 167036270Swpaul 167136270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 167236270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 167336270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 167436270Swpaul sc->tl_txeoc = 1; 167536270Swpaul } else { 167636270Swpaul sc->tl_txeoc = 0; 167736270Swpaul /* First we have to ack the EOC interrupt. */ 167839583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 167936270Swpaul /* Then load the address of the next TX list. */ 168039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 168151439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 168236270Swpaul /* Restart TX channel. */ 168339583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 168436270Swpaul cmd &= ~TL_CMD_RT; 168536270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 168639583Swpaul CMD_PUT(sc, cmd); 168736270Swpaul return(0); 168836270Swpaul } 168936270Swpaul 169036270Swpaul return(1); 169136270Swpaul} 169236270Swpaul 1693102336Salfredstatic int 1694102336Salfredtl_intvec_adchk(xsc, type) 169536270Swpaul void *xsc; 169636270Swpaul u_int32_t type; 169736270Swpaul{ 169836270Swpaul struct tl_softc *sc; 169936270Swpaul 170036270Swpaul sc = xsc; 170136270Swpaul 170239627Swpaul if (type) 1703105599Sbrooks if_printf(&sc->arpcom.ac_if, "adapter check: %x\n", 170441656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 170536270Swpaul 170639583Swpaul tl_softreset(sc, 1); 170737626Swpaul tl_stop(sc); 170836270Swpaul tl_init(sc); 170939583Swpaul CMD_SET(sc, TL_CMD_INTSON); 171036270Swpaul 171136270Swpaul return(0); 171236270Swpaul} 171336270Swpaul 1714102336Salfredstatic int 1715102336Salfredtl_intvec_netsts(xsc, type) 171636270Swpaul void *xsc; 171736270Swpaul u_int32_t type; 171836270Swpaul{ 171936270Swpaul struct tl_softc *sc; 172036270Swpaul u_int16_t netsts; 172136270Swpaul 172236270Swpaul sc = xsc; 172336270Swpaul 172439583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 172539583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 172636270Swpaul 1727105599Sbrooks if_printf(&sc->arpcom.ac_if, "network status: %x\n", netsts); 172836270Swpaul 172936270Swpaul return(1); 173036270Swpaul} 173136270Swpaul 1732102336Salfredstatic void 1733102336Salfredtl_intr(xsc) 173439583Swpaul void *xsc; 173536270Swpaul{ 173636270Swpaul struct tl_softc *sc; 173736270Swpaul struct ifnet *ifp; 173836270Swpaul int r = 0; 173936270Swpaul u_int32_t type = 0; 174036270Swpaul u_int16_t ints = 0; 174136270Swpaul u_int8_t ivec = 0; 174236270Swpaul 174339583Swpaul sc = xsc; 174467087Swpaul TL_LOCK(sc); 174536270Swpaul 174636270Swpaul /* Disable interrupts */ 174739583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 174839583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 174936270Swpaul type = (ints << 16) & 0xFFFF0000; 175036270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 175136270Swpaul ints = (ints & TL_INT_MASK) >> 2; 175236270Swpaul 175336270Swpaul ifp = &sc->arpcom.ac_if; 175436270Swpaul 175536270Swpaul switch(ints) { 175636270Swpaul case (TL_INTR_INVALID): 175739583Swpaul#ifdef DIAGNOSTIC 1758105599Sbrooks if_printf(ifp, "got an invalid interrupt!\n"); 175939583Swpaul#endif 176039583Swpaul /* Re-enable interrupts but don't ack this one. */ 176139583Swpaul CMD_PUT(sc, type); 176239583Swpaul r = 0; 176336270Swpaul break; 176436270Swpaul case (TL_INTR_TXEOF): 176536270Swpaul r = tl_intvec_txeof((void *)sc, type); 176636270Swpaul break; 176736270Swpaul case (TL_INTR_TXEOC): 176836270Swpaul r = tl_intvec_txeoc((void *)sc, type); 176936270Swpaul break; 177036270Swpaul case (TL_INTR_STATOFLOW): 177139583Swpaul tl_stats_update(sc); 177239583Swpaul r = 1; 177336270Swpaul break; 177436270Swpaul case (TL_INTR_RXEOF): 177536270Swpaul r = tl_intvec_rxeof((void *)sc, type); 177636270Swpaul break; 177736270Swpaul case (TL_INTR_DUMMY): 1778105599Sbrooks if_printf(ifp, "got a dummy interrupt\n"); 177939583Swpaul r = 1; 178036270Swpaul break; 178136270Swpaul case (TL_INTR_ADCHK): 178236270Swpaul if (ivec) 178336270Swpaul r = tl_intvec_adchk((void *)sc, type); 178436270Swpaul else 178536270Swpaul r = tl_intvec_netsts((void *)sc, type); 178636270Swpaul break; 178736270Swpaul case (TL_INTR_RXEOC): 178836270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 178936270Swpaul break; 179036270Swpaul default: 1791105599Sbrooks if_printf(ifp, "bogus interrupt type\n"); 179236270Swpaul break; 179336270Swpaul } 179436270Swpaul 179536270Swpaul /* Re-enable interrupts */ 179637626Swpaul if (r) { 179739583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 179837626Swpaul } 179936270Swpaul 180037626Swpaul if (ifp->if_snd.ifq_head != NULL) 180137626Swpaul tl_start(ifp); 180237626Swpaul 180367087Swpaul TL_UNLOCK(sc); 180467087Swpaul 180536270Swpaul return; 180636270Swpaul} 180736270Swpaul 1808102336Salfredstatic void 1809102336Salfredtl_stats_update(xsc) 181036270Swpaul void *xsc; 181136270Swpaul{ 181236270Swpaul struct tl_softc *sc; 181336270Swpaul struct ifnet *ifp; 181436270Swpaul struct tl_stats tl_stats; 181550462Swpaul struct mii_data *mii; 181636270Swpaul u_int32_t *p; 181736270Swpaul 181836270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 181936270Swpaul 182036270Swpaul sc = xsc; 182167087Swpaul TL_LOCK(sc); 182236270Swpaul ifp = &sc->arpcom.ac_if; 182336270Swpaul 182436270Swpaul p = (u_int32_t *)&tl_stats; 182536270Swpaul 182639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 182739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 182839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 182939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 183039583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 183139583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 183236270Swpaul 183336270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 183436270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 183536270Swpaul tl_stats.tl_tx_multi_collision; 183636270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 183736270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 183836270Swpaul tl_rx_overrun(tl_stats); 183936270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 184036270Swpaul 184151439Swpaul if (tl_tx_underrun(tl_stats)) { 184251439Swpaul u_int8_t tx_thresh; 184351439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 184451439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 184551439Swpaul tx_thresh >>= 4; 184651439Swpaul tx_thresh++; 1847105599Sbrooks if_printf(ifp, "tx underrun -- increasing " 1848105599Sbrooks "tx threshold to %d bytes\n", 184951439Swpaul (64 * (tx_thresh * 4))); 185051439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 185151439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 185251439Swpaul } 185351439Swpaul } 185451439Swpaul 185536270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 185636302Swpaul 185750462Swpaul if (!sc->tl_bitrate) { 185850462Swpaul mii = device_get_softc(sc->tl_miibus); 185950462Swpaul mii_tick(mii); 186050462Swpaul } 186150462Swpaul 186267087Swpaul TL_UNLOCK(sc); 186348992Swpaul 186436302Swpaul return; 186536270Swpaul} 186636270Swpaul 186736270Swpaul/* 186836270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 186936270Swpaul * pointers to the fragment pointers. 187036270Swpaul */ 1871102336Salfredstatic int 1872102336Salfredtl_encap(sc, c, m_head) 187336270Swpaul struct tl_softc *sc; 187436270Swpaul struct tl_chain *c; 187536270Swpaul struct mbuf *m_head; 187636270Swpaul{ 187736270Swpaul int frag = 0; 187836270Swpaul struct tl_frag *f = NULL; 187936270Swpaul int total_len; 188036270Swpaul struct mbuf *m; 1881105599Sbrooks struct ifnet *ifp = &sc->arpcom.ac_if; 188236270Swpaul 188336270Swpaul /* 188436270Swpaul * Start packing the mbufs in this chain into 188536270Swpaul * the fragment pointers. Stop when we run out 188636270Swpaul * of fragments or hit the end of the mbuf chain. 188736270Swpaul */ 188836270Swpaul m = m_head; 188936270Swpaul total_len = 0; 189036270Swpaul 189136270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 189236270Swpaul if (m->m_len != 0) { 189336270Swpaul if (frag == TL_MAXFRAGS) 189436270Swpaul break; 189536270Swpaul total_len+= m->m_len; 189636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 189736270Swpaul vtophys(mtod(m, vm_offset_t)); 189836270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 189936270Swpaul frag++; 190036270Swpaul } 190136270Swpaul } 190236270Swpaul 190336270Swpaul /* 190436270Swpaul * Handle special cases. 190536270Swpaul * Special case #1: we used up all 10 fragments, but 190636270Swpaul * we have more mbufs left in the chain. Copy the 190736270Swpaul * data into an mbuf cluster. Note that we don't 190836270Swpaul * bother clearing the values in the other fragment 190936270Swpaul * pointers/counters; it wouldn't gain us anything, 191036270Swpaul * and would waste cycles. 191136270Swpaul */ 191236270Swpaul if (m != NULL) { 191336270Swpaul struct mbuf *m_new = NULL; 191436270Swpaul 191536270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 191636270Swpaul if (m_new == NULL) { 1917105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 191836270Swpaul return(1); 191936270Swpaul } 192036270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 192136270Swpaul MCLGET(m_new, M_DONTWAIT); 192236270Swpaul if (!(m_new->m_flags & M_EXT)) { 192336270Swpaul m_freem(m_new); 1924105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 192536270Swpaul return(1); 192636270Swpaul } 192736270Swpaul } 192836270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 192936270Swpaul mtod(m_new, caddr_t)); 193036270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 193136270Swpaul m_freem(m_head); 193236270Swpaul m_head = m_new; 193336270Swpaul f = &c->tl_ptr->tl_frag[0]; 193436270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 193536270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 193636270Swpaul frag = 1; 193736270Swpaul } 193836270Swpaul 193936270Swpaul /* 194036270Swpaul * Special case #2: the frame is smaller than the minimum 194136270Swpaul * frame size. We have to pad it to make the chip happy. 194236270Swpaul */ 194336270Swpaul if (total_len < TL_MIN_FRAMELEN) { 194436270Swpaul if (frag == TL_MAXFRAGS) 1945105599Sbrooks if_printf(ifp, 1946105599Sbrooks "all frags filled but frame still to small!\n"); 194736270Swpaul f = &c->tl_ptr->tl_frag[frag]; 194836270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 194936270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 195036270Swpaul total_len += f->tlist_dcnt; 195136270Swpaul frag++; 195236270Swpaul } 195336270Swpaul 195436270Swpaul c->tl_mbuf = m_head; 195536270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 195636270Swpaul c->tl_ptr->tlist_frsize = total_len; 195736270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 195836270Swpaul c->tl_ptr->tlist_fptr = 0; 195936270Swpaul 196036270Swpaul return(0); 196136270Swpaul} 196236270Swpaul 196336270Swpaul/* 196436270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 196536270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 196636270Swpaul * copy of the pointers since the transmit list fragment pointers are 196736270Swpaul * physical addresses. 196836270Swpaul */ 1969102336Salfredstatic void 1970102336Salfredtl_start(ifp) 197136270Swpaul struct ifnet *ifp; 197236270Swpaul{ 197336270Swpaul struct tl_softc *sc; 197436270Swpaul struct mbuf *m_head = NULL; 197536270Swpaul u_int32_t cmd; 197636270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 197736270Swpaul 197836270Swpaul sc = ifp->if_softc; 197967087Swpaul TL_LOCK(sc); 198036270Swpaul 198136270Swpaul /* 198236270Swpaul * Check for an available queue slot. If there are none, 198336270Swpaul * punt. 198436270Swpaul */ 198536270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 198636270Swpaul ifp->if_flags |= IFF_OACTIVE; 198767087Swpaul TL_UNLOCK(sc); 198836270Swpaul return; 198936270Swpaul } 199036270Swpaul 199136270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 199236270Swpaul 199336270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 199436270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 199536270Swpaul if (m_head == NULL) 199636270Swpaul break; 199736270Swpaul 199836270Swpaul /* Pick a chain member off the free list. */ 199936270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 200036270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 200136270Swpaul 200236270Swpaul cur_tx->tl_next = NULL; 200336270Swpaul 200436270Swpaul /* Pack the data into the list. */ 200536270Swpaul tl_encap(sc, cur_tx, m_head); 200636270Swpaul 200736270Swpaul /* Chain it together */ 200836270Swpaul if (prev != NULL) { 200936270Swpaul prev->tl_next = cur_tx; 201036270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 201136270Swpaul } 201236270Swpaul prev = cur_tx; 201336270Swpaul 201436270Swpaul /* 201536270Swpaul * If there's a BPF listener, bounce a copy of this frame 201636270Swpaul * to him. 201736270Swpaul */ 201836270Swpaul if (ifp->if_bpf) 201936270Swpaul bpf_mtap(ifp, cur_tx->tl_mbuf); 202036270Swpaul } 202136270Swpaul 202236270Swpaul /* 202341526Swpaul * If there are no packets queued, bail. 202441526Swpaul */ 202567087Swpaul if (cur_tx == NULL) { 202667087Swpaul TL_UNLOCK(sc); 202741526Swpaul return; 202867087Swpaul } 202941526Swpaul 203041526Swpaul /* 203136270Swpaul * That's all we can stands, we can't stands no more. 203236270Swpaul * If there are no other transfers pending, then issue the 203336270Swpaul * TX GO command to the adapter to start things moving. 203436270Swpaul * Otherwise, just leave the data in the queue and let 203536270Swpaul * the EOF/EOC interrupt handler send. 203636270Swpaul */ 203736270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 203836270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 203936270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 204039583Swpaul 204136270Swpaul if (sc->tl_txeoc) { 204236270Swpaul sc->tl_txeoc = 0; 204339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 204439583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 204536270Swpaul cmd &= ~TL_CMD_RT; 204636270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 204739583Swpaul CMD_PUT(sc, cmd); 204836270Swpaul } 204936270Swpaul } else { 205036270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 205142146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 205236270Swpaul } 205336270Swpaul 205436270Swpaul /* 205536270Swpaul * Set a timeout in case the chip goes out to lunch. 205636270Swpaul */ 205736270Swpaul ifp->if_timer = 5; 205867087Swpaul TL_UNLOCK(sc); 205936270Swpaul 206036270Swpaul return; 206136270Swpaul} 206236270Swpaul 2063102336Salfredstatic void 2064102336Salfredtl_init(xsc) 206536270Swpaul void *xsc; 206636270Swpaul{ 206736270Swpaul struct tl_softc *sc = xsc; 206836270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 206950462Swpaul struct mii_data *mii; 207036270Swpaul 207167087Swpaul TL_LOCK(sc); 207236270Swpaul 207336270Swpaul ifp = &sc->arpcom.ac_if; 207436270Swpaul 207536270Swpaul /* 207636270Swpaul * Cancel pending I/O. 207736270Swpaul */ 207836270Swpaul tl_stop(sc); 207936270Swpaul 208051439Swpaul /* Initialize TX FIFO threshold */ 208151439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 208251439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 208351439Swpaul 208451439Swpaul /* Set PCI burst size */ 208551439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 208651439Swpaul 208736270Swpaul /* 208836270Swpaul * Set 'capture all frames' bit for promiscuous mode. 208936270Swpaul */ 209039583Swpaul if (ifp->if_flags & IFF_PROMISC) 209139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 209239583Swpaul else 209339583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 209436270Swpaul 209536270Swpaul /* 209636270Swpaul * Set capture broadcast bit to capture broadcast frames. 209736270Swpaul */ 209839583Swpaul if (ifp->if_flags & IFF_BROADCAST) 209939583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210039583Swpaul else 210139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210236270Swpaul 210350468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 210450468Swpaul 210536270Swpaul /* Init our MAC address */ 210641656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 210736270Swpaul 210839583Swpaul /* Init multicast filter, if needed. */ 210939583Swpaul tl_setmulti(sc); 211039583Swpaul 211136270Swpaul /* Init circular RX list. */ 211239583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2113105599Sbrooks if_printf(ifp, 2114105599Sbrooks "initialization failed: no memory for rx buffers\n"); 211539583Swpaul tl_stop(sc); 211667087Swpaul TL_UNLOCK(sc); 211736270Swpaul return; 211836270Swpaul } 211936270Swpaul 212036270Swpaul /* Init TX pointers. */ 212136270Swpaul tl_list_tx_init(sc); 212236270Swpaul 212339583Swpaul /* Enable PCI interrupts. */ 212439583Swpaul CMD_SET(sc, TL_CMD_INTSON); 212536270Swpaul 212636270Swpaul /* Load the address of the rx list */ 212739583Swpaul CMD_SET(sc, TL_CMD_RT); 212839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 212936270Swpaul 213050462Swpaul if (!sc->tl_bitrate) { 213150462Swpaul if (sc->tl_miibus != NULL) { 213250462Swpaul mii = device_get_softc(sc->tl_miibus); 213350462Swpaul mii_mediachg(mii); 213450462Swpaul } 213550462Swpaul } 213638030Swpaul 213736270Swpaul /* Send the RX go command */ 213850468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 213936270Swpaul 214036270Swpaul ifp->if_flags |= IFF_RUNNING; 214136270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 214236270Swpaul 214336270Swpaul /* Start the stats update counter */ 214436270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 214567087Swpaul TL_UNLOCK(sc); 214636270Swpaul 214736270Swpaul return; 214836270Swpaul} 214936270Swpaul 215036270Swpaul/* 215136270Swpaul * Set media options. 215236270Swpaul */ 2153102336Salfredstatic int 2154102336Salfredtl_ifmedia_upd(ifp) 215536270Swpaul struct ifnet *ifp; 215636270Swpaul{ 215736270Swpaul struct tl_softc *sc; 215850462Swpaul struct mii_data *mii = NULL; 215936270Swpaul 216036270Swpaul sc = ifp->if_softc; 216136270Swpaul 216250462Swpaul if (sc->tl_bitrate) 216350462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 216450462Swpaul else { 216550462Swpaul mii = device_get_softc(sc->tl_miibus); 216650462Swpaul mii_mediachg(mii); 216750462Swpaul } 216836270Swpaul 216936270Swpaul return(0); 217036270Swpaul} 217136270Swpaul 217236270Swpaul/* 217336270Swpaul * Report current media status. 217436270Swpaul */ 2175102336Salfredstatic void 2176102336Salfredtl_ifmedia_sts(ifp, ifmr) 217736270Swpaul struct ifnet *ifp; 217836270Swpaul struct ifmediareq *ifmr; 217936270Swpaul{ 218036270Swpaul struct tl_softc *sc; 218150462Swpaul struct mii_data *mii; 218236270Swpaul 218336270Swpaul sc = ifp->if_softc; 218436270Swpaul 218536270Swpaul ifmr->ifm_active = IFM_ETHER; 218636270Swpaul 218745155Swpaul if (sc->tl_bitrate) { 218845155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 218945155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 219045155Swpaul else 219145155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 219245155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 219345155Swpaul ifmr->ifm_active |= IFM_HDX; 219445155Swpaul else 219545155Swpaul ifmr->ifm_active |= IFM_FDX; 219645155Swpaul return; 219736270Swpaul } else { 219850462Swpaul mii = device_get_softc(sc->tl_miibus); 219950462Swpaul mii_pollstat(mii); 220050462Swpaul ifmr->ifm_active = mii->mii_media_active; 220150462Swpaul ifmr->ifm_status = mii->mii_media_status; 220236270Swpaul } 220336270Swpaul 220436270Swpaul return; 220536270Swpaul} 220636270Swpaul 2207102336Salfredstatic int 2208102336Salfredtl_ioctl(ifp, command, data) 220936270Swpaul struct ifnet *ifp; 221036735Sdfr u_long command; 221136270Swpaul caddr_t data; 221236270Swpaul{ 221336270Swpaul struct tl_softc *sc = ifp->if_softc; 221436270Swpaul struct ifreq *ifr = (struct ifreq *) data; 221536270Swpaul int s, error = 0; 221636270Swpaul 221736270Swpaul s = splimp(); 221836270Swpaul 221936270Swpaul switch(command) { 222036270Swpaul case SIOCSIFADDR: 222136270Swpaul case SIOCGIFADDR: 222236270Swpaul case SIOCSIFMTU: 222336270Swpaul error = ether_ioctl(ifp, command, data); 222436270Swpaul break; 222536270Swpaul case SIOCSIFFLAGS: 222636270Swpaul if (ifp->if_flags & IFF_UP) { 222750462Swpaul if (ifp->if_flags & IFF_RUNNING && 222850462Swpaul ifp->if_flags & IFF_PROMISC && 222950462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 223050462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 223150462Swpaul tl_setmulti(sc); 223250462Swpaul } else if (ifp->if_flags & IFF_RUNNING && 223350462Swpaul !(ifp->if_flags & IFF_PROMISC) && 223450462Swpaul sc->tl_if_flags & IFF_PROMISC) { 223550462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 223650462Swpaul tl_setmulti(sc); 223750462Swpaul } else 223850462Swpaul tl_init(sc); 223936270Swpaul } else { 224036270Swpaul if (ifp->if_flags & IFF_RUNNING) { 224136270Swpaul tl_stop(sc); 224236270Swpaul } 224336270Swpaul } 224450462Swpaul sc->tl_if_flags = ifp->if_flags; 224536270Swpaul error = 0; 224636270Swpaul break; 224736270Swpaul case SIOCADDMULTI: 224836270Swpaul case SIOCDELMULTI: 224936270Swpaul tl_setmulti(sc); 225036270Swpaul error = 0; 225136270Swpaul break; 225236270Swpaul case SIOCSIFMEDIA: 225336270Swpaul case SIOCGIFMEDIA: 225450462Swpaul if (sc->tl_bitrate) 225550462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 225650462Swpaul else { 225750462Swpaul struct mii_data *mii; 225850462Swpaul mii = device_get_softc(sc->tl_miibus); 225950462Swpaul error = ifmedia_ioctl(ifp, ifr, 226050462Swpaul &mii->mii_media, command); 226150462Swpaul } 226236270Swpaul break; 226336270Swpaul default: 226436270Swpaul error = EINVAL; 226536270Swpaul break; 226636270Swpaul } 226736270Swpaul 226836270Swpaul (void)splx(s); 226936270Swpaul 227036270Swpaul return(error); 227136270Swpaul} 227236270Swpaul 2273102336Salfredstatic void 2274102336Salfredtl_watchdog(ifp) 227536270Swpaul struct ifnet *ifp; 227636270Swpaul{ 227736270Swpaul struct tl_softc *sc; 227836270Swpaul 227936270Swpaul sc = ifp->if_softc; 228036270Swpaul 2281105599Sbrooks if_printf(ifp, "device timeout\n"); 228236270Swpaul 228336270Swpaul ifp->if_oerrors++; 228436270Swpaul 228550468Swpaul tl_softreset(sc, 1); 228636270Swpaul tl_init(sc); 228736270Swpaul 228836270Swpaul return; 228936270Swpaul} 229036270Swpaul 229136270Swpaul/* 229236270Swpaul * Stop the adapter and free any mbufs allocated to the 229336270Swpaul * RX and TX lists. 229436270Swpaul */ 2295102336Salfredstatic void 2296102336Salfredtl_stop(sc) 229736270Swpaul struct tl_softc *sc; 229836270Swpaul{ 229936270Swpaul register int i; 230036270Swpaul struct ifnet *ifp; 230136270Swpaul 230267087Swpaul TL_LOCK(sc); 230367087Swpaul 230436270Swpaul ifp = &sc->arpcom.ac_if; 230536270Swpaul 230636270Swpaul /* Stop the stats updater. */ 230736270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 230836270Swpaul 230936270Swpaul /* Stop the transmitter */ 231039583Swpaul CMD_CLR(sc, TL_CMD_RT); 231139583Swpaul CMD_SET(sc, TL_CMD_STOP); 231239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231336270Swpaul 231436270Swpaul /* Stop the receiver */ 231539583Swpaul CMD_SET(sc, TL_CMD_RT); 231639583Swpaul CMD_SET(sc, TL_CMD_STOP); 231739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231836270Swpaul 231936270Swpaul /* 232036270Swpaul * Disable host interrupts. 232136270Swpaul */ 232239583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 232336270Swpaul 232436270Swpaul /* 232536270Swpaul * Clear list pointer. 232636270Swpaul */ 232739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 232836270Swpaul 232936270Swpaul /* 233036270Swpaul * Free the RX lists. 233136270Swpaul */ 233236270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 233336270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 233436270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 233536270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 233636270Swpaul } 233736270Swpaul } 233836270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 233936270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 234036270Swpaul 234136270Swpaul /* 234236270Swpaul * Free the TX list buffers. 234336270Swpaul */ 234436270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 234536270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 234636270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 234736270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 234836270Swpaul } 234936270Swpaul } 235036270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 235136270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 235236270Swpaul 235336270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 235467087Swpaul TL_UNLOCK(sc); 235536270Swpaul 235636270Swpaul return; 235736270Swpaul} 235836270Swpaul 235936270Swpaul/* 236036270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 236136270Swpaul * get confused by errant DMAs when rebooting. 236236270Swpaul */ 2363102336Salfredstatic void 2364102336Salfredtl_shutdown(dev) 236548992Swpaul device_t dev; 236636270Swpaul{ 236739583Swpaul struct tl_softc *sc; 236836270Swpaul 236948992Swpaul sc = device_get_softc(dev); 237036270Swpaul 237139583Swpaul tl_stop(sc); 237236270Swpaul 237336270Swpaul return; 237436270Swpaul} 2375