if_tl.c revision 56060
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 56060 2000-01-15 20:14:49Z wpaul $ 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 */ 19936270Swpaul#include <machine/clock.h> /* for DELAY */ 20045155Swpaul#include <machine/bus_memio.h> 20145155Swpaul#include <machine/bus_pio.h> 20245155Swpaul#include <machine/bus.h> 20348992Swpaul#include <machine/resource.h> 20448992Swpaul#include <sys/bus.h> 20548992Swpaul#include <sys/rman.h> 20636270Swpaul 20750462Swpaul#include <dev/mii/mii.h> 20850462Swpaul#include <dev/mii/miivar.h> 20950462Swpaul 21036270Swpaul#include <pci/pcireg.h> 21136270Swpaul#include <pci/pcivar.h> 21236270Swpaul 21339957Swpaul/* 21439957Swpaul * Default to using PIO register access mode to pacify certain 21539957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21639957Swpaul * don't seem to handle memory mapped mode properly. 21739957Swpaul */ 21839957Swpaul#define TL_USEIOSPACE 21939957Swpaul 22036270Swpaul#include <pci/if_tlreg.h> 22136270Swpaul 22251089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 22350462Swpaul#include "miibus_if.h" 22450462Swpaul 22541591Sarchie#if !defined(lint) 22641591Sarchiestatic const char rcsid[] = 22750477Speter "$FreeBSD: head/sys/pci/if_tl.c 56060 2000-01-15 20:14:49Z wpaul $"; 22836270Swpaul#endif 22936270Swpaul 23036270Swpaul/* 23136270Swpaul * Various supported device vendors/types and their names. 23236270Swpaul */ 23336270Swpaul 23436270Swpaulstatic struct tl_type tl_devs[] = { 23536270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23636270Swpaul "Texas Instruments ThunderLAN" }, 23736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23836270Swpaul "Compaq Netelligent 10" }, 23936270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 24036270Swpaul "Compaq Netelligent 10/100" }, 24136270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24236270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24336270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24436270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24536270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24636270Swpaul "Compaq NetFlex-3/P Integrated" }, 24736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24836270Swpaul "Compaq NetFlex-3/P" }, 24936270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 25036270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25137626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25237626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25337626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25437626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25537626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25637626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25737626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25837626Swpaul "Olicom OC-2183/2185" }, 25937626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 26037626Swpaul "Olicom OC-2325" }, 26137626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26237626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26336270Swpaul { 0, 0, NULL } 26436270Swpaul}; 26536270Swpaul 26648992Swpaulstatic int tl_probe __P((device_t)); 26748992Swpaulstatic int tl_attach __P((device_t)); 26848992Swpaulstatic int tl_detach __P((device_t)); 26936270Swpaulstatic int tl_intvec_rxeoc __P((void *, u_int32_t)); 27036270Swpaulstatic int tl_intvec_txeoc __P((void *, u_int32_t)); 27136270Swpaulstatic int tl_intvec_txeof __P((void *, u_int32_t)); 27236270Swpaulstatic int tl_intvec_rxeof __P((void *, u_int32_t)); 27336270Swpaulstatic int tl_intvec_adchk __P((void *, u_int32_t)); 27436270Swpaulstatic int tl_intvec_netsts __P((void *, u_int32_t)); 27536270Swpaul 27637626Swpaulstatic int tl_newbuf __P((struct tl_softc *, 27737626Swpaul struct tl_chain_onefrag *)); 27836270Swpaulstatic void tl_stats_update __P((void *)); 27936270Swpaulstatic int tl_encap __P((struct tl_softc *, struct tl_chain *, 28036270Swpaul struct mbuf *)); 28136270Swpaul 28236270Swpaulstatic void tl_intr __P((void *)); 28336270Swpaulstatic void tl_start __P((struct ifnet *)); 28436735Sdfrstatic int tl_ioctl __P((struct ifnet *, u_long, caddr_t)); 28536270Swpaulstatic void tl_init __P((void *)); 28636270Swpaulstatic void tl_stop __P((struct tl_softc *)); 28736270Swpaulstatic void tl_watchdog __P((struct ifnet *)); 28848992Swpaulstatic void tl_shutdown __P((device_t)); 28936270Swpaulstatic int tl_ifmedia_upd __P((struct ifnet *)); 29036270Swpaulstatic void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 29136270Swpaul 29241656Swpaulstatic u_int8_t tl_eeprom_putbyte __P((struct tl_softc *, int)); 29339583Swpaulstatic u_int8_t tl_eeprom_getbyte __P((struct tl_softc *, 29441656Swpaul int, u_int8_t *)); 29539583Swpaulstatic int tl_read_eeprom __P((struct tl_softc *, caddr_t, int, int)); 29636270Swpaul 29739583Swpaulstatic void tl_mii_sync __P((struct tl_softc *)); 29839583Swpaulstatic void tl_mii_send __P((struct tl_softc *, u_int32_t, int)); 29939583Swpaulstatic int tl_mii_readreg __P((struct tl_softc *, struct tl_mii_frame *)); 30039583Swpaulstatic int tl_mii_writereg __P((struct tl_softc *, struct tl_mii_frame *)); 30150462Swpaulstatic int tl_miibus_readreg __P((device_t, int, int)); 30250462Swpaulstatic int tl_miibus_writereg __P((device_t, int, int, int)); 30350462Swpaulstatic void tl_miibus_statchg __P((device_t)); 30436270Swpaul 30536270Swpaulstatic void tl_setmode __P((struct tl_softc *, int)); 30641656Swpaulstatic int tl_calchash __P((caddr_t)); 30736270Swpaulstatic void tl_setmulti __P((struct tl_softc *)); 30841656Swpaulstatic void tl_setfilt __P((struct tl_softc *, caddr_t, int)); 30939583Swpaulstatic void tl_softreset __P((struct tl_softc *, int)); 31050468Swpaulstatic void tl_hardreset __P((device_t)); 31136270Swpaulstatic int tl_list_rx_init __P((struct tl_softc *)); 31236270Swpaulstatic int tl_list_tx_init __P((struct tl_softc *)); 31336270Swpaul 31441656Swpaulstatic u_int8_t tl_dio_read8 __P((struct tl_softc *, int)); 31541656Swpaulstatic u_int16_t tl_dio_read16 __P((struct tl_softc *, int)); 31641656Swpaulstatic u_int32_t tl_dio_read32 __P((struct tl_softc *, int)); 31741656Swpaulstatic void tl_dio_write8 __P((struct tl_softc *, int, int)); 31841656Swpaulstatic void tl_dio_write16 __P((struct tl_softc *, int, int)); 31941656Swpaulstatic void tl_dio_write32 __P((struct tl_softc *, int, int)); 32041656Swpaulstatic void tl_dio_setbit __P((struct tl_softc *, int, int)); 32141656Swpaulstatic void tl_dio_clrbit __P((struct tl_softc *, int, int)); 32241656Swpaulstatic void tl_dio_setbit16 __P((struct tl_softc *, int, int)); 32341656Swpaulstatic void tl_dio_clrbit16 __P((struct tl_softc *, int, int)); 32439583Swpaul 32549010Swpaul#ifdef TL_USEIOSPACE 32649010Swpaul#define TL_RES SYS_RES_IOPORT 32749010Swpaul#define TL_RID TL_PCI_LOIO 32849010Swpaul#else 32949010Swpaul#define TL_RES SYS_RES_MEMORY 33049010Swpaul#define TL_RID TL_PCI_LOMEM 33149010Swpaul#endif 33249010Swpaul 33348992Swpaulstatic device_method_t tl_methods[] = { 33448992Swpaul /* Device interface */ 33548992Swpaul DEVMETHOD(device_probe, tl_probe), 33648992Swpaul DEVMETHOD(device_attach, tl_attach), 33748992Swpaul DEVMETHOD(device_detach, tl_detach), 33848992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33950462Swpaul 34050462Swpaul /* bus interface */ 34150462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 34250462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 34350462Swpaul 34450462Swpaul /* MII interface */ 34550462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34650462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34750462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34850462Swpaul 34948992Swpaul { 0, 0 } 35048992Swpaul}; 35148992Swpaul 35248992Swpaulstatic driver_t tl_driver = { 35351455Swpaul "tl", 35448992Swpaul tl_methods, 35548992Swpaul sizeof(struct tl_softc) 35648992Swpaul}; 35748992Swpaul 35848992Swpaulstatic devclass_t tl_devclass; 35948992Swpaul 36051533SwpaulDRIVER_MODULE(if_tl, pci, tl_driver, tl_devclass, 0, 0); 36151473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 36248992Swpaul 36339583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 36441656Swpaul struct tl_softc *sc; 36541656Swpaul int reg; 36639583Swpaul{ 36739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36839583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36939583Swpaul} 37039583Swpaul 37139583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 37241656Swpaul struct tl_softc *sc; 37341656Swpaul int reg; 37439583Swpaul{ 37539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37639583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37739583Swpaul} 37839583Swpaul 37939583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 38041656Swpaul struct tl_softc *sc; 38141656Swpaul int reg; 38239583Swpaul{ 38339583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38439583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38539583Swpaul} 38639583Swpaul 38739583Swpaulstatic void tl_dio_write8(sc, reg, val) 38841656Swpaul struct tl_softc *sc; 38941656Swpaul int reg; 39041656Swpaul int val; 39139583Swpaul{ 39239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39339583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 39439583Swpaul return; 39539583Swpaul} 39639583Swpaul 39739583Swpaulstatic void tl_dio_write16(sc, reg, val) 39841656Swpaul struct tl_softc *sc; 39941656Swpaul int reg; 40041656Swpaul int val; 40139583Swpaul{ 40239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40339583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 40439583Swpaul return; 40539583Swpaul} 40639583Swpaul 40739583Swpaulstatic void tl_dio_write32(sc, reg, val) 40841656Swpaul struct tl_softc *sc; 40941656Swpaul int reg; 41041656Swpaul int val; 41139583Swpaul{ 41239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 41339583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 41439583Swpaul return; 41539583Swpaul} 41639583Swpaul 41739583Swpaulstatic void tl_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 43239583Swpaulstatic void tl_dio_clrbit(sc, reg, bit) 43341656Swpaul struct tl_softc *sc; 43441656Swpaul int reg; 43541656Swpaul int bit; 43639583Swpaul{ 43739583Swpaul u_int8_t f; 43839583Swpaul 43939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 44039583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 44139583Swpaul f &= ~bit; 44239583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44339583Swpaul 44439583Swpaul return; 44539583Swpaul} 44639583Swpaul 44739583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44841656Swpaul struct tl_softc *sc; 44941656Swpaul int reg; 45041656Swpaul int bit; 45139583Swpaul{ 45239583Swpaul u_int16_t f; 45339583Swpaul 45439583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45539583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45639583Swpaul f |= bit; 45739583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45839583Swpaul 45939583Swpaul return; 46039583Swpaul} 46139583Swpaul 46239583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46341656Swpaul struct tl_softc *sc; 46441656Swpaul int reg; 46541656Swpaul int bit; 46639583Swpaul{ 46739583Swpaul u_int16_t f; 46839583Swpaul 46939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 47039583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 47139583Swpaul f &= ~bit; 47239583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47339583Swpaul 47439583Swpaul return; 47539583Swpaul} 47639583Swpaul 47736270Swpaul/* 47836270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 47936270Swpaul */ 48039583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 48139583Swpaul struct tl_softc *sc; 48241656Swpaul int byte; 48336270Swpaul{ 48436270Swpaul register int i, ack = 0; 48536270Swpaul 48636270Swpaul /* 48736270Swpaul * Make sure we're in TX mode. 48836270Swpaul */ 48939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 49036270Swpaul 49136270Swpaul /* 49236270Swpaul * Feed in each bit and stobe the clock. 49336270Swpaul */ 49436270Swpaul for (i = 0x80; i; i >>= 1) { 49536270Swpaul if (byte & i) { 49639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49736270Swpaul } else { 49839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 49936270Swpaul } 50039583Swpaul DELAY(1); 50139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50239583Swpaul DELAY(1); 50339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50436270Swpaul } 50536270Swpaul 50636270Swpaul /* 50736270Swpaul * Turn off TX mode. 50836270Swpaul */ 50939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 51036270Swpaul 51136270Swpaul /* 51236270Swpaul * Check for ack. 51336270Swpaul */ 51439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51539583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51736270Swpaul 51836270Swpaul return(ack); 51936270Swpaul} 52036270Swpaul 52136270Swpaul/* 52236270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52336270Swpaul */ 52439583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52539583Swpaul struct tl_softc *sc; 52641656Swpaul int addr; 52736270Swpaul u_int8_t *dest; 52836270Swpaul{ 52936270Swpaul register int i; 53036270Swpaul u_int8_t byte = 0; 53136270Swpaul 53239583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53339583Swpaul 53436270Swpaul EEPROM_START; 53539583Swpaul 53636270Swpaul /* 53736270Swpaul * Send write control code to EEPROM. 53836270Swpaul */ 53939583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 54039583Swpaul printf("tl%d: failed to send write command, status: %x\n", 54139583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 54236270Swpaul return(1); 54339583Swpaul } 54436270Swpaul 54536270Swpaul /* 54636270Swpaul * Send address of byte we want to read. 54736270Swpaul */ 54839583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 54939583Swpaul printf("tl%d: failed to send address, status: %x\n", 55039583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 55136270Swpaul return(1); 55239583Swpaul } 55336270Swpaul 55436270Swpaul EEPROM_STOP; 55536270Swpaul EEPROM_START; 55636270Swpaul /* 55736270Swpaul * Send read control code to EEPROM. 55836270Swpaul */ 55939583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 56039583Swpaul printf("tl%d: failed to send write command, status: %x\n", 56139583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 56236270Swpaul return(1); 56339583Swpaul } 56436270Swpaul 56536270Swpaul /* 56636270Swpaul * Start reading bits from EEPROM. 56736270Swpaul */ 56839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56936270Swpaul for (i = 0x80; i; i >>= 1) { 57039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57139583Swpaul DELAY(1); 57239583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57336270Swpaul byte |= i; 57439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57536501Swpaul DELAY(1); 57636270Swpaul } 57736270Swpaul 57836270Swpaul EEPROM_STOP; 57936270Swpaul 58036270Swpaul /* 58136270Swpaul * No ACK generated for read, so just return byte. 58236270Swpaul */ 58336270Swpaul 58436270Swpaul *dest = byte; 58536270Swpaul 58636270Swpaul return(0); 58736270Swpaul} 58836270Swpaul 58939583Swpaul/* 59039583Swpaul * Read a sequence of bytes from the EEPROM. 59139583Swpaul */ 59239583Swpaulstatic int tl_read_eeprom(sc, dest, off, cnt) 59339583Swpaul struct tl_softc *sc; 59439583Swpaul caddr_t dest; 59539583Swpaul int off; 59639583Swpaul int cnt; 59736270Swpaul{ 59839583Swpaul int err = 0, i; 59939583Swpaul u_int8_t byte = 0; 60039583Swpaul 60139583Swpaul for (i = 0; i < cnt; i++) { 60239583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60339583Swpaul if (err) 60439583Swpaul break; 60539583Swpaul *(dest + i) = byte; 60639583Swpaul } 60739583Swpaul 60839583Swpaul return(err ? 1 : 0); 60939583Swpaul} 61039583Swpaul 61139583Swpaulstatic void tl_mii_sync(sc) 61239583Swpaul struct tl_softc *sc; 61339583Swpaul{ 61436270Swpaul register int i; 61536270Swpaul 61639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 61736270Swpaul 61836270Swpaul for (i = 0; i < 32; i++) { 61939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62136270Swpaul } 62236270Swpaul 62336270Swpaul return; 62436270Swpaul} 62536270Swpaul 62639583Swpaulstatic void tl_mii_send(sc, bits, cnt) 62739583Swpaul struct tl_softc *sc; 62836270Swpaul u_int32_t bits; 62936270Swpaul int cnt; 63036270Swpaul{ 63136270Swpaul int i; 63236270Swpaul 63336270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63536270Swpaul if (bits & i) { 63639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 63736270Swpaul } else { 63839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 63936270Swpaul } 64039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64136270Swpaul } 64236270Swpaul} 64336270Swpaul 64439583Swpaulstatic int tl_mii_readreg(sc, frame) 64539583Swpaul struct tl_softc *sc; 64636270Swpaul struct tl_mii_frame *frame; 64736270Swpaul 64836270Swpaul{ 64936270Swpaul int i, ack, s; 65036270Swpaul int minten = 0; 65136270Swpaul 65236270Swpaul s = splimp(); 65336270Swpaul 65439583Swpaul tl_mii_sync(sc); 65536270Swpaul 65636270Swpaul /* 65736270Swpaul * Set up frame for RX. 65836270Swpaul */ 65936270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66036270Swpaul frame->mii_opcode = TL_MII_READOP; 66136270Swpaul frame->mii_turnaround = 0; 66236270Swpaul frame->mii_data = 0; 66336270Swpaul 66436270Swpaul /* 66536270Swpaul * Turn off MII interrupt by forcing MINTEN low. 66636270Swpaul */ 66739583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 66836270Swpaul if (minten) { 66939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67036270Swpaul } 67136270Swpaul 67236270Swpaul /* 67336270Swpaul * Turn on data xmit. 67436270Swpaul */ 67539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 67636270Swpaul 67736270Swpaul /* 67836270Swpaul * Send command/address info. 67936270Swpaul */ 68039583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68139583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68239583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68339583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 68436270Swpaul 68536270Swpaul /* 68636270Swpaul * Turn off xmit. 68736270Swpaul */ 68839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 68936270Swpaul 69036270Swpaul /* Idle bit */ 69139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69336270Swpaul 69436270Swpaul /* Check for ack */ 69539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69639583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 69736270Swpaul 69836270Swpaul /* Complete the cycle */ 69939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70036270Swpaul 70136270Swpaul /* 70236270Swpaul * Now try reading data bits. If the ack failed, we still 70336270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 70436270Swpaul */ 70536270Swpaul if (ack) { 70636270Swpaul for(i = 0; i < 16; i++) { 70739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 70839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70936270Swpaul } 71036270Swpaul goto fail; 71136270Swpaul } 71236270Swpaul 71336270Swpaul for (i = 0x8000; i; i >>= 1) { 71439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71536270Swpaul if (!ack) { 71639583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 71736270Swpaul frame->mii_data |= i; 71836270Swpaul } 71939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72036270Swpaul } 72136270Swpaul 72236270Swpaulfail: 72336270Swpaul 72439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72636270Swpaul 72736270Swpaul /* Reenable interrupts */ 72836270Swpaul if (minten) { 72939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73036270Swpaul } 73136270Swpaul 73236270Swpaul splx(s); 73336270Swpaul 73436270Swpaul if (ack) 73536270Swpaul return(1); 73636270Swpaul return(0); 73736270Swpaul} 73836270Swpaul 73939583Swpaulstatic int tl_mii_writereg(sc, frame) 74039583Swpaul struct tl_softc *sc; 74136270Swpaul struct tl_mii_frame *frame; 74236270Swpaul 74336270Swpaul{ 74436270Swpaul int s; 74536270Swpaul int minten; 74636270Swpaul 74739583Swpaul tl_mii_sync(sc); 74836270Swpaul 74936270Swpaul s = splimp(); 75036270Swpaul /* 75136270Swpaul * Set up frame for TX. 75236270Swpaul */ 75336270Swpaul 75436270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 75536270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 75636270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 75736270Swpaul 75836270Swpaul /* 75936270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76036270Swpaul */ 76139583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76236270Swpaul if (minten) { 76339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76436270Swpaul } 76536270Swpaul 76636270Swpaul /* 76736270Swpaul * Turn on data output. 76836270Swpaul */ 76939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77036270Swpaul 77139583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77239583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77339583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77439583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 77539583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 77639583Swpaul tl_mii_send(sc, frame->mii_data, 16); 77736270Swpaul 77839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 77939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78036270Swpaul 78136270Swpaul /* 78236270Swpaul * Turn off xmit. 78336270Swpaul */ 78439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 78536270Swpaul 78636270Swpaul /* Reenable interrupts */ 78736270Swpaul if (minten) 78839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 78936270Swpaul 79036270Swpaul splx(s); 79136270Swpaul 79236270Swpaul return(0); 79336270Swpaul} 79436270Swpaul 79550462Swpaulstatic int tl_miibus_readreg(dev, phy, reg) 79650462Swpaul device_t dev; 79750462Swpaul int phy, reg; 79850462Swpaul{ 79936270Swpaul struct tl_softc *sc; 80036270Swpaul struct tl_mii_frame frame; 80136270Swpaul 80250462Swpaul sc = device_get_softc(dev); 80336270Swpaul bzero((char *)&frame, sizeof(frame)); 80436270Swpaul 80550462Swpaul frame.mii_phyaddr = phy; 80636270Swpaul frame.mii_regaddr = reg; 80739583Swpaul tl_mii_readreg(sc, &frame); 80836270Swpaul 80936270Swpaul return(frame.mii_data); 81036270Swpaul} 81136270Swpaul 81250462Swpaulstatic int tl_miibus_writereg(dev, phy, reg, data) 81350462Swpaul device_t dev; 81450462Swpaul int phy, reg, data; 81550462Swpaul{ 81636270Swpaul struct tl_softc *sc; 81736270Swpaul struct tl_mii_frame frame; 81836270Swpaul 81950462Swpaul sc = device_get_softc(dev); 82036270Swpaul bzero((char *)&frame, sizeof(frame)); 82136270Swpaul 82250462Swpaul frame.mii_phyaddr = phy; 82336270Swpaul frame.mii_regaddr = reg; 82436270Swpaul frame.mii_data = data; 82536270Swpaul 82639583Swpaul tl_mii_writereg(sc, &frame); 82736270Swpaul 82850462Swpaul return(0); 82936270Swpaul} 83036270Swpaul 83150462Swpaulstatic void tl_miibus_statchg(dev) 83250462Swpaul device_t dev; 83350462Swpaul{ 83436270Swpaul struct tl_softc *sc; 83550462Swpaul struct mii_data *mii; 83636270Swpaul 83750462Swpaul sc = device_get_softc(dev); 83850462Swpaul mii = device_get_softc(sc->tl_miibus); 83936270Swpaul 84050462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 84150462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84236270Swpaul } else { 84350462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84436270Swpaul } 84536270Swpaul 84636270Swpaul return; 84736270Swpaul} 84836270Swpaul 84936270Swpaul/* 85050462Swpaul * Set modes for bitrate devices. 85136270Swpaul */ 85236270Swpaulstatic void tl_setmode(sc, media) 85336270Swpaul struct tl_softc *sc; 85436270Swpaul int media; 85536270Swpaul{ 85650462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 85750462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 85836270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 85950462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86036270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 86150462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86336270Swpaul } else { 86450462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86539583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86636270Swpaul } 86736270Swpaul } 86836270Swpaul 86936270Swpaul return; 87036270Swpaul} 87136270Swpaul 87236464Swpaul/* 87336464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 87436464Swpaul * table. This hash is simply the address split into 6-bit chunks 87536464Swpaul * XOR'd, e.g. 87636464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 87736464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 87836464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 87936464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 88036464Swpaul */ 88136270Swpaulstatic int tl_calchash(addr) 88241656Swpaul caddr_t addr; 88336270Swpaul{ 88437626Swpaul int t; 88536270Swpaul 88636464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 88736464Swpaul (addr[2] ^ addr[5]); 88836464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 88936270Swpaul} 89036270Swpaul 89139583Swpaul/* 89239583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 89339583Swpaul * the multicast hash filter. The perfect filter can be programmed 89439583Swpaul * with up to four MAC addresses. The first one is always used to 89539583Swpaul * hold the station address, which leaves us free to use the other 89639583Swpaul * three for multicast addresses. 89739583Swpaul */ 89839583Swpaulstatic void tl_setfilt(sc, addr, slot) 89939583Swpaul struct tl_softc *sc; 90041656Swpaul caddr_t addr; 90139583Swpaul int slot; 90239583Swpaul{ 90339583Swpaul int i; 90439583Swpaul u_int16_t regaddr; 90539583Swpaul 90639583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 90739583Swpaul 90839583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 90939583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 91039583Swpaul 91139583Swpaul return; 91239583Swpaul} 91339583Swpaul 91439583Swpaul/* 91539583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 91639583Swpaul * linked list. This is fine, except addresses are added from the head 91739583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 91839583Swpaul * group to always be in the perfect filter, but as more groups are added, 91939583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 92039583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 92139583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 92239583Swpaul * and into the hash table. 92339583Swpaul * 92439583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 92539583Swpaul * circular queue, we don't have the ability to just grab the tail of 92639583Swpaul * the list and traverse it backwards. Instead, we have to traverse 92739583Swpaul * the list once to find the tail, then traverse it again backwards to 92839583Swpaul * update the multicast filter. 92939583Swpaul */ 93036270Swpaulstatic void tl_setmulti(sc) 93136270Swpaul struct tl_softc *sc; 93236270Swpaul{ 93336270Swpaul struct ifnet *ifp; 93436270Swpaul u_int32_t hashes[2] = { 0, 0 }; 93539583Swpaul int h, i; 93636270Swpaul struct ifmultiaddr *ifma; 93739583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 93836270Swpaul ifp = &sc->arpcom.ac_if; 93936270Swpaul 94039583Swpaul /* First, zot all the existing filters. */ 94139583Swpaul for (i = 1; i < 4; i++) 94241656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 94339583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 94439583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 94539583Swpaul 94639583Swpaul /* Now program new ones. */ 94739583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 94836270Swpaul hashes[0] = 0xFFFFFFFF; 94936270Swpaul hashes[1] = 0xFFFFFFFF; 95036270Swpaul } else { 95139583Swpaul i = 1; 95239583Swpaul /* First find the tail of the list. */ 95336270Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 95436270Swpaul ifma = ifma->ifma_link.le_next) { 95539583Swpaul if (ifma->ifma_link.le_next == NULL) 95639583Swpaul break; 95739583Swpaul } 95839583Swpaul /* Now traverse the list backwards. */ 95939583Swpaul for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs; 96039583Swpaul ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) { 96136270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 96236270Swpaul continue; 96339583Swpaul /* 96439583Swpaul * Program the first three multicast groups 96539583Swpaul * into the perfect filter. For all others, 96639583Swpaul * use the hash table. 96739583Swpaul */ 96839583Swpaul if (i < 4) { 96939583Swpaul tl_setfilt(sc, 97039583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97139583Swpaul i++; 97239583Swpaul continue; 97339583Swpaul } 97439583Swpaul 97536270Swpaul h = tl_calchash( 97636270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 97736270Swpaul if (h < 32) 97836270Swpaul hashes[0] |= (1 << h); 97936270Swpaul else 98036317Swpaul hashes[1] |= (1 << (h - 32)); 98136270Swpaul } 98236270Swpaul } 98336270Swpaul 98439583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 98539583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 98636270Swpaul 98736270Swpaul return; 98836270Swpaul} 98936270Swpaul 99039583Swpaul/* 99139583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 99239583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99339583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 99439583Swpaul * experience this isn't necessary. 99539583Swpaul */ 99650468Swpaulstatic void tl_hardreset(dev) 99750468Swpaul device_t dev; 99850468Swpaul{ 99939583Swpaul struct tl_softc *sc; 100039583Swpaul int i; 100150468Swpaul u_int16_t flags; 100239583Swpaul 100350468Swpaul sc = device_get_softc(dev); 100439583Swpaul 100550468Swpaul tl_mii_sync(sc); 100639583Swpaul 100750468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 100839583Swpaul 100950468Swpaul for (i = 0; i < MII_NPHY; i++) 101050468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 101139583Swpaul 101250468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 101339583Swpaul DELAY(50000); 101450468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 101539583Swpaul tl_mii_sync(sc); 101650468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 101739583Swpaul 101850468Swpaul DELAY(50000); 101939583Swpaul return; 102039583Swpaul} 102139583Swpaul 102239583Swpaulstatic void tl_softreset(sc, internal) 102339583Swpaul struct tl_softc *sc; 102436270Swpaul int internal; 102536270Swpaul{ 102639583Swpaul u_int32_t cmd, dummy, i; 102736270Swpaul 102836270Swpaul /* Assert the adapter reset bit. */ 102939583Swpaul CMD_SET(sc, TL_CMD_ADRST); 103050468Swpaul 103136270Swpaul /* Turn off interrupts */ 103239583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 103336270Swpaul 103436270Swpaul /* First, clear the stats registers. */ 103539583Swpaul for (i = 0; i < 5; i++) 103639583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 103736270Swpaul 103836270Swpaul /* Clear Areg and Hash registers */ 103939583Swpaul for (i = 0; i < 8; i++) 104039583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104136270Swpaul 104236270Swpaul /* 104336270Swpaul * Set up Netconfig register. Enable one channel and 104436270Swpaul * one fragment mode. 104536270Swpaul */ 104639583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 104745155Swpaul if (internal && !sc->tl_bitrate) { 104839583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 104936270Swpaul } else { 105039583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105136270Swpaul } 105236270Swpaul 105345155Swpaul /* Handle cards with bitrate devices. */ 105445155Swpaul if (sc->tl_bitrate) 105545155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 105645155Swpaul 105736270Swpaul /* 105836270Swpaul * Load adapter irq pacing timer and tx threshold. 105936270Swpaul * We make the transmit threshold 1 initially but we may 106036270Swpaul * change that later. 106136270Swpaul */ 106239583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 106336270Swpaul cmd |= TL_CMD_NES; 106436270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 106539583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 106639583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 106736270Swpaul 106836270Swpaul /* Unreset the MII */ 106939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 107036270Swpaul 107136270Swpaul /* Take the adapter out of reset */ 107239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 107336270Swpaul 107436270Swpaul /* Wait for things to settle down a little. */ 107536270Swpaul DELAY(500); 107636270Swpaul 107736270Swpaul return; 107836270Swpaul} 107936270Swpaul 108036270Swpaul/* 108136270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 108239583Swpaul * against our list and return its name if we find a match. 108336270Swpaul */ 108448992Swpaulstatic int tl_probe(dev) 108548992Swpaul device_t dev; 108636270Swpaul{ 108736270Swpaul struct tl_type *t; 108836270Swpaul 108936270Swpaul t = tl_devs; 109036270Swpaul 109136270Swpaul while(t->tl_name != NULL) { 109248992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 109348992Swpaul (pci_get_device(dev) == t->tl_did)) { 109448992Swpaul device_set_desc(dev, t->tl_name); 109548992Swpaul return(0); 109648992Swpaul } 109736270Swpaul t++; 109836270Swpaul } 109936270Swpaul 110048992Swpaul return(ENXIO); 110136270Swpaul} 110236270Swpaul 110348992Swpaulstatic int tl_attach(dev) 110448992Swpaul device_t dev; 110536270Swpaul{ 110650462Swpaul int s, i; 110736270Swpaul u_int32_t command; 110839583Swpaul u_int16_t did, vid; 110939583Swpaul struct tl_type *t; 111039583Swpaul struct ifnet *ifp; 111139583Swpaul struct tl_softc *sc; 111248992Swpaul int unit, error = 0, rid; 111336270Swpaul 111436270Swpaul s = splimp(); 111536270Swpaul 111648992Swpaul vid = pci_get_vendor(dev); 111748992Swpaul did = pci_get_device(dev); 111848992Swpaul sc = device_get_softc(dev); 111948992Swpaul unit = device_get_unit(dev); 112048992Swpaul bzero(sc, sizeof(struct tl_softc)); 112139583Swpaul 112239583Swpaul t = tl_devs; 112339583Swpaul while(t->tl_name != NULL) { 112439583Swpaul if (vid == t->tl_vid && did == t->tl_did) 112536270Swpaul break; 112639583Swpaul t++; 112739583Swpaul } 112836270Swpaul 112939583Swpaul if (t->tl_name == NULL) { 113039583Swpaul printf("tl%d: unknown device!?\n", unit); 113136270Swpaul goto fail; 113236270Swpaul } 113336270Swpaul 113436270Swpaul /* 113536270Swpaul * Map control/status registers. 113636270Swpaul */ 113748992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 113839583Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 113948992Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 114048992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 114136270Swpaul 114239583Swpaul#ifdef TL_USEIOSPACE 114339583Swpaul if (!(command & PCIM_CMD_PORTEN)) { 114439583Swpaul printf("tl%d: failed to enable I/O ports!\n", unit); 114548992Swpaul error = ENXIO; 114639583Swpaul goto fail; 114739583Swpaul } 114839583Swpaul 114948992Swpaul rid = TL_PCI_LOIO; 115048992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 115148992Swpaul 0, ~0, 1, RF_ACTIVE); 115248992Swpaul 115348992Swpaul /* 115448992Swpaul * Some cards have the I/O and memory mapped address registers 115548992Swpaul * reversed. Try both combinations before giving up. 115648992Swpaul */ 115748992Swpaul if (sc->tl_res == NULL) { 115848992Swpaul rid = TL_PCI_LOMEM; 115948992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 116048992Swpaul 0, ~0, 1, RF_ACTIVE); 116145155Swpaul } 116239583Swpaul#else 116336270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 116439583Swpaul printf("tl%d: failed to enable memory mapping!\n", unit); 116548992Swpaul error = ENXIO; 116636270Swpaul goto fail; 116736270Swpaul } 116836270Swpaul 116948992Swpaul rid = TL_PCI_LOMEM; 117048992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 117148992Swpaul 0, ~0, 1, RF_ACTIVE); 117248992Swpaul if (sc->tl_res == NULL) { 117348992Swpaul rid = TL_PCI_LOIO; 117448992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 117548992Swpaul 0, ~0, 1, RF_ACTIVE); 117636270Swpaul } 117739583Swpaul#endif 117836270Swpaul 117948992Swpaul if (sc->tl_res == NULL) { 118048992Swpaul printf("tl%d: couldn't map ports/memory\n", unit); 118148992Swpaul error = ENXIO; 118248992Swpaul goto fail; 118348992Swpaul } 118448992Swpaul 118548992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 118648992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 118748992Swpaul 118839583Swpaul#ifdef notdef 118939583Swpaul /* 119039583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 119139583Swpaul * timer all the way up to its maximum value. I'm not sure 119239583Swpaul * if this is really necessary, but what the manual wants, 119339583Swpaul * the manual gets. 119439583Swpaul */ 119548992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 119639583Swpaul command |= 0x0000FF00; 119748992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 119839583Swpaul#endif 119936270Swpaul 120036270Swpaul /* Allocate interrupt */ 120148992Swpaul rid = 0; 120248992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 120348992Swpaul RF_SHAREABLE | RF_ACTIVE); 120448992Swpaul 120548992Swpaul if (sc->tl_irq == NULL) { 120649010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 120739583Swpaul printf("tl%d: couldn't map interrupt\n", unit); 120848992Swpaul error = ENXIO; 120936270Swpaul goto fail; 121036270Swpaul } 121136270Swpaul 121248992Swpaul error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 121348992Swpaul tl_intr, sc, &sc->tl_intrhand); 121448992Swpaul 121548992Swpaul if (error) { 121649010Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_res); 121749010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 121848992Swpaul printf("tl%d: couldn't set up irq\n", unit); 121948992Swpaul goto fail; 122048992Swpaul } 122148992Swpaul 122236270Swpaul /* 122351439Swpaul * Now allocate memory for the TX and RX lists. 122436270Swpaul */ 122551439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 122651657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 122739583Swpaul 122851439Swpaul if (sc->tl_ldata == NULL) { 122949010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 123048992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 123149010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 123239583Swpaul printf("tl%d: no memory for list buffers!\n", unit); 123348992Swpaul error = ENXIO; 123436270Swpaul goto fail; 123536270Swpaul } 123636270Swpaul 123739583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 123839583Swpaul 123939583Swpaul sc->tl_unit = unit; 124039583Swpaul sc->tl_dinfo = t; 124143235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 124239583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 124339583Swpaul if (t->tl_vid == OLICOM_VENDORID) 124439583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 124539583Swpaul 124639583Swpaul /* Reset the adapter. */ 124739583Swpaul tl_softreset(sc, 1); 124850468Swpaul tl_hardreset(dev); 124939583Swpaul tl_softreset(sc, 1); 125039583Swpaul 125138030Swpaul /* 125239583Swpaul * Get station address from the EEPROM. 125339583Swpaul */ 125439583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 125539583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 125649010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 125748992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 125849010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 125951439Swpaul contigfree(sc->tl_ldata, 126051439Swpaul sizeof(struct tl_list_data), M_DEVBUF); 126139583Swpaul printf("tl%d: failed to read station address\n", unit); 126248992Swpaul error = ENXIO; 126339583Swpaul goto fail; 126439583Swpaul } 126539583Swpaul 126639583Swpaul /* 126739583Swpaul * XXX Olicom, in its desire to be different from the 126839583Swpaul * rest of the world, has done strange things with the 126939583Swpaul * encoding of the station address in the EEPROM. First 127039583Swpaul * of all, they store the address at offset 0xF8 rather 127139583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 127239583Swpaul * Second, they store the address in three 16-bit words in 127339583Swpaul * network byte order, as opposed to storing it sequentially 127439583Swpaul * like all the other ThunderLAN cards. In order to get 127539583Swpaul * the station address in a form that matches what the Olicom 127639583Swpaul * diagnostic utility specifies, we have to byte-swap each 127739583Swpaul * word. To make things even more confusing, neither 00:00:28 127839583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 127939583Swpaul */ 128039583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 128139583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 128239583Swpaul u_int16_t *p; 128339583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 128439583Swpaul *p = ntohs(*p); 128539583Swpaul } 128639583Swpaul } 128739583Swpaul 128839583Swpaul /* 128936270Swpaul * A ThunderLAN chip was detected. Inform the world. 129036270Swpaul */ 129139583Swpaul printf("tl%d: Ethernet address: %6D\n", unit, 129239583Swpaul sc->arpcom.ac_enaddr, ":"); 129336270Swpaul 129439583Swpaul ifp = &sc->arpcom.ac_if; 129539583Swpaul ifp->if_softc = sc; 129639583Swpaul ifp->if_unit = sc->tl_unit; 129739583Swpaul ifp->if_name = "tl"; 129839583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 129939583Swpaul ifp->if_ioctl = tl_ioctl; 130039583Swpaul ifp->if_output = ether_output; 130139583Swpaul ifp->if_start = tl_start; 130239583Swpaul ifp->if_watchdog = tl_watchdog; 130339583Swpaul ifp->if_init = tl_init; 130439583Swpaul ifp->if_mtu = ETHERMTU; 130551439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 130639583Swpaul callout_handle_init(&sc->tl_stat_ch); 130739583Swpaul 130839583Swpaul /* Reset the adapter again. */ 130939583Swpaul tl_softreset(sc, 1); 131050468Swpaul tl_hardreset(dev); 131139583Swpaul tl_softreset(sc, 1); 131239583Swpaul 131336270Swpaul /* 131450462Swpaul * Do MII setup. If no PHYs are found, then this is a 131550462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 131650462Swpaul * and AUI/BNC. 131736270Swpaul */ 131850462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 131950462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 132045155Swpaul struct ifmedia *ifm; 132145155Swpaul sc->tl_bitrate = 1; 132245155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 132345155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 132445155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 132545155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 132645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 132745166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 132845155Swpaul /* Reset again, this time setting bitrate mode. */ 132945155Swpaul tl_softreset(sc, 1); 133045155Swpaul ifm = &sc->ifmedia; 133145155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 133245155Swpaul tl_ifmedia_upd(ifp); 133336270Swpaul } 133436270Swpaul 133539583Swpaul /* 133639583Swpaul * Call MI attach routines. 133739583Swpaul */ 133839583Swpaul if_attach(ifp); 133939583Swpaul ether_ifattach(ifp); 134038030Swpaul 134139583Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 134239583Swpaul 134336270Swpaulfail: 134436270Swpaul splx(s); 134548992Swpaul return(error); 134636270Swpaul} 134736270Swpaul 134848992Swpaulstatic int tl_detach(dev) 134948992Swpaul device_t dev; 135048992Swpaul{ 135148992Swpaul struct tl_softc *sc; 135248992Swpaul struct ifnet *ifp; 135348992Swpaul int s; 135448992Swpaul 135548992Swpaul s = splimp(); 135648992Swpaul 135748992Swpaul sc = device_get_softc(dev); 135848992Swpaul ifp = &sc->arpcom.ac_if; 135948992Swpaul 136048992Swpaul tl_stop(sc); 136148992Swpaul if_detach(ifp); 136248992Swpaul 136350462Swpaul bus_generic_detach(dev); 136450462Swpaul device_delete_child(dev, sc->tl_miibus); 136550462Swpaul 136651439Swpaul contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 136750462Swpaul if (sc->tl_bitrate) 136850462Swpaul ifmedia_removeall(&sc->ifmedia); 136948992Swpaul 137048992Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 137148992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 137249010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 137348992Swpaul 137448992Swpaul splx(s); 137548992Swpaul 137648992Swpaul return(0); 137748992Swpaul} 137848992Swpaul 137936270Swpaul/* 138036270Swpaul * Initialize the transmit lists. 138136270Swpaul */ 138236270Swpaulstatic int tl_list_tx_init(sc) 138336270Swpaul struct tl_softc *sc; 138436270Swpaul{ 138536270Swpaul struct tl_chain_data *cd; 138636270Swpaul struct tl_list_data *ld; 138736270Swpaul int i; 138836270Swpaul 138936270Swpaul cd = &sc->tl_cdata; 139036270Swpaul ld = sc->tl_ldata; 139136270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 139236270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 139336270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 139436270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 139536270Swpaul else 139636270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 139736270Swpaul } 139836270Swpaul 139936270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 140036270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 140136270Swpaul sc->tl_txeoc = 1; 140236270Swpaul 140336270Swpaul return(0); 140436270Swpaul} 140536270Swpaul 140636270Swpaul/* 140736270Swpaul * Initialize the RX lists and allocate mbufs for them. 140836270Swpaul */ 140936270Swpaulstatic int tl_list_rx_init(sc) 141036270Swpaul struct tl_softc *sc; 141136270Swpaul{ 141236270Swpaul struct tl_chain_data *cd; 141336270Swpaul struct tl_list_data *ld; 141436270Swpaul int i; 141536270Swpaul 141636270Swpaul cd = &sc->tl_cdata; 141736270Swpaul ld = sc->tl_ldata; 141836270Swpaul 141940795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 142036270Swpaul cd->tl_rx_chain[i].tl_ptr = 142137626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 142239583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 142339583Swpaul return(ENOBUFS); 142440795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 142536270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 142636270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 142736270Swpaul } else { 142836270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 142936270Swpaul ld->tl_rx_list[i].tlist_fptr = 143036270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 143136270Swpaul } 143236270Swpaul } 143336270Swpaul 143436270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 143536270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 143636270Swpaul 143736270Swpaul return(0); 143836270Swpaul} 143936270Swpaul 144036270Swpaulstatic int tl_newbuf(sc, c) 144136270Swpaul struct tl_softc *sc; 144237626Swpaul struct tl_chain_onefrag *c; 144336270Swpaul{ 144436270Swpaul struct mbuf *m_new = NULL; 144536270Swpaul 144636270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 144736270Swpaul if (m_new == NULL) { 144856060Swpaul printf("tl%d: no memory for rx list -- packet dropped!\n", 144936270Swpaul sc->tl_unit); 145036270Swpaul return(ENOBUFS); 145136270Swpaul } 145236270Swpaul 145336270Swpaul MCLGET(m_new, M_DONTWAIT); 145436270Swpaul if (!(m_new->m_flags & M_EXT)) { 145556060Swpaul printf("tl%d: no memory for rx list -- packet dropped!\n", 145639583Swpaul sc->tl_unit); 145736270Swpaul m_freem(m_new); 145836270Swpaul return(ENOBUFS); 145936270Swpaul } 146036270Swpaul 146145155Swpaul#ifdef __alpha__ 146245155Swpaul m_new->m_data += 2; 146345155Swpaul#endif 146445155Swpaul 146536270Swpaul c->tl_mbuf = m_new; 146636270Swpaul c->tl_next = NULL; 146736270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 146836270Swpaul c->tl_ptr->tlist_fptr = 0; 146937626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 147037626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 147156060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 147236270Swpaul 147336270Swpaul return(0); 147436270Swpaul} 147536270Swpaul/* 147636270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 147736270Swpaul * tells us that a full ethernet frame has been captured and we need 147836270Swpaul * to handle it. 147936270Swpaul * 148036270Swpaul * Reception is done using 'lists' which consist of a header and a 148136270Swpaul * series of 10 data count/data address pairs that point to buffers. 148236270Swpaul * Initially you're supposed to create a list, populate it with pointers 148336270Swpaul * to buffers, then load the physical address of the list into the 148436270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 148536270Swpaul * frame into the buffers for you. 148636270Swpaul * 148736270Swpaul * To make things as fast as possible, we have the chip DMA directly 148836270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 148936270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 149036270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 149136270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 149236270Swpaul * the chip, it will always think it has an endless receive channel. 149336270Swpaul * 149436270Swpaul * If we happen to fall behind and the chip manages to fill up all of 149536270Swpaul * the buffers, it will generate an end of channel interrupt and wait 149636270Swpaul * for us to empty the chain and restart the receiver. 149736270Swpaul */ 149836270Swpaulstatic int tl_intvec_rxeof(xsc, type) 149936270Swpaul void *xsc; 150036270Swpaul u_int32_t type; 150136270Swpaul{ 150236270Swpaul struct tl_softc *sc; 150336270Swpaul int r = 0, total_len = 0; 150436270Swpaul struct ether_header *eh; 150536270Swpaul struct mbuf *m; 150636270Swpaul struct ifnet *ifp; 150737626Swpaul struct tl_chain_onefrag *cur_rx; 150836270Swpaul 150936270Swpaul sc = xsc; 151036270Swpaul ifp = &sc->arpcom.ac_if; 151136270Swpaul 151256060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 151356060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 151456060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 151556060Swpaul break; 151636270Swpaul r++; 151736270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 151836270Swpaul m = cur_rx->tl_mbuf; 151936270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 152036270Swpaul 152139583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 152239583Swpaul ifp->if_ierrors++; 152339583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 152439583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 152539583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 152639583Swpaul continue; 152739583Swpaul } 152836270Swpaul 152936270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 153036270Swpaul vtophys(cur_rx->tl_ptr); 153136270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 153236270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 153336270Swpaul 153436270Swpaul eh = mtod(m, struct ether_header *); 153536270Swpaul m->m_pkthdr.rcvif = ifp; 153636270Swpaul 153737626Swpaul /* 153837626Swpaul * Note: when the ThunderLAN chip is in 'capture all 153937626Swpaul * frames' mode, it will receive its own transmissions. 154037626Swpaul * We drop don't need to process our own transmissions, 154137626Swpaul * so we drop them here and continue. 154237626Swpaul */ 154339583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 154439583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 154537626Swpaul ETHER_ADDR_LEN)) { 154637626Swpaul m_freem(m); 154737626Swpaul continue; 154837626Swpaul } 154937626Swpaul 155036270Swpaul /* 155136270Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 155236270Swpaul * don't pass it up to the ether_input() layer unless it's 155336270Swpaul * a broadcast packet, multicast packet, matches our ethernet 155436270Swpaul * address or the interface is in promiscuous mode. If we don't 155536270Swpaul * want the packet, just forget it. We leave the mbuf in place 155636270Swpaul * since it can be used again later. 155736270Swpaul */ 155836270Swpaul if (ifp->if_bpf) { 155936270Swpaul m->m_pkthdr.len = m->m_len = total_len; 156036270Swpaul bpf_mtap(ifp, m); 156136270Swpaul if (ifp->if_flags & IFF_PROMISC && 156236270Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 156336270Swpaul ETHER_ADDR_LEN) && 156436270Swpaul (eh->ether_dhost[0] & 1) == 0)) { 156536270Swpaul m_freem(m); 156636270Swpaul continue; 156736270Swpaul } 156836270Swpaul } 156951583Swpaul 157036270Swpaul /* Remove header from mbuf and pass it on. */ 157136270Swpaul m->m_pkthdr.len = m->m_len = 157236270Swpaul total_len - sizeof(struct ether_header); 157336270Swpaul m->m_data += sizeof(struct ether_header); 157436270Swpaul ether_input(ifp, eh, m); 157536270Swpaul } 157636270Swpaul 157736270Swpaul return(r); 157836270Swpaul} 157936270Swpaul 158036270Swpaul/* 158136270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 158236270Swpaul * initialized or the adapter reached a list with a forward pointer 158336270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 158436270Swpaul * the card has hit the end of the receive buffer chain and we need to 158536270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 158636270Swpaul */ 158736270Swpaulstatic int tl_intvec_rxeoc(xsc, type) 158836270Swpaul void *xsc; 158936270Swpaul u_int32_t type; 159036270Swpaul{ 159136270Swpaul struct tl_softc *sc; 159236270Swpaul int r; 159356060Swpaul struct tl_chain_data *cd; 159436270Swpaul 159556060Swpaul 159636270Swpaul sc = xsc; 159756060Swpaul cd = &sc->tl_cdata; 159836270Swpaul 159936270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 160036270Swpaul r = tl_intvec_rxeof(xsc, type); 160139583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 160236270Swpaul r = 1; 160356060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 160456060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 160539583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 160636270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 160736270Swpaul return(r); 160836270Swpaul} 160936270Swpaul 161036270Swpaulstatic int tl_intvec_txeof(xsc, type) 161136270Swpaul void *xsc; 161236270Swpaul u_int32_t type; 161336270Swpaul{ 161436270Swpaul struct tl_softc *sc; 161536270Swpaul int r = 0; 161636270Swpaul struct tl_chain *cur_tx; 161736270Swpaul 161836270Swpaul sc = xsc; 161936270Swpaul 162036270Swpaul /* 162136270Swpaul * Go through our tx list and free mbufs for those 162236270Swpaul * frames that have been sent. 162336270Swpaul */ 162436270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 162536270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 162636270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 162736270Swpaul break; 162836270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 162936270Swpaul 163036270Swpaul r++; 163136270Swpaul m_freem(cur_tx->tl_mbuf); 163236270Swpaul cur_tx->tl_mbuf = NULL; 163336270Swpaul 163436270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 163536270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 163637626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 163737626Swpaul break; 163836270Swpaul } 163936270Swpaul 164036270Swpaul return(r); 164136270Swpaul} 164236270Swpaul 164336270Swpaul/* 164436270Swpaul * The transmit end of channel interrupt. The adapter triggers this 164536270Swpaul * interrupt to tell us it hit the end of the current transmit list. 164636270Swpaul * 164736270Swpaul * A note about this: it's possible for a condition to arise where 164836270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 164936270Swpaul * You have to avoid this since the chip expects things to go in a 165036270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 165136270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 165236270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 165336270Swpaul * interrupt should be received and acknowledged before any more frames 165436270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 165536270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 165636270Swpaul * it could attempt to issue a transmit command prematurely. 165736270Swpaul * 165836270Swpaul * To guard against this, tl_start() will only issue transmit commands 165936270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 166036270Swpaul * can set this flag once tl_start() has cleared it. 166136270Swpaul */ 166236270Swpaulstatic int tl_intvec_txeoc(xsc, type) 166336270Swpaul void *xsc; 166436270Swpaul u_int32_t type; 166536270Swpaul{ 166636270Swpaul struct tl_softc *sc; 166736270Swpaul struct ifnet *ifp; 166836270Swpaul u_int32_t cmd; 166936270Swpaul 167036270Swpaul sc = xsc; 167136270Swpaul ifp = &sc->arpcom.ac_if; 167236270Swpaul 167336270Swpaul /* Clear the timeout timer. */ 167436270Swpaul ifp->if_timer = 0; 167536270Swpaul 167636270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 167736270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 167836270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 167936270Swpaul sc->tl_txeoc = 1; 168036270Swpaul } else { 168136270Swpaul sc->tl_txeoc = 0; 168236270Swpaul /* First we have to ack the EOC interrupt. */ 168339583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 168436270Swpaul /* Then load the address of the next TX list. */ 168539583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 168651439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 168736270Swpaul /* Restart TX channel. */ 168839583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 168936270Swpaul cmd &= ~TL_CMD_RT; 169036270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 169139583Swpaul CMD_PUT(sc, cmd); 169236270Swpaul return(0); 169336270Swpaul } 169436270Swpaul 169536270Swpaul return(1); 169636270Swpaul} 169736270Swpaul 169836270Swpaulstatic int tl_intvec_adchk(xsc, type) 169936270Swpaul void *xsc; 170036270Swpaul u_int32_t type; 170136270Swpaul{ 170236270Swpaul struct tl_softc *sc; 170336270Swpaul 170436270Swpaul sc = xsc; 170536270Swpaul 170639627Swpaul if (type) 170739627Swpaul printf("tl%d: adapter check: %x\n", sc->tl_unit, 170841656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 170936270Swpaul 171039583Swpaul tl_softreset(sc, 1); 171137626Swpaul tl_stop(sc); 171236270Swpaul tl_init(sc); 171339583Swpaul CMD_SET(sc, TL_CMD_INTSON); 171436270Swpaul 171536270Swpaul return(0); 171636270Swpaul} 171736270Swpaul 171836270Swpaulstatic int tl_intvec_netsts(xsc, type) 171936270Swpaul void *xsc; 172036270Swpaul u_int32_t type; 172136270Swpaul{ 172236270Swpaul struct tl_softc *sc; 172336270Swpaul u_int16_t netsts; 172436270Swpaul 172536270Swpaul sc = xsc; 172636270Swpaul 172739583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 172839583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 172936270Swpaul 173036270Swpaul printf("tl%d: network status: %x\n", sc->tl_unit, netsts); 173136270Swpaul 173236270Swpaul return(1); 173336270Swpaul} 173436270Swpaul 173539583Swpaulstatic void tl_intr(xsc) 173639583Swpaul void *xsc; 173736270Swpaul{ 173836270Swpaul struct tl_softc *sc; 173936270Swpaul struct ifnet *ifp; 174036270Swpaul int r = 0; 174136270Swpaul u_int32_t type = 0; 174236270Swpaul u_int16_t ints = 0; 174336270Swpaul u_int8_t ivec = 0; 174436270Swpaul 174539583Swpaul sc = xsc; 174636270Swpaul 174736270Swpaul /* Disable interrupts */ 174839583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 174939583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 175036270Swpaul type = (ints << 16) & 0xFFFF0000; 175136270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 175236270Swpaul ints = (ints & TL_INT_MASK) >> 2; 175336270Swpaul 175436270Swpaul ifp = &sc->arpcom.ac_if; 175536270Swpaul 175636270Swpaul switch(ints) { 175736270Swpaul case (TL_INTR_INVALID): 175839583Swpaul#ifdef DIAGNOSTIC 175939583Swpaul printf("tl%d: got an invalid interrupt!\n", sc->tl_unit); 176039583Swpaul#endif 176139583Swpaul /* Re-enable interrupts but don't ack this one. */ 176239583Swpaul CMD_PUT(sc, type); 176339583Swpaul r = 0; 176436270Swpaul break; 176536270Swpaul case (TL_INTR_TXEOF): 176636270Swpaul r = tl_intvec_txeof((void *)sc, type); 176736270Swpaul break; 176836270Swpaul case (TL_INTR_TXEOC): 176936270Swpaul r = tl_intvec_txeoc((void *)sc, type); 177036270Swpaul break; 177136270Swpaul case (TL_INTR_STATOFLOW): 177239583Swpaul tl_stats_update(sc); 177339583Swpaul r = 1; 177436270Swpaul break; 177536270Swpaul case (TL_INTR_RXEOF): 177636270Swpaul r = tl_intvec_rxeof((void *)sc, type); 177736270Swpaul break; 177836270Swpaul case (TL_INTR_DUMMY): 177939583Swpaul printf("tl%d: got a dummy interrupt\n", sc->tl_unit); 178039583Swpaul r = 1; 178136270Swpaul break; 178236270Swpaul case (TL_INTR_ADCHK): 178336270Swpaul if (ivec) 178436270Swpaul r = tl_intvec_adchk((void *)sc, type); 178536270Swpaul else 178636270Swpaul r = tl_intvec_netsts((void *)sc, type); 178736270Swpaul break; 178836270Swpaul case (TL_INTR_RXEOC): 178936270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 179036270Swpaul break; 179136270Swpaul default: 179236270Swpaul printf("tl%d: bogus interrupt type\n", ifp->if_unit); 179336270Swpaul break; 179436270Swpaul } 179536270Swpaul 179636270Swpaul /* Re-enable interrupts */ 179737626Swpaul if (r) { 179839583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 179937626Swpaul } 180036270Swpaul 180137626Swpaul if (ifp->if_snd.ifq_head != NULL) 180237626Swpaul tl_start(ifp); 180337626Swpaul 180436270Swpaul return; 180536270Swpaul} 180636270Swpaul 180736270Swpaulstatic void tl_stats_update(xsc) 180836270Swpaul void *xsc; 180936270Swpaul{ 181036270Swpaul struct tl_softc *sc; 181136270Swpaul struct ifnet *ifp; 181236270Swpaul struct tl_stats tl_stats; 181350462Swpaul struct mii_data *mii; 181436270Swpaul u_int32_t *p; 181548992Swpaul int s; 181636270Swpaul 181748992Swpaul s = splimp(); 181848992Swpaul 181936270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 182036270Swpaul 182136270Swpaul sc = xsc; 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++; 184751439Swpaul printf("tl%d: tx underrun -- increasing " 184851439Swpaul "tx threshold to %d bytes\n", sc->tl_unit, 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 186248992Swpaul splx(s); 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 */ 187136270Swpaulstatic int tl_encap(sc, c, m_head) 187236270Swpaul struct tl_softc *sc; 187336270Swpaul struct tl_chain *c; 187436270Swpaul struct mbuf *m_head; 187536270Swpaul{ 187636270Swpaul int frag = 0; 187736270Swpaul struct tl_frag *f = NULL; 187836270Swpaul int total_len; 187936270Swpaul struct mbuf *m; 188036270Swpaul 188136270Swpaul /* 188236270Swpaul * Start packing the mbufs in this chain into 188336270Swpaul * the fragment pointers. Stop when we run out 188436270Swpaul * of fragments or hit the end of the mbuf chain. 188536270Swpaul */ 188636270Swpaul m = m_head; 188736270Swpaul total_len = 0; 188836270Swpaul 188936270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 189036270Swpaul if (m->m_len != 0) { 189136270Swpaul if (frag == TL_MAXFRAGS) 189236270Swpaul break; 189336270Swpaul total_len+= m->m_len; 189436270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 189536270Swpaul vtophys(mtod(m, vm_offset_t)); 189636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 189736270Swpaul frag++; 189836270Swpaul } 189936270Swpaul } 190036270Swpaul 190136270Swpaul /* 190236270Swpaul * Handle special cases. 190336270Swpaul * Special case #1: we used up all 10 fragments, but 190436270Swpaul * we have more mbufs left in the chain. Copy the 190536270Swpaul * data into an mbuf cluster. Note that we don't 190636270Swpaul * bother clearing the values in the other fragment 190736270Swpaul * pointers/counters; it wouldn't gain us anything, 190836270Swpaul * and would waste cycles. 190936270Swpaul */ 191036270Swpaul if (m != NULL) { 191136270Swpaul struct mbuf *m_new = NULL; 191236270Swpaul 191336270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 191436270Swpaul if (m_new == NULL) { 191556060Swpaul printf("tl%d: no memory for tx list\n", sc->tl_unit); 191636270Swpaul return(1); 191736270Swpaul } 191836270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 191936270Swpaul MCLGET(m_new, M_DONTWAIT); 192036270Swpaul if (!(m_new->m_flags & M_EXT)) { 192136270Swpaul m_freem(m_new); 192256060Swpaul printf("tl%d: no memory for tx list\n", 192336270Swpaul sc->tl_unit); 192436270Swpaul return(1); 192536270Swpaul } 192636270Swpaul } 192736270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 192836270Swpaul mtod(m_new, caddr_t)); 192936270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 193036270Swpaul m_freem(m_head); 193136270Swpaul m_head = m_new; 193236270Swpaul f = &c->tl_ptr->tl_frag[0]; 193336270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 193436270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 193536270Swpaul frag = 1; 193636270Swpaul } 193736270Swpaul 193836270Swpaul /* 193936270Swpaul * Special case #2: the frame is smaller than the minimum 194036270Swpaul * frame size. We have to pad it to make the chip happy. 194136270Swpaul */ 194236270Swpaul if (total_len < TL_MIN_FRAMELEN) { 194336270Swpaul if (frag == TL_MAXFRAGS) 194439583Swpaul printf("tl%d: all frags filled but " 194539583Swpaul "frame still to small!\n", sc->tl_unit); 194636270Swpaul f = &c->tl_ptr->tl_frag[frag]; 194736270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 194836270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 194936270Swpaul total_len += f->tlist_dcnt; 195036270Swpaul frag++; 195136270Swpaul } 195236270Swpaul 195336270Swpaul c->tl_mbuf = m_head; 195436270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 195536270Swpaul c->tl_ptr->tlist_frsize = total_len; 195636270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 195736270Swpaul c->tl_ptr->tlist_fptr = 0; 195836270Swpaul 195936270Swpaul return(0); 196036270Swpaul} 196136270Swpaul 196236270Swpaul/* 196336270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 196436270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 196536270Swpaul * copy of the pointers since the transmit list fragment pointers are 196636270Swpaul * physical addresses. 196736270Swpaul */ 196836270Swpaulstatic void tl_start(ifp) 196936270Swpaul struct ifnet *ifp; 197036270Swpaul{ 197136270Swpaul struct tl_softc *sc; 197236270Swpaul struct mbuf *m_head = NULL; 197336270Swpaul u_int32_t cmd; 197436270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 197536270Swpaul 197636270Swpaul sc = ifp->if_softc; 197736270Swpaul 197836270Swpaul /* 197936270Swpaul * Check for an available queue slot. If there are none, 198036270Swpaul * punt. 198136270Swpaul */ 198236270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 198336270Swpaul ifp->if_flags |= IFF_OACTIVE; 198436270Swpaul return; 198536270Swpaul } 198636270Swpaul 198736270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 198836270Swpaul 198936270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 199036270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 199136270Swpaul if (m_head == NULL) 199236270Swpaul break; 199336270Swpaul 199436270Swpaul /* Pick a chain member off the free list. */ 199536270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 199636270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 199736270Swpaul 199836270Swpaul cur_tx->tl_next = NULL; 199936270Swpaul 200036270Swpaul /* Pack the data into the list. */ 200136270Swpaul tl_encap(sc, cur_tx, m_head); 200236270Swpaul 200336270Swpaul /* Chain it together */ 200436270Swpaul if (prev != NULL) { 200536270Swpaul prev->tl_next = cur_tx; 200636270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 200736270Swpaul } 200836270Swpaul prev = cur_tx; 200936270Swpaul 201036270Swpaul /* 201136270Swpaul * If there's a BPF listener, bounce a copy of this frame 201236270Swpaul * to him. 201336270Swpaul */ 201436270Swpaul if (ifp->if_bpf) 201536270Swpaul bpf_mtap(ifp, cur_tx->tl_mbuf); 201636270Swpaul } 201736270Swpaul 201836270Swpaul /* 201941526Swpaul * If there are no packets queued, bail. 202041526Swpaul */ 202141526Swpaul if (cur_tx == NULL) 202241526Swpaul return; 202341526Swpaul 202441526Swpaul /* 202536270Swpaul * That's all we can stands, we can't stands no more. 202636270Swpaul * If there are no other transfers pending, then issue the 202736270Swpaul * TX GO command to the adapter to start things moving. 202836270Swpaul * Otherwise, just leave the data in the queue and let 202936270Swpaul * the EOF/EOC interrupt handler send. 203036270Swpaul */ 203136270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 203236270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 203336270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 203439583Swpaul 203536270Swpaul if (sc->tl_txeoc) { 203636270Swpaul sc->tl_txeoc = 0; 203739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 203839583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 203936270Swpaul cmd &= ~TL_CMD_RT; 204036270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 204139583Swpaul CMD_PUT(sc, cmd); 204236270Swpaul } 204336270Swpaul } else { 204436270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 204542146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 204636270Swpaul } 204736270Swpaul 204836270Swpaul /* 204936270Swpaul * Set a timeout in case the chip goes out to lunch. 205036270Swpaul */ 205136270Swpaul ifp->if_timer = 5; 205236270Swpaul 205336270Swpaul return; 205436270Swpaul} 205536270Swpaul 205636270Swpaulstatic void tl_init(xsc) 205736270Swpaul void *xsc; 205836270Swpaul{ 205936270Swpaul struct tl_softc *sc = xsc; 206036270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 206136270Swpaul int s; 206250462Swpaul struct mii_data *mii; 206336270Swpaul 206436270Swpaul s = splimp(); 206536270Swpaul 206636270Swpaul ifp = &sc->arpcom.ac_if; 206736270Swpaul 206836270Swpaul /* 206936270Swpaul * Cancel pending I/O. 207036270Swpaul */ 207136270Swpaul tl_stop(sc); 207236270Swpaul 207351439Swpaul /* Initialize TX FIFO threshold */ 207451439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 207551439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 207651439Swpaul 207751439Swpaul /* Set PCI burst size */ 207851439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 207951439Swpaul 208036270Swpaul /* 208136270Swpaul * Set 'capture all frames' bit for promiscuous mode. 208236270Swpaul */ 208339583Swpaul if (ifp->if_flags & IFF_PROMISC) 208439583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 208539583Swpaul else 208639583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 208736270Swpaul 208836270Swpaul /* 208936270Swpaul * Set capture broadcast bit to capture broadcast frames. 209036270Swpaul */ 209139583Swpaul if (ifp->if_flags & IFF_BROADCAST) 209239583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 209339583Swpaul else 209439583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 209536270Swpaul 209650468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 209750468Swpaul 209836270Swpaul /* Init our MAC address */ 209941656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 210036270Swpaul 210139583Swpaul /* Init multicast filter, if needed. */ 210239583Swpaul tl_setmulti(sc); 210339583Swpaul 210436270Swpaul /* Init circular RX list. */ 210539583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 210639583Swpaul printf("tl%d: initialization failed: no " 210739583Swpaul "memory for rx buffers\n", sc->tl_unit); 210839583Swpaul tl_stop(sc); 210936270Swpaul return; 211036270Swpaul } 211136270Swpaul 211236270Swpaul /* Init TX pointers. */ 211336270Swpaul tl_list_tx_init(sc); 211436270Swpaul 211539583Swpaul /* Enable PCI interrupts. */ 211639583Swpaul CMD_SET(sc, TL_CMD_INTSON); 211736270Swpaul 211836270Swpaul /* Load the address of the rx list */ 211939583Swpaul CMD_SET(sc, TL_CMD_RT); 212039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 212136270Swpaul 212250462Swpaul if (!sc->tl_bitrate) { 212350462Swpaul if (sc->tl_miibus != NULL) { 212450462Swpaul mii = device_get_softc(sc->tl_miibus); 212550462Swpaul mii_mediachg(mii); 212650462Swpaul } 212750462Swpaul } 212838030Swpaul 212936270Swpaul /* Send the RX go command */ 213050468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 213136270Swpaul 213236270Swpaul ifp->if_flags |= IFF_RUNNING; 213336270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 213436270Swpaul 213536270Swpaul (void)splx(s); 213636270Swpaul 213736270Swpaul /* Start the stats update counter */ 213836270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 213936270Swpaul 214036270Swpaul return; 214136270Swpaul} 214236270Swpaul 214336270Swpaul/* 214436270Swpaul * Set media options. 214536270Swpaul */ 214636270Swpaulstatic int tl_ifmedia_upd(ifp) 214736270Swpaul struct ifnet *ifp; 214836270Swpaul{ 214936270Swpaul struct tl_softc *sc; 215050462Swpaul struct mii_data *mii = NULL; 215136270Swpaul 215236270Swpaul sc = ifp->if_softc; 215336270Swpaul 215450462Swpaul if (sc->tl_bitrate) 215550462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 215650462Swpaul else { 215750462Swpaul mii = device_get_softc(sc->tl_miibus); 215850462Swpaul mii_mediachg(mii); 215950462Swpaul } 216036270Swpaul 216136270Swpaul return(0); 216236270Swpaul} 216336270Swpaul 216436270Swpaul/* 216536270Swpaul * Report current media status. 216636270Swpaul */ 216736270Swpaulstatic void tl_ifmedia_sts(ifp, ifmr) 216836270Swpaul struct ifnet *ifp; 216936270Swpaul struct ifmediareq *ifmr; 217036270Swpaul{ 217136270Swpaul struct tl_softc *sc; 217250462Swpaul struct mii_data *mii; 217336270Swpaul 217436270Swpaul sc = ifp->if_softc; 217536270Swpaul 217636270Swpaul ifmr->ifm_active = IFM_ETHER; 217736270Swpaul 217845155Swpaul if (sc->tl_bitrate) { 217945155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 218045155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 218145155Swpaul else 218245155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 218345155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 218445155Swpaul ifmr->ifm_active |= IFM_HDX; 218545155Swpaul else 218645155Swpaul ifmr->ifm_active |= IFM_FDX; 218745155Swpaul return; 218836270Swpaul } else { 218950462Swpaul mii = device_get_softc(sc->tl_miibus); 219050462Swpaul mii_pollstat(mii); 219150462Swpaul ifmr->ifm_active = mii->mii_media_active; 219250462Swpaul ifmr->ifm_status = mii->mii_media_status; 219336270Swpaul } 219436270Swpaul 219536270Swpaul return; 219636270Swpaul} 219736270Swpaul 219836270Swpaulstatic int tl_ioctl(ifp, command, data) 219936270Swpaul struct ifnet *ifp; 220036735Sdfr u_long command; 220136270Swpaul caddr_t data; 220236270Swpaul{ 220336270Swpaul struct tl_softc *sc = ifp->if_softc; 220436270Swpaul struct ifreq *ifr = (struct ifreq *) data; 220536270Swpaul int s, error = 0; 220636270Swpaul 220736270Swpaul s = splimp(); 220836270Swpaul 220936270Swpaul switch(command) { 221036270Swpaul case SIOCSIFADDR: 221136270Swpaul case SIOCGIFADDR: 221236270Swpaul case SIOCSIFMTU: 221336270Swpaul error = ether_ioctl(ifp, command, data); 221436270Swpaul break; 221536270Swpaul case SIOCSIFFLAGS: 221636270Swpaul if (ifp->if_flags & IFF_UP) { 221750462Swpaul if (ifp->if_flags & IFF_RUNNING && 221850462Swpaul ifp->if_flags & IFF_PROMISC && 221950462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 222050462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 222150462Swpaul tl_setmulti(sc); 222250462Swpaul } else if (ifp->if_flags & IFF_RUNNING && 222350462Swpaul !(ifp->if_flags & IFF_PROMISC) && 222450462Swpaul sc->tl_if_flags & IFF_PROMISC) { 222550462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 222650462Swpaul tl_setmulti(sc); 222750462Swpaul } else 222850462Swpaul tl_init(sc); 222936270Swpaul } else { 223036270Swpaul if (ifp->if_flags & IFF_RUNNING) { 223136270Swpaul tl_stop(sc); 223236270Swpaul } 223336270Swpaul } 223450462Swpaul sc->tl_if_flags = ifp->if_flags; 223536270Swpaul error = 0; 223636270Swpaul break; 223736270Swpaul case SIOCADDMULTI: 223836270Swpaul case SIOCDELMULTI: 223936270Swpaul tl_setmulti(sc); 224036270Swpaul error = 0; 224136270Swpaul break; 224236270Swpaul case SIOCSIFMEDIA: 224336270Swpaul case SIOCGIFMEDIA: 224450462Swpaul if (sc->tl_bitrate) 224550462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 224650462Swpaul else { 224750462Swpaul struct mii_data *mii; 224850462Swpaul mii = device_get_softc(sc->tl_miibus); 224950462Swpaul error = ifmedia_ioctl(ifp, ifr, 225050462Swpaul &mii->mii_media, command); 225150462Swpaul } 225236270Swpaul break; 225336270Swpaul default: 225436270Swpaul error = EINVAL; 225536270Swpaul break; 225636270Swpaul } 225736270Swpaul 225836270Swpaul (void)splx(s); 225936270Swpaul 226036270Swpaul return(error); 226136270Swpaul} 226236270Swpaul 226336270Swpaulstatic void tl_watchdog(ifp) 226436270Swpaul struct ifnet *ifp; 226536270Swpaul{ 226636270Swpaul struct tl_softc *sc; 226736270Swpaul 226836270Swpaul sc = ifp->if_softc; 226936270Swpaul 227050462Swpaul printf("tl%d: device timeout\n", sc->tl_unit); 227136270Swpaul 227236270Swpaul ifp->if_oerrors++; 227336270Swpaul 227450468Swpaul tl_softreset(sc, 1); 227536270Swpaul tl_init(sc); 227636270Swpaul 227736270Swpaul return; 227836270Swpaul} 227936270Swpaul 228036270Swpaul/* 228136270Swpaul * Stop the adapter and free any mbufs allocated to the 228236270Swpaul * RX and TX lists. 228336270Swpaul */ 228436270Swpaulstatic void tl_stop(sc) 228536270Swpaul struct tl_softc *sc; 228636270Swpaul{ 228736270Swpaul register int i; 228836270Swpaul struct ifnet *ifp; 228936270Swpaul 229036270Swpaul ifp = &sc->arpcom.ac_if; 229136270Swpaul 229236270Swpaul /* Stop the stats updater. */ 229336270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 229436270Swpaul 229536270Swpaul /* Stop the transmitter */ 229639583Swpaul CMD_CLR(sc, TL_CMD_RT); 229739583Swpaul CMD_SET(sc, TL_CMD_STOP); 229839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 229936270Swpaul 230036270Swpaul /* Stop the receiver */ 230139583Swpaul CMD_SET(sc, TL_CMD_RT); 230239583Swpaul CMD_SET(sc, TL_CMD_STOP); 230339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 230436270Swpaul 230536270Swpaul /* 230636270Swpaul * Disable host interrupts. 230736270Swpaul */ 230839583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 230936270Swpaul 231036270Swpaul /* 231136270Swpaul * Clear list pointer. 231236270Swpaul */ 231339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231436270Swpaul 231536270Swpaul /* 231636270Swpaul * Free the RX lists. 231736270Swpaul */ 231836270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 231936270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 232036270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 232136270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 232236270Swpaul } 232336270Swpaul } 232436270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 232536270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 232636270Swpaul 232736270Swpaul /* 232836270Swpaul * Free the TX list buffers. 232936270Swpaul */ 233036270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 233136270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 233236270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 233336270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 233436270Swpaul } 233536270Swpaul } 233636270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 233736270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 233836270Swpaul 233936270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 234036270Swpaul 234136270Swpaul return; 234236270Swpaul} 234336270Swpaul 234436270Swpaul/* 234536270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 234636270Swpaul * get confused by errant DMAs when rebooting. 234736270Swpaul */ 234848992Swpaulstatic void tl_shutdown(dev) 234948992Swpaul device_t dev; 235036270Swpaul{ 235139583Swpaul struct tl_softc *sc; 235236270Swpaul 235348992Swpaul sc = device_get_softc(dev); 235436270Swpaul 235539583Swpaul tl_stop(sc); 235636270Swpaul 235736270Swpaul return; 235836270Swpaul} 2359