if_tl.c revision 50468
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 * 3250462Swpaul * $Id: if_tl.c,v 1.37 1999/07/23 02:06:56 wpaul Exp $ 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 18148645Sdes#include "bpf.h" 18236270Swpaul 18336270Swpaul#include <sys/param.h> 18436270Swpaul#include <sys/systm.h> 18536270Swpaul#include <sys/sockio.h> 18636270Swpaul#include <sys/mbuf.h> 18736270Swpaul#include <sys/malloc.h> 18836270Swpaul#include <sys/kernel.h> 18936270Swpaul#include <sys/socket.h> 19036270Swpaul 19136270Swpaul#include <net/if.h> 19236270Swpaul#include <net/if_arp.h> 19336270Swpaul#include <net/ethernet.h> 19436270Swpaul#include <net/if_dl.h> 19536270Swpaul#include <net/if_media.h> 19636270Swpaul 19748645Sdes#if NBPF > 0 19836270Swpaul#include <net/bpf.h> 19936270Swpaul#endif 20036270Swpaul 20136270Swpaul#include <vm/vm.h> /* for vtophys */ 20236270Swpaul#include <vm/pmap.h> /* for vtophys */ 20336270Swpaul#include <machine/clock.h> /* for DELAY */ 20445155Swpaul#include <machine/bus_memio.h> 20545155Swpaul#include <machine/bus_pio.h> 20645155Swpaul#include <machine/bus.h> 20748992Swpaul#include <machine/resource.h> 20848992Swpaul#include <sys/bus.h> 20948992Swpaul#include <sys/rman.h> 21036270Swpaul 21150462Swpaul#include <dev/mii/mii.h> 21250462Swpaul#include <dev/mii/miivar.h> 21350462Swpaul 21436270Swpaul#include <pci/pcireg.h> 21536270Swpaul#include <pci/pcivar.h> 21636270Swpaul 21739957Swpaul/* 21839957Swpaul * Default to using PIO register access mode to pacify certain 21939957Swpaul * laptop docking stations with built-in ThunderLAN chips that 22039957Swpaul * don't seem to handle memory mapped mode properly. 22139957Swpaul */ 22239957Swpaul#define TL_USEIOSPACE 22339957Swpaul 22436270Swpaul#include <pci/if_tlreg.h> 22536270Swpaul 22650462Swpaul#include "miibus_if.h" 22750462Swpaul 22841591Sarchie#if !defined(lint) 22941591Sarchiestatic const char rcsid[] = 23050462Swpaul "$Id: if_tl.c,v 1.37 1999/07/23 02:06:56 wpaul Exp $"; 23136270Swpaul#endif 23236270Swpaul 23336270Swpaul/* 23436270Swpaul * Various supported device vendors/types and their names. 23536270Swpaul */ 23636270Swpaul 23736270Swpaulstatic struct tl_type tl_devs[] = { 23836270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23936270Swpaul "Texas Instruments ThunderLAN" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 24136270Swpaul "Compaq Netelligent 10" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 24336270Swpaul "Compaq Netelligent 10/100" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24536270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24736270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24936270Swpaul "Compaq NetFlex-3/P Integrated" }, 25036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 25136270Swpaul "Compaq NetFlex-3/P" }, 25236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 25336270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25437626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25537626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25637626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25737626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25837626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25937626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 26037626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 26137626Swpaul "Olicom OC-2183/2185" }, 26237626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 26337626Swpaul "Olicom OC-2325" }, 26437626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26537626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26636270Swpaul { 0, 0, NULL } 26736270Swpaul}; 26836270Swpaul 26948992Swpaulstatic int tl_probe __P((device_t)); 27048992Swpaulstatic int tl_attach __P((device_t)); 27148992Swpaulstatic int tl_detach __P((device_t)); 27236270Swpaulstatic int tl_intvec_rxeoc __P((void *, u_int32_t)); 27336270Swpaulstatic int tl_intvec_txeoc __P((void *, u_int32_t)); 27436270Swpaulstatic int tl_intvec_txeof __P((void *, u_int32_t)); 27536270Swpaulstatic int tl_intvec_rxeof __P((void *, u_int32_t)); 27636270Swpaulstatic int tl_intvec_adchk __P((void *, u_int32_t)); 27736270Swpaulstatic int tl_intvec_netsts __P((void *, u_int32_t)); 27836270Swpaul 27937626Swpaulstatic int tl_newbuf __P((struct tl_softc *, 28037626Swpaul struct tl_chain_onefrag *)); 28136270Swpaulstatic void tl_stats_update __P((void *)); 28236270Swpaulstatic int tl_encap __P((struct tl_softc *, struct tl_chain *, 28336270Swpaul struct mbuf *)); 28436270Swpaul 28536270Swpaulstatic void tl_intr __P((void *)); 28636270Swpaulstatic void tl_start __P((struct ifnet *)); 28736735Sdfrstatic int tl_ioctl __P((struct ifnet *, u_long, caddr_t)); 28836270Swpaulstatic void tl_init __P((void *)); 28936270Swpaulstatic void tl_stop __P((struct tl_softc *)); 29036270Swpaulstatic void tl_watchdog __P((struct ifnet *)); 29148992Swpaulstatic void tl_shutdown __P((device_t)); 29236270Swpaulstatic int tl_ifmedia_upd __P((struct ifnet *)); 29336270Swpaulstatic void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 29436270Swpaul 29541656Swpaulstatic u_int8_t tl_eeprom_putbyte __P((struct tl_softc *, int)); 29639583Swpaulstatic u_int8_t tl_eeprom_getbyte __P((struct tl_softc *, 29741656Swpaul int, u_int8_t *)); 29839583Swpaulstatic int tl_read_eeprom __P((struct tl_softc *, caddr_t, int, int)); 29936270Swpaul 30039583Swpaulstatic void tl_mii_sync __P((struct tl_softc *)); 30139583Swpaulstatic void tl_mii_send __P((struct tl_softc *, u_int32_t, int)); 30239583Swpaulstatic int tl_mii_readreg __P((struct tl_softc *, struct tl_mii_frame *)); 30339583Swpaulstatic int tl_mii_writereg __P((struct tl_softc *, struct tl_mii_frame *)); 30450462Swpaulstatic int tl_miibus_readreg __P((device_t, int, int)); 30550462Swpaulstatic int tl_miibus_writereg __P((device_t, int, int, int)); 30650462Swpaulstatic void tl_miibus_statchg __P((device_t)); 30736270Swpaul 30836270Swpaulstatic void tl_setmode __P((struct tl_softc *, int)); 30941656Swpaulstatic int tl_calchash __P((caddr_t)); 31036270Swpaulstatic void tl_setmulti __P((struct tl_softc *)); 31141656Swpaulstatic void tl_setfilt __P((struct tl_softc *, caddr_t, int)); 31239583Swpaulstatic void tl_softreset __P((struct tl_softc *, int)); 31350468Swpaulstatic void tl_hardreset __P((device_t)); 31436270Swpaulstatic int tl_list_rx_init __P((struct tl_softc *)); 31536270Swpaulstatic int tl_list_tx_init __P((struct tl_softc *)); 31636270Swpaul 31741656Swpaulstatic u_int8_t tl_dio_read8 __P((struct tl_softc *, int)); 31841656Swpaulstatic u_int16_t tl_dio_read16 __P((struct tl_softc *, int)); 31941656Swpaulstatic u_int32_t tl_dio_read32 __P((struct tl_softc *, int)); 32041656Swpaulstatic void tl_dio_write8 __P((struct tl_softc *, int, int)); 32141656Swpaulstatic void tl_dio_write16 __P((struct tl_softc *, int, int)); 32241656Swpaulstatic void tl_dio_write32 __P((struct tl_softc *, int, int)); 32341656Swpaulstatic void tl_dio_setbit __P((struct tl_softc *, int, int)); 32441656Swpaulstatic void tl_dio_clrbit __P((struct tl_softc *, int, int)); 32541656Swpaulstatic void tl_dio_setbit16 __P((struct tl_softc *, int, int)); 32641656Swpaulstatic void tl_dio_clrbit16 __P((struct tl_softc *, int, int)); 32739583Swpaul 32849010Swpaul#ifdef TL_USEIOSPACE 32949010Swpaul#define TL_RES SYS_RES_IOPORT 33049010Swpaul#define TL_RID TL_PCI_LOIO 33149010Swpaul#else 33249010Swpaul#define TL_RES SYS_RES_MEMORY 33349010Swpaul#define TL_RID TL_PCI_LOMEM 33449010Swpaul#endif 33549010Swpaul 33648992Swpaulstatic device_method_t tl_methods[] = { 33748992Swpaul /* Device interface */ 33848992Swpaul DEVMETHOD(device_probe, tl_probe), 33948992Swpaul DEVMETHOD(device_attach, tl_attach), 34048992Swpaul DEVMETHOD(device_detach, tl_detach), 34148992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 34250462Swpaul 34350462Swpaul /* bus interface */ 34450462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 34550462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 34650462Swpaul 34750462Swpaul /* MII interface */ 34850462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34950462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 35050462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 35150462Swpaul 35248992Swpaul { 0, 0 } 35348992Swpaul}; 35448992Swpaul 35548992Swpaulstatic driver_t tl_driver = { 35648992Swpaul "tl", 35748992Swpaul tl_methods, 35848992Swpaul sizeof(struct tl_softc) 35948992Swpaul}; 36048992Swpaul 36148992Swpaulstatic devclass_t tl_devclass; 36248992Swpaul 36348992SwpaulDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 36450462SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 36548992Swpaul 36639583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 36741656Swpaul struct tl_softc *sc; 36841656Swpaul int reg; 36939583Swpaul{ 37039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37139583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 37239583Swpaul} 37339583Swpaul 37439583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 37541656Swpaul struct tl_softc *sc; 37641656Swpaul int reg; 37739583Swpaul{ 37839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37939583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 38039583Swpaul} 38139583Swpaul 38239583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 38341656Swpaul struct tl_softc *sc; 38441656Swpaul int reg; 38539583Swpaul{ 38639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38739583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38839583Swpaul} 38939583Swpaul 39039583Swpaulstatic void tl_dio_write8(sc, reg, val) 39141656Swpaul struct tl_softc *sc; 39241656Swpaul int reg; 39341656Swpaul int val; 39439583Swpaul{ 39539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39639583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 39739583Swpaul return; 39839583Swpaul} 39939583Swpaul 40039583Swpaulstatic void tl_dio_write16(sc, reg, val) 40141656Swpaul struct tl_softc *sc; 40241656Swpaul int reg; 40341656Swpaul int val; 40439583Swpaul{ 40539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40639583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 40739583Swpaul return; 40839583Swpaul} 40939583Swpaul 41039583Swpaulstatic void tl_dio_write32(sc, reg, val) 41141656Swpaul struct tl_softc *sc; 41241656Swpaul int reg; 41341656Swpaul int val; 41439583Swpaul{ 41539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 41639583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 41739583Swpaul return; 41839583Swpaul} 41939583Swpaul 42039583Swpaulstatic void tl_dio_setbit(sc, reg, bit) 42141656Swpaul struct tl_softc *sc; 42241656Swpaul int reg; 42341656Swpaul int bit; 42439583Swpaul{ 42539583Swpaul u_int8_t f; 42639583Swpaul 42739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42839583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42939583Swpaul f |= bit; 43039583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 43139583Swpaul 43239583Swpaul return; 43339583Swpaul} 43439583Swpaul 43539583Swpaulstatic void tl_dio_clrbit(sc, reg, bit) 43641656Swpaul struct tl_softc *sc; 43741656Swpaul int reg; 43841656Swpaul int bit; 43939583Swpaul{ 44039583Swpaul u_int8_t f; 44139583Swpaul 44239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 44339583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 44439583Swpaul f &= ~bit; 44539583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44639583Swpaul 44739583Swpaul return; 44839583Swpaul} 44939583Swpaul 45039583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 45141656Swpaul struct tl_softc *sc; 45241656Swpaul int reg; 45341656Swpaul int bit; 45439583Swpaul{ 45539583Swpaul u_int16_t f; 45639583Swpaul 45739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45839583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45939583Swpaul f |= bit; 46039583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 46139583Swpaul 46239583Swpaul return; 46339583Swpaul} 46439583Swpaul 46539583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46641656Swpaul struct tl_softc *sc; 46741656Swpaul int reg; 46841656Swpaul int bit; 46939583Swpaul{ 47039583Swpaul u_int16_t f; 47139583Swpaul 47239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 47339583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 47439583Swpaul f &= ~bit; 47539583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47639583Swpaul 47739583Swpaul return; 47839583Swpaul} 47939583Swpaul 48036270Swpaul/* 48136270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 48236270Swpaul */ 48339583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 48439583Swpaul struct tl_softc *sc; 48541656Swpaul int byte; 48636270Swpaul{ 48736270Swpaul register int i, ack = 0; 48836270Swpaul 48936270Swpaul /* 49036270Swpaul * Make sure we're in TX mode. 49136270Swpaul */ 49239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 49336270Swpaul 49436270Swpaul /* 49536270Swpaul * Feed in each bit and stobe the clock. 49636270Swpaul */ 49736270Swpaul for (i = 0x80; i; i >>= 1) { 49836270Swpaul if (byte & i) { 49939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 50036270Swpaul } else { 50139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 50236270Swpaul } 50339583Swpaul DELAY(1); 50439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50539583Swpaul DELAY(1); 50639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50736270Swpaul } 50836270Swpaul 50936270Swpaul /* 51036270Swpaul * Turn off TX mode. 51136270Swpaul */ 51239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 51336270Swpaul 51436270Swpaul /* 51536270Swpaul * Check for ack. 51636270Swpaul */ 51739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51839583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 52036270Swpaul 52136270Swpaul return(ack); 52236270Swpaul} 52336270Swpaul 52436270Swpaul/* 52536270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52636270Swpaul */ 52739583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52839583Swpaul struct tl_softc *sc; 52941656Swpaul int addr; 53036270Swpaul u_int8_t *dest; 53136270Swpaul{ 53236270Swpaul register int i; 53336270Swpaul u_int8_t byte = 0; 53436270Swpaul 53539583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53639583Swpaul 53736270Swpaul EEPROM_START; 53839583Swpaul 53936270Swpaul /* 54036270Swpaul * Send write control code to EEPROM. 54136270Swpaul */ 54239583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 54339583Swpaul printf("tl%d: failed to send write command, status: %x\n", 54439583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 54536270Swpaul return(1); 54639583Swpaul } 54736270Swpaul 54836270Swpaul /* 54936270Swpaul * Send address of byte we want to read. 55036270Swpaul */ 55139583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 55239583Swpaul printf("tl%d: failed to send address, status: %x\n", 55339583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 55436270Swpaul return(1); 55539583Swpaul } 55636270Swpaul 55736270Swpaul EEPROM_STOP; 55836270Swpaul EEPROM_START; 55936270Swpaul /* 56036270Swpaul * Send read control code to EEPROM. 56136270Swpaul */ 56239583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 56339583Swpaul printf("tl%d: failed to send write command, status: %x\n", 56439583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 56536270Swpaul return(1); 56639583Swpaul } 56736270Swpaul 56836270Swpaul /* 56936270Swpaul * Start reading bits from EEPROM. 57036270Swpaul */ 57139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 57236270Swpaul for (i = 0x80; i; i >>= 1) { 57339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57439583Swpaul DELAY(1); 57539583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57636270Swpaul byte |= i; 57739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57836501Swpaul DELAY(1); 57936270Swpaul } 58036270Swpaul 58136270Swpaul EEPROM_STOP; 58236270Swpaul 58336270Swpaul /* 58436270Swpaul * No ACK generated for read, so just return byte. 58536270Swpaul */ 58636270Swpaul 58736270Swpaul *dest = byte; 58836270Swpaul 58936270Swpaul return(0); 59036270Swpaul} 59136270Swpaul 59239583Swpaul/* 59339583Swpaul * Read a sequence of bytes from the EEPROM. 59439583Swpaul */ 59539583Swpaulstatic int tl_read_eeprom(sc, dest, off, cnt) 59639583Swpaul struct tl_softc *sc; 59739583Swpaul caddr_t dest; 59839583Swpaul int off; 59939583Swpaul int cnt; 60036270Swpaul{ 60139583Swpaul int err = 0, i; 60239583Swpaul u_int8_t byte = 0; 60339583Swpaul 60439583Swpaul for (i = 0; i < cnt; i++) { 60539583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60639583Swpaul if (err) 60739583Swpaul break; 60839583Swpaul *(dest + i) = byte; 60939583Swpaul } 61039583Swpaul 61139583Swpaul return(err ? 1 : 0); 61239583Swpaul} 61339583Swpaul 61439583Swpaulstatic void tl_mii_sync(sc) 61539583Swpaul struct tl_softc *sc; 61639583Swpaul{ 61736270Swpaul register int i; 61836270Swpaul 61939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 62036270Swpaul 62136270Swpaul for (i = 0; i < 32; i++) { 62239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62436270Swpaul } 62536270Swpaul 62636270Swpaul return; 62736270Swpaul} 62836270Swpaul 62939583Swpaulstatic void tl_mii_send(sc, bits, cnt) 63039583Swpaul struct tl_softc *sc; 63136270Swpaul u_int32_t bits; 63236270Swpaul int cnt; 63336270Swpaul{ 63436270Swpaul int i; 63536270Swpaul 63636270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63836270Swpaul if (bits & i) { 63939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 64036270Swpaul } else { 64139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64236270Swpaul } 64339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64436270Swpaul } 64536270Swpaul} 64636270Swpaul 64739583Swpaulstatic int tl_mii_readreg(sc, frame) 64839583Swpaul struct tl_softc *sc; 64936270Swpaul struct tl_mii_frame *frame; 65036270Swpaul 65136270Swpaul{ 65236270Swpaul int i, ack, s; 65336270Swpaul int minten = 0; 65436270Swpaul 65536270Swpaul s = splimp(); 65636270Swpaul 65739583Swpaul tl_mii_sync(sc); 65836270Swpaul 65936270Swpaul /* 66036270Swpaul * Set up frame for RX. 66136270Swpaul */ 66236270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66336270Swpaul frame->mii_opcode = TL_MII_READOP; 66436270Swpaul frame->mii_turnaround = 0; 66536270Swpaul frame->mii_data = 0; 66636270Swpaul 66736270Swpaul /* 66836270Swpaul * Turn off MII interrupt by forcing MINTEN low. 66936270Swpaul */ 67039583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 67136270Swpaul if (minten) { 67239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67336270Swpaul } 67436270Swpaul 67536270Swpaul /* 67636270Swpaul * Turn on data xmit. 67736270Swpaul */ 67839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 67936270Swpaul 68036270Swpaul /* 68136270Swpaul * Send command/address info. 68236270Swpaul */ 68339583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68439583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68539583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68639583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 68736270Swpaul 68836270Swpaul /* 68936270Swpaul * Turn off xmit. 69036270Swpaul */ 69139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69236270Swpaul 69336270Swpaul /* Idle bit */ 69439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69636270Swpaul 69736270Swpaul /* Check for ack */ 69839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69939583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 70036270Swpaul 70136270Swpaul /* Complete the cycle */ 70239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70336270Swpaul 70436270Swpaul /* 70536270Swpaul * Now try reading data bits. If the ack failed, we still 70636270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 70736270Swpaul */ 70836270Swpaul if (ack) { 70936270Swpaul for(i = 0; i < 16; i++) { 71039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71236270Swpaul } 71336270Swpaul goto fail; 71436270Swpaul } 71536270Swpaul 71636270Swpaul for (i = 0x8000; i; i >>= 1) { 71739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71836270Swpaul if (!ack) { 71939583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 72036270Swpaul frame->mii_data |= i; 72136270Swpaul } 72239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72336270Swpaul } 72436270Swpaul 72536270Swpaulfail: 72636270Swpaul 72739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72936270Swpaul 73036270Swpaul /* Reenable interrupts */ 73136270Swpaul if (minten) { 73239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73336270Swpaul } 73436270Swpaul 73536270Swpaul splx(s); 73636270Swpaul 73736270Swpaul if (ack) 73836270Swpaul return(1); 73936270Swpaul return(0); 74036270Swpaul} 74136270Swpaul 74239583Swpaulstatic int tl_mii_writereg(sc, frame) 74339583Swpaul struct tl_softc *sc; 74436270Swpaul struct tl_mii_frame *frame; 74536270Swpaul 74636270Swpaul{ 74736270Swpaul int s; 74836270Swpaul int minten; 74936270Swpaul 75039583Swpaul tl_mii_sync(sc); 75136270Swpaul 75236270Swpaul s = splimp(); 75336270Swpaul /* 75436270Swpaul * Set up frame for TX. 75536270Swpaul */ 75636270Swpaul 75736270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 75836270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 75936270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 76036270Swpaul 76136270Swpaul /* 76236270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76336270Swpaul */ 76439583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76536270Swpaul if (minten) { 76639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76736270Swpaul } 76836270Swpaul 76936270Swpaul /* 77036270Swpaul * Turn on data output. 77136270Swpaul */ 77239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77336270Swpaul 77439583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77539583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77639583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77739583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 77839583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 77939583Swpaul tl_mii_send(sc, frame->mii_data, 16); 78036270Swpaul 78139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78336270Swpaul 78436270Swpaul /* 78536270Swpaul * Turn off xmit. 78636270Swpaul */ 78739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 78836270Swpaul 78936270Swpaul /* Reenable interrupts */ 79036270Swpaul if (minten) 79139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79236270Swpaul 79336270Swpaul splx(s); 79436270Swpaul 79536270Swpaul return(0); 79636270Swpaul} 79736270Swpaul 79850462Swpaulstatic int tl_miibus_readreg(dev, phy, reg) 79950462Swpaul device_t dev; 80050462Swpaul int phy, reg; 80150462Swpaul{ 80236270Swpaul struct tl_softc *sc; 80336270Swpaul struct tl_mii_frame frame; 80436270Swpaul 80550462Swpaul sc = device_get_softc(dev); 80636270Swpaul bzero((char *)&frame, sizeof(frame)); 80736270Swpaul 80850462Swpaul frame.mii_phyaddr = phy; 80936270Swpaul frame.mii_regaddr = reg; 81039583Swpaul tl_mii_readreg(sc, &frame); 81136270Swpaul 81236270Swpaul return(frame.mii_data); 81336270Swpaul} 81436270Swpaul 81550462Swpaulstatic int tl_miibus_writereg(dev, phy, reg, data) 81650462Swpaul device_t dev; 81750462Swpaul int phy, reg, data; 81850462Swpaul{ 81936270Swpaul struct tl_softc *sc; 82036270Swpaul struct tl_mii_frame frame; 82136270Swpaul 82250462Swpaul sc = device_get_softc(dev); 82336270Swpaul bzero((char *)&frame, sizeof(frame)); 82436270Swpaul 82550462Swpaul frame.mii_phyaddr = phy; 82636270Swpaul frame.mii_regaddr = reg; 82736270Swpaul frame.mii_data = data; 82836270Swpaul 82939583Swpaul tl_mii_writereg(sc, &frame); 83036270Swpaul 83150462Swpaul return(0); 83236270Swpaul} 83336270Swpaul 83450462Swpaulstatic void tl_miibus_statchg(dev) 83550462Swpaul device_t dev; 83650462Swpaul{ 83736270Swpaul struct tl_softc *sc; 83850462Swpaul struct mii_data *mii; 83936270Swpaul 84050462Swpaul sc = device_get_softc(dev); 84150462Swpaul mii = device_get_softc(sc->tl_miibus); 84236270Swpaul 84350462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 84450462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84536270Swpaul } else { 84650462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84736270Swpaul } 84836270Swpaul 84936270Swpaul return; 85036270Swpaul} 85136270Swpaul 85236270Swpaul/* 85350462Swpaul * Set modes for bitrate devices. 85436270Swpaul */ 85536270Swpaulstatic void tl_setmode(sc, media) 85636270Swpaul struct tl_softc *sc; 85736270Swpaul int media; 85836270Swpaul{ 85950462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 86050462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86136270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 86250462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86336270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 86450462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86539583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86636270Swpaul } else { 86750462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86839583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86936270Swpaul } 87036270Swpaul } 87136270Swpaul 87236270Swpaul return; 87336270Swpaul} 87436270Swpaul 87536464Swpaul/* 87636464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 87736464Swpaul * table. This hash is simply the address split into 6-bit chunks 87836464Swpaul * XOR'd, e.g. 87936464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 88036464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 88136464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 88236464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 88336464Swpaul */ 88436270Swpaulstatic int tl_calchash(addr) 88541656Swpaul caddr_t addr; 88636270Swpaul{ 88737626Swpaul int t; 88836270Swpaul 88936464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 89036464Swpaul (addr[2] ^ addr[5]); 89136464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 89236270Swpaul} 89336270Swpaul 89439583Swpaul/* 89539583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 89639583Swpaul * the multicast hash filter. The perfect filter can be programmed 89739583Swpaul * with up to four MAC addresses. The first one is always used to 89839583Swpaul * hold the station address, which leaves us free to use the other 89939583Swpaul * three for multicast addresses. 90039583Swpaul */ 90139583Swpaulstatic void tl_setfilt(sc, addr, slot) 90239583Swpaul struct tl_softc *sc; 90341656Swpaul caddr_t addr; 90439583Swpaul int slot; 90539583Swpaul{ 90639583Swpaul int i; 90739583Swpaul u_int16_t regaddr; 90839583Swpaul 90939583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 91039583Swpaul 91139583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 91239583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 91339583Swpaul 91439583Swpaul return; 91539583Swpaul} 91639583Swpaul 91739583Swpaul/* 91839583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 91939583Swpaul * linked list. This is fine, except addresses are added from the head 92039583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 92139583Swpaul * group to always be in the perfect filter, but as more groups are added, 92239583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 92339583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 92439583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 92539583Swpaul * and into the hash table. 92639583Swpaul * 92739583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 92839583Swpaul * circular queue, we don't have the ability to just grab the tail of 92939583Swpaul * the list and traverse it backwards. Instead, we have to traverse 93039583Swpaul * the list once to find the tail, then traverse it again backwards to 93139583Swpaul * update the multicast filter. 93239583Swpaul */ 93336270Swpaulstatic void tl_setmulti(sc) 93436270Swpaul struct tl_softc *sc; 93536270Swpaul{ 93636270Swpaul struct ifnet *ifp; 93736270Swpaul u_int32_t hashes[2] = { 0, 0 }; 93839583Swpaul int h, i; 93936270Swpaul struct ifmultiaddr *ifma; 94039583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 94136270Swpaul ifp = &sc->arpcom.ac_if; 94236270Swpaul 94339583Swpaul /* First, zot all the existing filters. */ 94439583Swpaul for (i = 1; i < 4; i++) 94541656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 94639583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 94739583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 94839583Swpaul 94939583Swpaul /* Now program new ones. */ 95039583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 95136270Swpaul hashes[0] = 0xFFFFFFFF; 95236270Swpaul hashes[1] = 0xFFFFFFFF; 95336270Swpaul } else { 95439583Swpaul i = 1; 95539583Swpaul /* First find the tail of the list. */ 95636270Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 95736270Swpaul ifma = ifma->ifma_link.le_next) { 95839583Swpaul if (ifma->ifma_link.le_next == NULL) 95939583Swpaul break; 96039583Swpaul } 96139583Swpaul /* Now traverse the list backwards. */ 96239583Swpaul for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs; 96339583Swpaul ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) { 96436270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 96536270Swpaul continue; 96639583Swpaul /* 96739583Swpaul * Program the first three multicast groups 96839583Swpaul * into the perfect filter. For all others, 96939583Swpaul * use the hash table. 97039583Swpaul */ 97139583Swpaul if (i < 4) { 97239583Swpaul tl_setfilt(sc, 97339583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97439583Swpaul i++; 97539583Swpaul continue; 97639583Swpaul } 97739583Swpaul 97836270Swpaul h = tl_calchash( 97936270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 98036270Swpaul if (h < 32) 98136270Swpaul hashes[0] |= (1 << h); 98236270Swpaul else 98336317Swpaul hashes[1] |= (1 << (h - 32)); 98436270Swpaul } 98536270Swpaul } 98636270Swpaul 98739583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 98839583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 98936270Swpaul 99036270Swpaul return; 99136270Swpaul} 99236270Swpaul 99339583Swpaul/* 99439583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 99539583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99639583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 99739583Swpaul * experience this isn't necessary. 99839583Swpaul */ 99950468Swpaulstatic void tl_hardreset(dev) 100050468Swpaul device_t dev; 100150468Swpaul{ 100239583Swpaul struct tl_softc *sc; 100339583Swpaul int i; 100450468Swpaul u_int16_t flags; 100539583Swpaul 100650468Swpaul sc = device_get_softc(dev); 100739583Swpaul 100850468Swpaul tl_mii_sync(sc); 100939583Swpaul 101050468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 101139583Swpaul 101250468Swpaul for (i = 0; i < MII_NPHY; i++) 101350468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 101439583Swpaul 101550468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 101639583Swpaul DELAY(50000); 101750468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 101839583Swpaul tl_mii_sync(sc); 101950468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 102039583Swpaul 102150468Swpaul DELAY(50000); 102239583Swpaul return; 102339583Swpaul} 102439583Swpaul 102539583Swpaulstatic void tl_softreset(sc, internal) 102639583Swpaul struct tl_softc *sc; 102736270Swpaul int internal; 102836270Swpaul{ 102939583Swpaul u_int32_t cmd, dummy, i; 103036270Swpaul 103136270Swpaul /* Assert the adapter reset bit. */ 103239583Swpaul CMD_SET(sc, TL_CMD_ADRST); 103350468Swpaul 103436270Swpaul /* Turn off interrupts */ 103539583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 103636270Swpaul 103736270Swpaul /* First, clear the stats registers. */ 103839583Swpaul for (i = 0; i < 5; i++) 103939583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 104036270Swpaul 104136270Swpaul /* Clear Areg and Hash registers */ 104239583Swpaul for (i = 0; i < 8; i++) 104339583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104436270Swpaul 104536270Swpaul /* 104636270Swpaul * Set up Netconfig register. Enable one channel and 104736270Swpaul * one fragment mode. 104836270Swpaul */ 104939583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 105045155Swpaul if (internal && !sc->tl_bitrate) { 105139583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105236270Swpaul } else { 105339583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105436270Swpaul } 105536270Swpaul 105645155Swpaul /* Handle cards with bitrate devices. */ 105745155Swpaul if (sc->tl_bitrate) 105845155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 105945155Swpaul 106036270Swpaul /* Set PCI burst size */ 106139583Swpaul tl_dio_write8(sc, TL_BSIZEREG, 0x33); 106236270Swpaul 106336270Swpaul /* 106436270Swpaul * Load adapter irq pacing timer and tx threshold. 106536270Swpaul * We make the transmit threshold 1 initially but we may 106636270Swpaul * change that later. 106736270Swpaul */ 106839583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 106936270Swpaul cmd |= TL_CMD_NES; 107036270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 107139583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 107239583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 107336270Swpaul 107436270Swpaul /* Unreset the MII */ 107539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 107636270Swpaul 107736270Swpaul /* Take the adapter out of reset */ 107839583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 107936270Swpaul 108036270Swpaul /* Wait for things to settle down a little. */ 108136270Swpaul DELAY(500); 108236270Swpaul 108336270Swpaul return; 108436270Swpaul} 108536270Swpaul 108636270Swpaul/* 108736270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 108839583Swpaul * against our list and return its name if we find a match. 108936270Swpaul */ 109048992Swpaulstatic int tl_probe(dev) 109148992Swpaul device_t dev; 109236270Swpaul{ 109336270Swpaul struct tl_type *t; 109436270Swpaul 109536270Swpaul t = tl_devs; 109636270Swpaul 109736270Swpaul while(t->tl_name != NULL) { 109848992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 109948992Swpaul (pci_get_device(dev) == t->tl_did)) { 110048992Swpaul device_set_desc(dev, t->tl_name); 110148992Swpaul return(0); 110248992Swpaul } 110336270Swpaul t++; 110436270Swpaul } 110536270Swpaul 110648992Swpaul return(ENXIO); 110736270Swpaul} 110836270Swpaul 110948992Swpaulstatic int tl_attach(dev) 111048992Swpaul device_t dev; 111136270Swpaul{ 111250462Swpaul int s, i; 111336270Swpaul u_int32_t command; 111439583Swpaul u_int16_t did, vid; 111539583Swpaul struct tl_type *t; 111639583Swpaul struct ifnet *ifp; 111739583Swpaul struct tl_softc *sc; 111839583Swpaul unsigned int round; 111939583Swpaul caddr_t roundptr; 112048992Swpaul int unit, error = 0, rid; 112136270Swpaul 112236270Swpaul s = splimp(); 112336270Swpaul 112448992Swpaul vid = pci_get_vendor(dev); 112548992Swpaul did = pci_get_device(dev); 112648992Swpaul sc = device_get_softc(dev); 112748992Swpaul unit = device_get_unit(dev); 112848992Swpaul bzero(sc, sizeof(struct tl_softc)); 112939583Swpaul 113039583Swpaul t = tl_devs; 113139583Swpaul while(t->tl_name != NULL) { 113239583Swpaul if (vid == t->tl_vid && did == t->tl_did) 113336270Swpaul break; 113439583Swpaul t++; 113539583Swpaul } 113636270Swpaul 113739583Swpaul if (t->tl_name == NULL) { 113839583Swpaul printf("tl%d: unknown device!?\n", unit); 113936270Swpaul goto fail; 114036270Swpaul } 114136270Swpaul 114236270Swpaul /* 114336270Swpaul * Map control/status registers. 114436270Swpaul */ 114548992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 114639583Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 114748992Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 114848992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 114936270Swpaul 115039583Swpaul#ifdef TL_USEIOSPACE 115139583Swpaul if (!(command & PCIM_CMD_PORTEN)) { 115239583Swpaul printf("tl%d: failed to enable I/O ports!\n", unit); 115348992Swpaul error = ENXIO; 115439583Swpaul goto fail; 115539583Swpaul } 115639583Swpaul 115748992Swpaul rid = TL_PCI_LOIO; 115848992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 115948992Swpaul 0, ~0, 1, RF_ACTIVE); 116048992Swpaul 116148992Swpaul /* 116248992Swpaul * Some cards have the I/O and memory mapped address registers 116348992Swpaul * reversed. Try both combinations before giving up. 116448992Swpaul */ 116548992Swpaul if (sc->tl_res == NULL) { 116648992Swpaul rid = TL_PCI_LOMEM; 116748992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 116848992Swpaul 0, ~0, 1, RF_ACTIVE); 116945155Swpaul } 117039583Swpaul#else 117136270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 117239583Swpaul printf("tl%d: failed to enable memory mapping!\n", unit); 117348992Swpaul error = ENXIO; 117436270Swpaul goto fail; 117536270Swpaul } 117636270Swpaul 117748992Swpaul rid = TL_PCI_LOMEM; 117848992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 117948992Swpaul 0, ~0, 1, RF_ACTIVE); 118048992Swpaul if (sc->tl_res == NULL) { 118148992Swpaul rid = TL_PCI_LOIO; 118248992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 118348992Swpaul 0, ~0, 1, RF_ACTIVE); 118436270Swpaul } 118539583Swpaul#endif 118636270Swpaul 118748992Swpaul if (sc->tl_res == NULL) { 118848992Swpaul printf("tl%d: couldn't map ports/memory\n", unit); 118948992Swpaul error = ENXIO; 119048992Swpaul goto fail; 119148992Swpaul } 119248992Swpaul 119348992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 119448992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 119548992Swpaul 119639583Swpaul#ifdef notdef 119739583Swpaul /* 119839583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 119939583Swpaul * timer all the way up to its maximum value. I'm not sure 120039583Swpaul * if this is really necessary, but what the manual wants, 120139583Swpaul * the manual gets. 120239583Swpaul */ 120348992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 120439583Swpaul command |= 0x0000FF00; 120548992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 120639583Swpaul#endif 120736270Swpaul 120836270Swpaul /* Allocate interrupt */ 120948992Swpaul rid = 0; 121048992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 121148992Swpaul RF_SHAREABLE | RF_ACTIVE); 121248992Swpaul 121348992Swpaul if (sc->tl_irq == NULL) { 121449010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 121539583Swpaul printf("tl%d: couldn't map interrupt\n", unit); 121648992Swpaul error = ENXIO; 121736270Swpaul goto fail; 121836270Swpaul } 121936270Swpaul 122048992Swpaul error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 122148992Swpaul tl_intr, sc, &sc->tl_intrhand); 122248992Swpaul 122348992Swpaul if (error) { 122449010Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_res); 122549010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 122648992Swpaul printf("tl%d: couldn't set up irq\n", unit); 122748992Swpaul goto fail; 122848992Swpaul } 122948992Swpaul 123036270Swpaul /* 123139583Swpaul * Now allocate memory for the TX and RX lists. Note that 123239583Swpaul * we actually allocate 8 bytes more than we really need: 123339583Swpaul * this is because we need to adjust the final address to 123439583Swpaul * be aligned on a quadword (64-bit) boundary in order to 123539583Swpaul * make the chip happy. If the list structures aren't properly 123639583Swpaul * aligned, DMA fails and the chip generates an adapter check 123739583Swpaul * interrupt and has to be reset. If you set up the softc struct 123839583Swpaul * just right you can sort of obtain proper alignment 'by chance.' 123939583Swpaul * But I don't want to depend on this, so instead the alignment 124039583Swpaul * is forced here. 124136270Swpaul */ 124239583Swpaul sc->tl_ldata_ptr = malloc(sizeof(struct tl_list_data) + 8, 124339583Swpaul M_DEVBUF, M_NOWAIT); 124439583Swpaul 124539583Swpaul if (sc->tl_ldata_ptr == NULL) { 124649010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 124748992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 124849010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 124939583Swpaul printf("tl%d: no memory for list buffers!\n", unit); 125048992Swpaul error = ENXIO; 125136270Swpaul goto fail; 125236270Swpaul } 125336270Swpaul 125436270Swpaul /* 125539583Swpaul * Convoluted but satisfies my ANSI sensibilities. GCC lets 125639583Swpaul * you do casts on the LHS of an assignment, but ANSI doesn't 125739583Swpaul * allow that. 125838030Swpaul */ 125939583Swpaul sc->tl_ldata = (struct tl_list_data *)sc->tl_ldata_ptr; 126048443Speter round = (uintptr_t)sc->tl_ldata_ptr & 0xF; 126139583Swpaul roundptr = sc->tl_ldata_ptr; 126239583Swpaul for (i = 0; i < 8; i++) { 126339583Swpaul if (round % 8) { 126439583Swpaul round++; 126539583Swpaul roundptr++; 126639583Swpaul } else 126739583Swpaul break; 126838030Swpaul } 126939583Swpaul sc->tl_ldata = (struct tl_list_data *)roundptr; 127038030Swpaul 127139583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 127239583Swpaul 127339583Swpaul sc->tl_unit = unit; 127439583Swpaul sc->tl_dinfo = t; 127543235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 127639583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 127739583Swpaul if (t->tl_vid == OLICOM_VENDORID) 127839583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 127939583Swpaul 128039583Swpaul /* Reset the adapter. */ 128139583Swpaul tl_softreset(sc, 1); 128250468Swpaul tl_hardreset(dev); 128339583Swpaul tl_softreset(sc, 1); 128439583Swpaul 128538030Swpaul /* 128639583Swpaul * Get station address from the EEPROM. 128739583Swpaul */ 128839583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 128939583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 129049010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 129148992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 129249010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 129348992Swpaul free(sc->tl_ldata_ptr, M_DEVBUF); 129439583Swpaul printf("tl%d: failed to read station address\n", unit); 129548992Swpaul error = ENXIO; 129639583Swpaul goto fail; 129739583Swpaul } 129839583Swpaul 129939583Swpaul /* 130039583Swpaul * XXX Olicom, in its desire to be different from the 130139583Swpaul * rest of the world, has done strange things with the 130239583Swpaul * encoding of the station address in the EEPROM. First 130339583Swpaul * of all, they store the address at offset 0xF8 rather 130439583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 130539583Swpaul * Second, they store the address in three 16-bit words in 130639583Swpaul * network byte order, as opposed to storing it sequentially 130739583Swpaul * like all the other ThunderLAN cards. In order to get 130839583Swpaul * the station address in a form that matches what the Olicom 130939583Swpaul * diagnostic utility specifies, we have to byte-swap each 131039583Swpaul * word. To make things even more confusing, neither 00:00:28 131139583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 131239583Swpaul */ 131339583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 131439583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 131539583Swpaul u_int16_t *p; 131639583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 131739583Swpaul *p = ntohs(*p); 131839583Swpaul } 131939583Swpaul } 132039583Swpaul 132139583Swpaul /* 132236270Swpaul * A ThunderLAN chip was detected. Inform the world. 132336270Swpaul */ 132439583Swpaul printf("tl%d: Ethernet address: %6D\n", unit, 132539583Swpaul sc->arpcom.ac_enaddr, ":"); 132636270Swpaul 132739583Swpaul ifp = &sc->arpcom.ac_if; 132839583Swpaul ifp->if_softc = sc; 132939583Swpaul ifp->if_unit = sc->tl_unit; 133039583Swpaul ifp->if_name = "tl"; 133139583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 133239583Swpaul ifp->if_ioctl = tl_ioctl; 133339583Swpaul ifp->if_output = ether_output; 133439583Swpaul ifp->if_start = tl_start; 133539583Swpaul ifp->if_watchdog = tl_watchdog; 133639583Swpaul ifp->if_init = tl_init; 133739583Swpaul ifp->if_mtu = ETHERMTU; 133846561Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 133939583Swpaul callout_handle_init(&sc->tl_stat_ch); 134039583Swpaul 134139583Swpaul /* Reset the adapter again. */ 134239583Swpaul tl_softreset(sc, 1); 134350468Swpaul tl_hardreset(dev); 134439583Swpaul tl_softreset(sc, 1); 134539583Swpaul 134636270Swpaul /* 134750462Swpaul * Do MII setup. If no PHYs are found, then this is a 134850462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 134950462Swpaul * and AUI/BNC. 135036270Swpaul */ 135150462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 135250462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 135345155Swpaul struct ifmedia *ifm; 135445155Swpaul sc->tl_bitrate = 1; 135545155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 135645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 135745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 135845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 135945155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 136045166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 136145155Swpaul /* Reset again, this time setting bitrate mode. */ 136245155Swpaul tl_softreset(sc, 1); 136345155Swpaul ifm = &sc->ifmedia; 136445155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 136545155Swpaul tl_ifmedia_upd(ifp); 136636270Swpaul } 136736270Swpaul 136839583Swpaul /* 136939583Swpaul * Call MI attach routines. 137039583Swpaul */ 137139583Swpaul if_attach(ifp); 137239583Swpaul ether_ifattach(ifp); 137338030Swpaul 137448645Sdes#if NBPF > 0 137539583Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 137639583Swpaul#endif 137739583Swpaul 137836270Swpaulfail: 137936270Swpaul splx(s); 138048992Swpaul return(error); 138136270Swpaul} 138236270Swpaul 138348992Swpaulstatic int tl_detach(dev) 138448992Swpaul device_t dev; 138548992Swpaul{ 138648992Swpaul struct tl_softc *sc; 138748992Swpaul struct ifnet *ifp; 138848992Swpaul int s; 138948992Swpaul 139048992Swpaul s = splimp(); 139148992Swpaul 139248992Swpaul sc = device_get_softc(dev); 139348992Swpaul ifp = &sc->arpcom.ac_if; 139448992Swpaul 139548992Swpaul tl_stop(sc); 139648992Swpaul if_detach(ifp); 139748992Swpaul 139850462Swpaul bus_generic_detach(dev); 139950462Swpaul device_delete_child(dev, sc->tl_miibus); 140050462Swpaul 140148992Swpaul free(sc->tl_ldata_ptr, M_DEVBUF); 140250462Swpaul if (sc->tl_bitrate) 140350462Swpaul ifmedia_removeall(&sc->ifmedia); 140448992Swpaul 140548992Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 140648992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 140749010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 140848992Swpaul 140948992Swpaul splx(s); 141048992Swpaul 141148992Swpaul return(0); 141248992Swpaul} 141348992Swpaul 141436270Swpaul/* 141536270Swpaul * Initialize the transmit lists. 141636270Swpaul */ 141736270Swpaulstatic int tl_list_tx_init(sc) 141836270Swpaul struct tl_softc *sc; 141936270Swpaul{ 142036270Swpaul struct tl_chain_data *cd; 142136270Swpaul struct tl_list_data *ld; 142236270Swpaul int i; 142336270Swpaul 142436270Swpaul cd = &sc->tl_cdata; 142536270Swpaul ld = sc->tl_ldata; 142636270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 142736270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 142836270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 142936270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 143036270Swpaul else 143136270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 143236270Swpaul } 143336270Swpaul 143436270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 143536270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 143636270Swpaul sc->tl_txeoc = 1; 143736270Swpaul 143836270Swpaul return(0); 143936270Swpaul} 144036270Swpaul 144136270Swpaul/* 144236270Swpaul * Initialize the RX lists and allocate mbufs for them. 144336270Swpaul */ 144436270Swpaulstatic int tl_list_rx_init(sc) 144536270Swpaul struct tl_softc *sc; 144636270Swpaul{ 144736270Swpaul struct tl_chain_data *cd; 144836270Swpaul struct tl_list_data *ld; 144936270Swpaul int i; 145036270Swpaul 145136270Swpaul cd = &sc->tl_cdata; 145236270Swpaul ld = sc->tl_ldata; 145336270Swpaul 145440795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 145536270Swpaul cd->tl_rx_chain[i].tl_ptr = 145637626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 145739583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 145839583Swpaul return(ENOBUFS); 145940795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 146036270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 146136270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 146236270Swpaul } else { 146336270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 146436270Swpaul ld->tl_rx_list[i].tlist_fptr = 146536270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 146636270Swpaul } 146736270Swpaul } 146836270Swpaul 146936270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 147036270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 147136270Swpaul 147236270Swpaul return(0); 147336270Swpaul} 147436270Swpaul 147536270Swpaulstatic int tl_newbuf(sc, c) 147636270Swpaul struct tl_softc *sc; 147737626Swpaul struct tl_chain_onefrag *c; 147836270Swpaul{ 147936270Swpaul struct mbuf *m_new = NULL; 148036270Swpaul 148136270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 148236270Swpaul if (m_new == NULL) { 148339583Swpaul printf("tl%d: no memory for rx list -- packet dropped!", 148436270Swpaul sc->tl_unit); 148536270Swpaul return(ENOBUFS); 148636270Swpaul } 148736270Swpaul 148836270Swpaul MCLGET(m_new, M_DONTWAIT); 148936270Swpaul if (!(m_new->m_flags & M_EXT)) { 149039583Swpaul printf("tl%d: no memory for rx list -- packet dropped!", 149139583Swpaul sc->tl_unit); 149236270Swpaul m_freem(m_new); 149336270Swpaul return(ENOBUFS); 149436270Swpaul } 149536270Swpaul 149645155Swpaul#ifdef __alpha__ 149745155Swpaul m_new->m_data += 2; 149845155Swpaul#endif 149945155Swpaul 150036270Swpaul c->tl_mbuf = m_new; 150136270Swpaul c->tl_next = NULL; 150236270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 150336270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 150436270Swpaul c->tl_ptr->tlist_fptr = 0; 150537626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 150637626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 150736270Swpaul 150836270Swpaul return(0); 150936270Swpaul} 151036270Swpaul/* 151136270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 151236270Swpaul * tells us that a full ethernet frame has been captured and we need 151336270Swpaul * to handle it. 151436270Swpaul * 151536270Swpaul * Reception is done using 'lists' which consist of a header and a 151636270Swpaul * series of 10 data count/data address pairs that point to buffers. 151736270Swpaul * Initially you're supposed to create a list, populate it with pointers 151836270Swpaul * to buffers, then load the physical address of the list into the 151936270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 152036270Swpaul * frame into the buffers for you. 152136270Swpaul * 152236270Swpaul * To make things as fast as possible, we have the chip DMA directly 152336270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 152436270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 152536270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 152636270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 152736270Swpaul * the chip, it will always think it has an endless receive channel. 152836270Swpaul * 152936270Swpaul * If we happen to fall behind and the chip manages to fill up all of 153036270Swpaul * the buffers, it will generate an end of channel interrupt and wait 153136270Swpaul * for us to empty the chain and restart the receiver. 153236270Swpaul */ 153336270Swpaulstatic int tl_intvec_rxeof(xsc, type) 153436270Swpaul void *xsc; 153536270Swpaul u_int32_t type; 153636270Swpaul{ 153736270Swpaul struct tl_softc *sc; 153836270Swpaul int r = 0, total_len = 0; 153936270Swpaul struct ether_header *eh; 154036270Swpaul struct mbuf *m; 154136270Swpaul struct ifnet *ifp; 154237626Swpaul struct tl_chain_onefrag *cur_rx; 154336270Swpaul 154436270Swpaul sc = xsc; 154536270Swpaul ifp = &sc->arpcom.ac_if; 154636270Swpaul 154736270Swpaul while(sc->tl_cdata.tl_rx_head->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP){ 154836270Swpaul r++; 154936270Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 155036270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 155136270Swpaul m = cur_rx->tl_mbuf; 155236270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 155336270Swpaul 155439583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 155539583Swpaul ifp->if_ierrors++; 155639583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 155739583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 155839583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 155939583Swpaul continue; 156039583Swpaul } 156136270Swpaul 156236270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 156336270Swpaul vtophys(cur_rx->tl_ptr); 156436270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 156536270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 156636270Swpaul 156736270Swpaul eh = mtod(m, struct ether_header *); 156836270Swpaul m->m_pkthdr.rcvif = ifp; 156936270Swpaul 157037626Swpaul /* 157137626Swpaul * Note: when the ThunderLAN chip is in 'capture all 157237626Swpaul * frames' mode, it will receive its own transmissions. 157337626Swpaul * We drop don't need to process our own transmissions, 157437626Swpaul * so we drop them here and continue. 157537626Swpaul */ 157639583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 157739583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 157837626Swpaul ETHER_ADDR_LEN)) { 157937626Swpaul m_freem(m); 158037626Swpaul continue; 158137626Swpaul } 158237626Swpaul 158348645Sdes#if NBPF > 0 158436270Swpaul /* 158536270Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 158636270Swpaul * don't pass it up to the ether_input() layer unless it's 158736270Swpaul * a broadcast packet, multicast packet, matches our ethernet 158836270Swpaul * address or the interface is in promiscuous mode. If we don't 158936270Swpaul * want the packet, just forget it. We leave the mbuf in place 159036270Swpaul * since it can be used again later. 159136270Swpaul */ 159236270Swpaul if (ifp->if_bpf) { 159336270Swpaul m->m_pkthdr.len = m->m_len = total_len; 159436270Swpaul bpf_mtap(ifp, m); 159536270Swpaul if (ifp->if_flags & IFF_PROMISC && 159636270Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 159736270Swpaul ETHER_ADDR_LEN) && 159836270Swpaul (eh->ether_dhost[0] & 1) == 0)) { 159936270Swpaul m_freem(m); 160036270Swpaul continue; 160136270Swpaul } 160236270Swpaul } 160336270Swpaul#endif 160436270Swpaul /* Remove header from mbuf and pass it on. */ 160536270Swpaul m->m_pkthdr.len = m->m_len = 160636270Swpaul total_len - sizeof(struct ether_header); 160736270Swpaul m->m_data += sizeof(struct ether_header); 160836270Swpaul ether_input(ifp, eh, m); 160936270Swpaul } 161036270Swpaul 161136270Swpaul return(r); 161236270Swpaul} 161336270Swpaul 161436270Swpaul/* 161536270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 161636270Swpaul * initialized or the adapter reached a list with a forward pointer 161736270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 161836270Swpaul * the card has hit the end of the receive buffer chain and we need to 161936270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 162036270Swpaul */ 162136270Swpaulstatic int tl_intvec_rxeoc(xsc, type) 162236270Swpaul void *xsc; 162336270Swpaul u_int32_t type; 162436270Swpaul{ 162536270Swpaul struct tl_softc *sc; 162636270Swpaul int r; 162736270Swpaul 162836270Swpaul sc = xsc; 162936270Swpaul 163036270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 163136270Swpaul r = tl_intvec_rxeof(xsc, type); 163239583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 163336270Swpaul r = 1; 163439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 163536270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 163636270Swpaul return(r); 163736270Swpaul} 163836270Swpaul 163936270Swpaulstatic int tl_intvec_txeof(xsc, type) 164036270Swpaul void *xsc; 164136270Swpaul u_int32_t type; 164236270Swpaul{ 164336270Swpaul struct tl_softc *sc; 164436270Swpaul int r = 0; 164536270Swpaul struct tl_chain *cur_tx; 164636270Swpaul 164736270Swpaul sc = xsc; 164836270Swpaul 164936270Swpaul /* 165036270Swpaul * Go through our tx list and free mbufs for those 165136270Swpaul * frames that have been sent. 165236270Swpaul */ 165336270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 165436270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 165536270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 165636270Swpaul break; 165736270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 165836270Swpaul 165936270Swpaul r++; 166036270Swpaul m_freem(cur_tx->tl_mbuf); 166136270Swpaul cur_tx->tl_mbuf = NULL; 166236270Swpaul 166336270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 166436270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 166537626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 166637626Swpaul break; 166736270Swpaul } 166836270Swpaul 166936270Swpaul return(r); 167036270Swpaul} 167136270Swpaul 167236270Swpaul/* 167336270Swpaul * The transmit end of channel interrupt. The adapter triggers this 167436270Swpaul * interrupt to tell us it hit the end of the current transmit list. 167536270Swpaul * 167636270Swpaul * A note about this: it's possible for a condition to arise where 167736270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 167836270Swpaul * You have to avoid this since the chip expects things to go in a 167936270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 168036270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 168136270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 168236270Swpaul * interrupt should be received and acknowledged before any more frames 168336270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 168436270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 168536270Swpaul * it could attempt to issue a transmit command prematurely. 168636270Swpaul * 168736270Swpaul * To guard against this, tl_start() will only issue transmit commands 168836270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 168936270Swpaul * can set this flag once tl_start() has cleared it. 169036270Swpaul */ 169136270Swpaulstatic int tl_intvec_txeoc(xsc, type) 169236270Swpaul void *xsc; 169336270Swpaul u_int32_t type; 169436270Swpaul{ 169536270Swpaul struct tl_softc *sc; 169636270Swpaul struct ifnet *ifp; 169736270Swpaul u_int32_t cmd; 169836270Swpaul 169936270Swpaul sc = xsc; 170036270Swpaul ifp = &sc->arpcom.ac_if; 170136270Swpaul 170236270Swpaul /* Clear the timeout timer. */ 170336270Swpaul ifp->if_timer = 0; 170436270Swpaul 170536270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 170636270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 170736270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 170836270Swpaul sc->tl_txeoc = 1; 170936270Swpaul } else { 171036270Swpaul sc->tl_txeoc = 0; 171136270Swpaul /* First we have to ack the EOC interrupt. */ 171239583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 171336270Swpaul /* Then load the address of the next TX list. */ 171439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 171539583Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 171636270Swpaul /* Restart TX channel. */ 171739583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 171836270Swpaul cmd &= ~TL_CMD_RT; 171936270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 172039583Swpaul CMD_PUT(sc, cmd); 172136270Swpaul return(0); 172236270Swpaul } 172336270Swpaul 172436270Swpaul return(1); 172536270Swpaul} 172636270Swpaul 172736270Swpaulstatic int tl_intvec_adchk(xsc, type) 172836270Swpaul void *xsc; 172936270Swpaul u_int32_t type; 173036270Swpaul{ 173136270Swpaul struct tl_softc *sc; 173236270Swpaul 173336270Swpaul sc = xsc; 173436270Swpaul 173539627Swpaul if (type) 173639627Swpaul printf("tl%d: adapter check: %x\n", sc->tl_unit, 173741656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 173836270Swpaul 173939583Swpaul tl_softreset(sc, 1); 174037626Swpaul tl_stop(sc); 174136270Swpaul tl_init(sc); 174239583Swpaul CMD_SET(sc, TL_CMD_INTSON); 174336270Swpaul 174436270Swpaul return(0); 174536270Swpaul} 174636270Swpaul 174736270Swpaulstatic int tl_intvec_netsts(xsc, type) 174836270Swpaul void *xsc; 174936270Swpaul u_int32_t type; 175036270Swpaul{ 175136270Swpaul struct tl_softc *sc; 175236270Swpaul u_int16_t netsts; 175336270Swpaul 175436270Swpaul sc = xsc; 175536270Swpaul 175639583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 175739583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 175836270Swpaul 175936270Swpaul printf("tl%d: network status: %x\n", sc->tl_unit, netsts); 176036270Swpaul 176136270Swpaul return(1); 176236270Swpaul} 176336270Swpaul 176439583Swpaulstatic void tl_intr(xsc) 176539583Swpaul void *xsc; 176636270Swpaul{ 176736270Swpaul struct tl_softc *sc; 176836270Swpaul struct ifnet *ifp; 176936270Swpaul int r = 0; 177036270Swpaul u_int32_t type = 0; 177136270Swpaul u_int16_t ints = 0; 177236270Swpaul u_int8_t ivec = 0; 177336270Swpaul 177439583Swpaul sc = xsc; 177536270Swpaul 177636270Swpaul /* Disable interrupts */ 177739583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 177839583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 177936270Swpaul type = (ints << 16) & 0xFFFF0000; 178036270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 178136270Swpaul ints = (ints & TL_INT_MASK) >> 2; 178236270Swpaul 178336270Swpaul ifp = &sc->arpcom.ac_if; 178436270Swpaul 178536270Swpaul switch(ints) { 178636270Swpaul case (TL_INTR_INVALID): 178739583Swpaul#ifdef DIAGNOSTIC 178839583Swpaul printf("tl%d: got an invalid interrupt!\n", sc->tl_unit); 178939583Swpaul#endif 179039583Swpaul /* Re-enable interrupts but don't ack this one. */ 179139583Swpaul CMD_PUT(sc, type); 179239583Swpaul r = 0; 179336270Swpaul break; 179436270Swpaul case (TL_INTR_TXEOF): 179536270Swpaul r = tl_intvec_txeof((void *)sc, type); 179636270Swpaul break; 179736270Swpaul case (TL_INTR_TXEOC): 179836270Swpaul r = tl_intvec_txeoc((void *)sc, type); 179936270Swpaul break; 180036270Swpaul case (TL_INTR_STATOFLOW): 180139583Swpaul tl_stats_update(sc); 180239583Swpaul r = 1; 180336270Swpaul break; 180436270Swpaul case (TL_INTR_RXEOF): 180536270Swpaul r = tl_intvec_rxeof((void *)sc, type); 180636270Swpaul break; 180736270Swpaul case (TL_INTR_DUMMY): 180839583Swpaul printf("tl%d: got a dummy interrupt\n", sc->tl_unit); 180939583Swpaul r = 1; 181036270Swpaul break; 181136270Swpaul case (TL_INTR_ADCHK): 181236270Swpaul if (ivec) 181336270Swpaul r = tl_intvec_adchk((void *)sc, type); 181436270Swpaul else 181536270Swpaul r = tl_intvec_netsts((void *)sc, type); 181636270Swpaul break; 181736270Swpaul case (TL_INTR_RXEOC): 181836270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 181936270Swpaul break; 182036270Swpaul default: 182136270Swpaul printf("tl%d: bogus interrupt type\n", ifp->if_unit); 182236270Swpaul break; 182336270Swpaul } 182436270Swpaul 182536270Swpaul /* Re-enable interrupts */ 182637626Swpaul if (r) { 182739583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 182837626Swpaul } 182936270Swpaul 183037626Swpaul if (ifp->if_snd.ifq_head != NULL) 183137626Swpaul tl_start(ifp); 183237626Swpaul 183336270Swpaul return; 183436270Swpaul} 183536270Swpaul 183636270Swpaulstatic void tl_stats_update(xsc) 183736270Swpaul void *xsc; 183836270Swpaul{ 183936270Swpaul struct tl_softc *sc; 184036270Swpaul struct ifnet *ifp; 184136270Swpaul struct tl_stats tl_stats; 184250462Swpaul struct mii_data *mii; 184336270Swpaul u_int32_t *p; 184448992Swpaul int s; 184536270Swpaul 184648992Swpaul s = splimp(); 184748992Swpaul 184836270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 184936270Swpaul 185036270Swpaul sc = xsc; 185136270Swpaul ifp = &sc->arpcom.ac_if; 185236270Swpaul 185336270Swpaul p = (u_int32_t *)&tl_stats; 185436270Swpaul 185539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 185639583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 185739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 185839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 185939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 186039583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 186136270Swpaul 186236270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 186336270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 186436270Swpaul tl_stats.tl_tx_multi_collision; 186536270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 186636270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 186736270Swpaul tl_rx_overrun(tl_stats); 186836270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 186936270Swpaul 187036270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 187136302Swpaul 187250462Swpaul if (!sc->tl_bitrate) { 187350462Swpaul mii = device_get_softc(sc->tl_miibus); 187450462Swpaul mii_tick(mii); 187550462Swpaul } 187650462Swpaul 187748992Swpaul splx(s); 187848992Swpaul 187936302Swpaul return; 188036270Swpaul} 188136270Swpaul 188236270Swpaul/* 188336270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 188436270Swpaul * pointers to the fragment pointers. 188536270Swpaul */ 188636270Swpaulstatic int tl_encap(sc, c, m_head) 188736270Swpaul struct tl_softc *sc; 188836270Swpaul struct tl_chain *c; 188936270Swpaul struct mbuf *m_head; 189036270Swpaul{ 189136270Swpaul int frag = 0; 189236270Swpaul struct tl_frag *f = NULL; 189336270Swpaul int total_len; 189436270Swpaul struct mbuf *m; 189536270Swpaul 189636270Swpaul /* 189736270Swpaul * Start packing the mbufs in this chain into 189836270Swpaul * the fragment pointers. Stop when we run out 189936270Swpaul * of fragments or hit the end of the mbuf chain. 190036270Swpaul */ 190136270Swpaul m = m_head; 190236270Swpaul total_len = 0; 190336270Swpaul 190436270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 190536270Swpaul if (m->m_len != 0) { 190636270Swpaul if (frag == TL_MAXFRAGS) 190736270Swpaul break; 190836270Swpaul total_len+= m->m_len; 190936270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 191036270Swpaul vtophys(mtod(m, vm_offset_t)); 191136270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 191236270Swpaul frag++; 191336270Swpaul } 191436270Swpaul } 191536270Swpaul 191636270Swpaul /* 191736270Swpaul * Handle special cases. 191836270Swpaul * Special case #1: we used up all 10 fragments, but 191936270Swpaul * we have more mbufs left in the chain. Copy the 192036270Swpaul * data into an mbuf cluster. Note that we don't 192136270Swpaul * bother clearing the values in the other fragment 192236270Swpaul * pointers/counters; it wouldn't gain us anything, 192336270Swpaul * and would waste cycles. 192436270Swpaul */ 192536270Swpaul if (m != NULL) { 192636270Swpaul struct mbuf *m_new = NULL; 192736270Swpaul 192836270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 192936270Swpaul if (m_new == NULL) { 193036270Swpaul printf("tl%d: no memory for tx list", sc->tl_unit); 193136270Swpaul return(1); 193236270Swpaul } 193336270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 193436270Swpaul MCLGET(m_new, M_DONTWAIT); 193536270Swpaul if (!(m_new->m_flags & M_EXT)) { 193636270Swpaul m_freem(m_new); 193736270Swpaul printf("tl%d: no memory for tx list", 193836270Swpaul sc->tl_unit); 193936270Swpaul return(1); 194036270Swpaul } 194136270Swpaul } 194236270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 194336270Swpaul mtod(m_new, caddr_t)); 194436270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 194536270Swpaul m_freem(m_head); 194636270Swpaul m_head = m_new; 194736270Swpaul f = &c->tl_ptr->tl_frag[0]; 194836270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 194936270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 195036270Swpaul frag = 1; 195136270Swpaul } 195236270Swpaul 195336270Swpaul /* 195436270Swpaul * Special case #2: the frame is smaller than the minimum 195536270Swpaul * frame size. We have to pad it to make the chip happy. 195636270Swpaul */ 195736270Swpaul if (total_len < TL_MIN_FRAMELEN) { 195836270Swpaul if (frag == TL_MAXFRAGS) 195939583Swpaul printf("tl%d: all frags filled but " 196039583Swpaul "frame still to small!\n", sc->tl_unit); 196136270Swpaul f = &c->tl_ptr->tl_frag[frag]; 196236270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 196336270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 196436270Swpaul total_len += f->tlist_dcnt; 196536270Swpaul frag++; 196636270Swpaul } 196736270Swpaul 196836270Swpaul c->tl_mbuf = m_head; 196936270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 197036270Swpaul c->tl_ptr->tlist_frsize = total_len; 197136270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 197236270Swpaul c->tl_ptr->tlist_fptr = 0; 197336270Swpaul 197436270Swpaul return(0); 197536270Swpaul} 197636270Swpaul 197736270Swpaul/* 197836270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 197936270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 198036270Swpaul * copy of the pointers since the transmit list fragment pointers are 198136270Swpaul * physical addresses. 198236270Swpaul */ 198336270Swpaulstatic void tl_start(ifp) 198436270Swpaul struct ifnet *ifp; 198536270Swpaul{ 198636270Swpaul struct tl_softc *sc; 198736270Swpaul struct mbuf *m_head = NULL; 198836270Swpaul u_int32_t cmd; 198936270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 199036270Swpaul 199136270Swpaul sc = ifp->if_softc; 199236270Swpaul 199336270Swpaul /* 199436270Swpaul * Check for an available queue slot. If there are none, 199536270Swpaul * punt. 199636270Swpaul */ 199736270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 199836270Swpaul ifp->if_flags |= IFF_OACTIVE; 199936270Swpaul return; 200036270Swpaul } 200136270Swpaul 200236270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 200336270Swpaul 200436270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 200536270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 200636270Swpaul if (m_head == NULL) 200736270Swpaul break; 200836270Swpaul 200936270Swpaul /* Pick a chain member off the free list. */ 201036270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 201136270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 201236270Swpaul 201336270Swpaul cur_tx->tl_next = NULL; 201436270Swpaul 201536270Swpaul /* Pack the data into the list. */ 201636270Swpaul tl_encap(sc, cur_tx, m_head); 201736270Swpaul 201836270Swpaul /* Chain it together */ 201936270Swpaul if (prev != NULL) { 202036270Swpaul prev->tl_next = cur_tx; 202136270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 202236270Swpaul } 202336270Swpaul prev = cur_tx; 202436270Swpaul 202536270Swpaul /* 202636270Swpaul * If there's a BPF listener, bounce a copy of this frame 202736270Swpaul * to him. 202836270Swpaul */ 202948645Sdes#if NBPF > 0 203036270Swpaul if (ifp->if_bpf) 203136270Swpaul bpf_mtap(ifp, cur_tx->tl_mbuf); 203236270Swpaul#endif 203336270Swpaul } 203436270Swpaul 203536270Swpaul /* 203641526Swpaul * If there are no packets queued, bail. 203741526Swpaul */ 203841526Swpaul if (cur_tx == NULL) 203941526Swpaul return; 204041526Swpaul 204141526Swpaul /* 204236270Swpaul * That's all we can stands, we can't stands no more. 204336270Swpaul * If there are no other transfers pending, then issue the 204436270Swpaul * TX GO command to the adapter to start things moving. 204536270Swpaul * Otherwise, just leave the data in the queue and let 204636270Swpaul * the EOF/EOC interrupt handler send. 204736270Swpaul */ 204836270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 204936270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 205036270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 205139583Swpaul 205236270Swpaul if (sc->tl_txeoc) { 205336270Swpaul sc->tl_txeoc = 0; 205439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 205539583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 205636270Swpaul cmd &= ~TL_CMD_RT; 205736270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 205839583Swpaul CMD_PUT(sc, cmd); 205936270Swpaul } 206036270Swpaul } else { 206136270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 206242146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 206336270Swpaul } 206436270Swpaul 206536270Swpaul /* 206636270Swpaul * Set a timeout in case the chip goes out to lunch. 206736270Swpaul */ 206836270Swpaul ifp->if_timer = 5; 206936270Swpaul 207036270Swpaul return; 207136270Swpaul} 207236270Swpaul 207336270Swpaulstatic void tl_init(xsc) 207436270Swpaul void *xsc; 207536270Swpaul{ 207636270Swpaul struct tl_softc *sc = xsc; 207736270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 207836270Swpaul int s; 207950462Swpaul struct mii_data *mii; 208036270Swpaul 208136270Swpaul s = splimp(); 208236270Swpaul 208336270Swpaul ifp = &sc->arpcom.ac_if; 208436270Swpaul 208536270Swpaul /* 208636270Swpaul * Cancel pending I/O. 208736270Swpaul */ 208836270Swpaul tl_stop(sc); 208936270Swpaul 209036270Swpaul /* 209136270Swpaul * Set 'capture all frames' bit for promiscuous mode. 209236270Swpaul */ 209339583Swpaul if (ifp->if_flags & IFF_PROMISC) 209439583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 209539583Swpaul else 209639583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 209736270Swpaul 209836270Swpaul /* 209936270Swpaul * Set capture broadcast bit to capture broadcast frames. 210036270Swpaul */ 210139583Swpaul if (ifp->if_flags & IFF_BROADCAST) 210239583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210339583Swpaul else 210439583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210536270Swpaul 210650468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 210750468Swpaul 210836270Swpaul /* Init our MAC address */ 210941656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 211036270Swpaul 211139583Swpaul /* Init multicast filter, if needed. */ 211239583Swpaul tl_setmulti(sc); 211339583Swpaul 211436270Swpaul /* Init circular RX list. */ 211539583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 211639583Swpaul printf("tl%d: initialization failed: no " 211739583Swpaul "memory for rx buffers\n", sc->tl_unit); 211839583Swpaul tl_stop(sc); 211936270Swpaul return; 212036270Swpaul } 212136270Swpaul 212236270Swpaul /* Init TX pointers. */ 212336270Swpaul tl_list_tx_init(sc); 212436270Swpaul 212539583Swpaul /* Enable PCI interrupts. */ 212639583Swpaul CMD_SET(sc, TL_CMD_INTSON); 212736270Swpaul 212836270Swpaul /* Load the address of the rx list */ 212939583Swpaul CMD_SET(sc, TL_CMD_RT); 213039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 213136270Swpaul 213250462Swpaul if (!sc->tl_bitrate) { 213350462Swpaul if (sc->tl_miibus != NULL) { 213450462Swpaul mii = device_get_softc(sc->tl_miibus); 213550462Swpaul mii_mediachg(mii); 213650462Swpaul } 213750462Swpaul } 213838030Swpaul 213936270Swpaul /* Send the RX go command */ 214050468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 214136270Swpaul 214236270Swpaul ifp->if_flags |= IFF_RUNNING; 214336270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 214436270Swpaul 214536270Swpaul (void)splx(s); 214636270Swpaul 214736270Swpaul /* Start the stats update counter */ 214836270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 214936270Swpaul 215036270Swpaul return; 215136270Swpaul} 215236270Swpaul 215336270Swpaul/* 215436270Swpaul * Set media options. 215536270Swpaul */ 215636270Swpaulstatic int tl_ifmedia_upd(ifp) 215736270Swpaul struct ifnet *ifp; 215836270Swpaul{ 215936270Swpaul struct tl_softc *sc; 216050462Swpaul struct mii_data *mii = NULL; 216136270Swpaul 216236270Swpaul sc = ifp->if_softc; 216336270Swpaul 216450462Swpaul if (sc->tl_bitrate) 216550462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 216650462Swpaul else { 216750462Swpaul mii = device_get_softc(sc->tl_miibus); 216850462Swpaul mii_mediachg(mii); 216950462Swpaul } 217036270Swpaul 217136270Swpaul return(0); 217236270Swpaul} 217336270Swpaul 217436270Swpaul/* 217536270Swpaul * Report current media status. 217636270Swpaul */ 217736270Swpaulstatic void tl_ifmedia_sts(ifp, ifmr) 217836270Swpaul struct ifnet *ifp; 217936270Swpaul struct ifmediareq *ifmr; 218036270Swpaul{ 218136270Swpaul struct tl_softc *sc; 218250462Swpaul struct mii_data *mii; 218336270Swpaul 218436270Swpaul sc = ifp->if_softc; 218536270Swpaul 218636270Swpaul ifmr->ifm_active = IFM_ETHER; 218736270Swpaul 218845155Swpaul if (sc->tl_bitrate) { 218945155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 219045155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 219145155Swpaul else 219245155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 219345155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 219445155Swpaul ifmr->ifm_active |= IFM_HDX; 219545155Swpaul else 219645155Swpaul ifmr->ifm_active |= IFM_FDX; 219745155Swpaul return; 219836270Swpaul } else { 219950462Swpaul mii = device_get_softc(sc->tl_miibus); 220050462Swpaul mii_pollstat(mii); 220150462Swpaul ifmr->ifm_active = mii->mii_media_active; 220250462Swpaul ifmr->ifm_status = mii->mii_media_status; 220336270Swpaul } 220436270Swpaul 220536270Swpaul return; 220636270Swpaul} 220736270Swpaul 220836270Swpaulstatic int tl_ioctl(ifp, command, data) 220936270Swpaul struct ifnet *ifp; 221036735Sdfr u_long command; 221136270Swpaul caddr_t data; 221236270Swpaul{ 221336270Swpaul struct tl_softc *sc = ifp->if_softc; 221436270Swpaul struct ifreq *ifr = (struct ifreq *) data; 221536270Swpaul int s, error = 0; 221636270Swpaul 221736270Swpaul s = splimp(); 221836270Swpaul 221936270Swpaul switch(command) { 222036270Swpaul case SIOCSIFADDR: 222136270Swpaul case SIOCGIFADDR: 222236270Swpaul case SIOCSIFMTU: 222336270Swpaul error = ether_ioctl(ifp, command, data); 222436270Swpaul break; 222536270Swpaul case SIOCSIFFLAGS: 222636270Swpaul if (ifp->if_flags & IFF_UP) { 222750462Swpaul if (ifp->if_flags & IFF_RUNNING && 222850462Swpaul ifp->if_flags & IFF_PROMISC && 222950462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 223050462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 223150462Swpaul tl_setmulti(sc); 223250462Swpaul } else if (ifp->if_flags & IFF_RUNNING && 223350462Swpaul !(ifp->if_flags & IFF_PROMISC) && 223450462Swpaul sc->tl_if_flags & IFF_PROMISC) { 223550462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 223650462Swpaul tl_setmulti(sc); 223750462Swpaul } else 223850462Swpaul tl_init(sc); 223936270Swpaul } else { 224036270Swpaul if (ifp->if_flags & IFF_RUNNING) { 224136270Swpaul tl_stop(sc); 224236270Swpaul } 224336270Swpaul } 224450462Swpaul sc->tl_if_flags = ifp->if_flags; 224536270Swpaul error = 0; 224636270Swpaul break; 224736270Swpaul case SIOCADDMULTI: 224836270Swpaul case SIOCDELMULTI: 224936270Swpaul tl_setmulti(sc); 225036270Swpaul error = 0; 225136270Swpaul break; 225236270Swpaul case SIOCSIFMEDIA: 225336270Swpaul case SIOCGIFMEDIA: 225450462Swpaul if (sc->tl_bitrate) 225550462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 225650462Swpaul else { 225750462Swpaul struct mii_data *mii; 225850462Swpaul mii = device_get_softc(sc->tl_miibus); 225950462Swpaul error = ifmedia_ioctl(ifp, ifr, 226050462Swpaul &mii->mii_media, command); 226150462Swpaul } 226236270Swpaul break; 226336270Swpaul default: 226436270Swpaul error = EINVAL; 226536270Swpaul break; 226636270Swpaul } 226736270Swpaul 226836270Swpaul (void)splx(s); 226936270Swpaul 227036270Swpaul return(error); 227136270Swpaul} 227236270Swpaul 227336270Swpaulstatic void tl_watchdog(ifp) 227436270Swpaul struct ifnet *ifp; 227536270Swpaul{ 227636270Swpaul struct tl_softc *sc; 227736270Swpaul 227836270Swpaul sc = ifp->if_softc; 227936270Swpaul 228050462Swpaul printf("tl%d: device timeout\n", sc->tl_unit); 228136270Swpaul 228236270Swpaul ifp->if_oerrors++; 228336270Swpaul 228450468Swpaul tl_softreset(sc, 1); 228536270Swpaul tl_init(sc); 228636270Swpaul 228736270Swpaul return; 228836270Swpaul} 228936270Swpaul 229036270Swpaul/* 229136270Swpaul * Stop the adapter and free any mbufs allocated to the 229236270Swpaul * RX and TX lists. 229336270Swpaul */ 229436270Swpaulstatic void tl_stop(sc) 229536270Swpaul struct tl_softc *sc; 229636270Swpaul{ 229736270Swpaul register int i; 229836270Swpaul struct ifnet *ifp; 229936270Swpaul 230036270Swpaul ifp = &sc->arpcom.ac_if; 230136270Swpaul 230236270Swpaul /* Stop the stats updater. */ 230336270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 230436270Swpaul 230536270Swpaul /* Stop the transmitter */ 230639583Swpaul CMD_CLR(sc, TL_CMD_RT); 230739583Swpaul CMD_SET(sc, TL_CMD_STOP); 230839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 230936270Swpaul 231036270Swpaul /* Stop the receiver */ 231139583Swpaul CMD_SET(sc, TL_CMD_RT); 231239583Swpaul CMD_SET(sc, TL_CMD_STOP); 231339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231436270Swpaul 231536270Swpaul /* 231636270Swpaul * Disable host interrupts. 231736270Swpaul */ 231839583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 231936270Swpaul 232036270Swpaul /* 232136270Swpaul * Clear list pointer. 232236270Swpaul */ 232339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 232436270Swpaul 232536270Swpaul /* 232636270Swpaul * Free the RX lists. 232736270Swpaul */ 232836270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 232936270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 233036270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 233136270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 233236270Swpaul } 233336270Swpaul } 233436270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 233536270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 233636270Swpaul 233736270Swpaul /* 233836270Swpaul * Free the TX list buffers. 233936270Swpaul */ 234036270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 234136270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 234236270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 234336270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 234436270Swpaul } 234536270Swpaul } 234636270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 234736270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 234836270Swpaul 234936270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 235036270Swpaul 235136270Swpaul return; 235236270Swpaul} 235336270Swpaul 235436270Swpaul/* 235536270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 235636270Swpaul * get confused by errant DMAs when rebooting. 235736270Swpaul */ 235848992Swpaulstatic void tl_shutdown(dev) 235948992Swpaul device_t dev; 236036270Swpaul{ 236139583Swpaul struct tl_softc *sc; 236236270Swpaul 236348992Swpaul sc = device_get_softc(dev); 236436270Swpaul 236539583Swpaul tl_stop(sc); 236636270Swpaul 236736270Swpaul return; 236836270Swpaul} 2369