if_tl.c revision 214264
1139825Simp/*- 236270Swpaul * Copyright (c) 1997, 1998 336270Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 436270Swpaul * 536270Swpaul * Redistribution and use in source and binary forms, with or without 636270Swpaul * modification, are permitted provided that the following conditions 736270Swpaul * are met: 836270Swpaul * 1. Redistributions of source code must retain the above copyright 936270Swpaul * notice, this list of conditions and the following disclaimer. 1036270Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1136270Swpaul * notice, this list of conditions and the following disclaimer in the 1236270Swpaul * documentation and/or other materials provided with the distribution. 1336270Swpaul * 3. All advertising materials mentioning features or use of this software 1436270Swpaul * must display the following acknowledgement: 1536270Swpaul * This product includes software developed by Bill Paul. 1636270Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1736270Swpaul * may be used to endorse or promote products derived from this software 1836270Swpaul * without specific prior written permission. 1936270Swpaul * 2036270Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2136270Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2236270Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2336270Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2436270Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2536270Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2636270Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2736270Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2836270Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2936270Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3036270Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3136270Swpaul */ 3236270Swpaul 33122678Sobrien#include <sys/cdefs.h> 34122678Sobrien__FBSDID("$FreeBSD: head/sys/dev/tl/if_tl.c 214264 2010-10-24 12:51:02Z marius $"); 35122678Sobrien 3636270Swpaul/* 3736270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3836270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3936270Swpaul * the National Semiconductor DP83840A physical interface and the 4036270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 4136270Swpaul * 4239583Swpaul * Written using the following four documents: 4336270Swpaul * 4436270Swpaul * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) 4536270Swpaul * National Semiconductor DP83840A data sheet (www.national.com) 4636270Swpaul * Microchip Technology 24C02C data sheet (www.microchip.com) 4739583Swpaul * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) 4836270Swpaul * 4936270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 5036270Swpaul * Electrical Engineering Department 5136270Swpaul * Columbia University, New York City 5236270Swpaul */ 5336270Swpaul/* 5436270Swpaul * Some notes about the ThunderLAN: 5536270Swpaul * 5636270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5736270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5839583Swpaul * independent interface (MII) bus. The MII allows the ThunderLAN chip to 5936270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 6036270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 6136270Swpaul * to act as a complete ethernet interface. 6236270Swpaul * 6336270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6436270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6536270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6639583Swpaul * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters 6739583Swpaul * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in 6839583Swpaul * concert with the ThunderLAN's internal PHY to provide full 10/100 6939583Swpaul * support. This is cheaper than using a standalone external PHY for both 7039583Swpaul * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. 7139583Swpaul * A serial EEPROM is also attached to the ThunderLAN chip to provide 7239583Swpaul * power-up default register settings and for storing the adapter's 7339583Swpaul * station address. Although not supported by this driver, the ThunderLAN 7439583Swpaul * chip can also be connected to token ring PHYs. 7536270Swpaul * 7636270Swpaul * The ThunderLAN has a set of registers which can be used to issue 7739583Swpaul * commands, acknowledge interrupts, and to manipulate other internal 7836270Swpaul * registers on its DIO bus. The primary registers can be accessed 7936270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 8036270Swpaul * depending on how the card is configured during the PCI probing 8136270Swpaul * phase. It is even possible to have both PIO and memory mapped 8236270Swpaul * access turned on at the same time. 8336270Swpaul * 8436270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 8536270Swpaul * using frame 'lists.' A list structure looks more or less like this: 8636270Swpaul * 8736270Swpaul * struct tl_frag { 8836270Swpaul * u_int32_t fragment_address; 8936270Swpaul * u_int32_t fragment_size; 9036270Swpaul * }; 9136270Swpaul * struct tl_list { 9236270Swpaul * u_int32_t forward_pointer; 9336270Swpaul * u_int16_t cstat; 9436270Swpaul * u_int16_t frame_size; 9536270Swpaul * struct tl_frag fragments[10]; 9636270Swpaul * }; 9736270Swpaul * 9836270Swpaul * The forward pointer in the list header can be either a 0 or the address 9936270Swpaul * of another list, which allows several lists to be linked together. Each 10036270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 10136270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 10236270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 10336270Swpaul * addresses are physical memory addresses, not virtual. Note also that 10436270Swpaul * a single ethernet frame can not span lists: if the host wants to 10536270Swpaul * transmit a frame and the frame data is split up over more than 10 10636270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 10736270Swpaul * 10836270Swpaul * To receive frames, the driver sets up a number of lists and populates 10936270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 11036270Swpaul * When a frame is received, the chip will DMA it into the memory regions 11136270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 11236270Swpaul * frame interrupt' when done. The driver may choose to use only one 11336270Swpaul * fragment per list; this may result is slighltly less efficient use 11436270Swpaul * of memory in exchange for improving performance. 11536270Swpaul * 11636270Swpaul * To transmit frames, the driver again sets up lists and fragment 11736270Swpaul * descriptors, only this time the buffers contain frame data that 11836270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 11936270Swpaul * has transfered the data into its on-board SRAM, it will trigger a 12036270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 12136270Swpaul * interrupt when it reaches the end of the list. 12236270Swpaul */ 12336270Swpaul/* 12436270Swpaul * Some notes about this driver: 12536270Swpaul * 12636270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 12736270Swpaul * reception, transmission and interrupt handling. The simplest approach 12836270Swpaul * is to use one list each for transmission and reception. In this mode, 12936270Swpaul * the ThunderLAN will generate two interrupts for every received frame 13036270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 13136270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 13236270Swpaul * performance to have to handle so many interrupts. 13336270Swpaul * 13436270Swpaul * Initially I wanted to create a circular list of receive buffers so 13536270Swpaul * that the ThunderLAN chip would think there was an infinitely long 13636270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 13736270Swpaul * doesn't work correctly under heavy load: while the manual says the 13836270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 13936270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 14036270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 14136270Swpaul * is that the chip might traverse the entire circular list and then wrap 14236270Swpaul * around before you have a chance to do anything about it. Consequently, 14336270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 14436270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 14536270Swpaul * is shifted to the end of the list. This gives the appearance of an 14636270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 14736270Swpaul * the chip and allow all of the lists to be filled up. 14836270Swpaul * 14936270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 15036270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 15136270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 15236270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 15336270Swpaul * 15436270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 15536270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 15636270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 15736270Swpaul * queue several frames in one shot, and only have to handle a total 15836270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 15936270Swpaul * are transmitted. Frame transmission is done directly out of the 16036270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 16136270Swpaul * The driver simply sets up the fragment descriptors in the transmit 16236270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 16336270Swpaul * 16436270Swpaul * Note that since the RX and TX lists themselves are always used 16536270Swpaul * only by the driver, the are malloc()ed once at driver initialization 16636270Swpaul * time and never free()ed. 16736270Swpaul * 16836270Swpaul * Also, in order to remain as platform independent as possible, this 16936270Swpaul * driver uses memory mapped register access to manipulate the card 17036270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 17136270Swpaul * (and related) instructions which are specific to the i386 platform. 17236270Swpaul * 17336270Swpaul * Using these techniques, this driver achieves very high performance 17436270Swpaul * by minimizing the amount of interrupts generated during large 17536270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 17636270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 17736270Swpaul * itself thereby reducing the load on the host CPU. 17836270Swpaul */ 17936270Swpaul 18036270Swpaul#include <sys/param.h> 18136270Swpaul#include <sys/systm.h> 18236270Swpaul#include <sys/sockio.h> 18336270Swpaul#include <sys/mbuf.h> 18436270Swpaul#include <sys/malloc.h> 18536270Swpaul#include <sys/kernel.h> 186129878Sphk#include <sys/module.h> 18736270Swpaul#include <sys/socket.h> 18836270Swpaul 18936270Swpaul#include <net/if.h> 19036270Swpaul#include <net/if_arp.h> 19136270Swpaul#include <net/ethernet.h> 19236270Swpaul#include <net/if_dl.h> 19336270Swpaul#include <net/if_media.h> 194147256Sbrooks#include <net/if_types.h> 19536270Swpaul 19636270Swpaul#include <net/bpf.h> 19736270Swpaul 19836270Swpaul#include <vm/vm.h> /* for vtophys */ 19936270Swpaul#include <vm/pmap.h> /* for vtophys */ 20045155Swpaul#include <machine/bus.h> 20148992Swpaul#include <machine/resource.h> 20248992Swpaul#include <sys/bus.h> 20348992Swpaul#include <sys/rman.h> 20436270Swpaul 20550462Swpaul#include <dev/mii/mii.h> 20650462Swpaul#include <dev/mii/miivar.h> 20750462Swpaul 208119288Simp#include <dev/pci/pcireg.h> 209119288Simp#include <dev/pci/pcivar.h> 21036270Swpaul 21139957Swpaul/* 21239957Swpaul * Default to using PIO register access mode to pacify certain 21339957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21439957Swpaul * don't seem to handle memory mapped mode properly. 21539957Swpaul */ 21639957Swpaul#define TL_USEIOSPACE 21739957Swpaul 218181738Simp#include <dev/tl/if_tlreg.h> 21936270Swpaul 220113506SmdoddMODULE_DEPEND(tl, pci, 1, 1, 1); 221113506SmdoddMODULE_DEPEND(tl, ether, 1, 1, 1); 22259758SpeterMODULE_DEPEND(tl, miibus, 1, 1, 1); 22359758Speter 224151545Simp/* "device miibus" required. See GENERIC if you get errors here. */ 22550462Swpaul#include "miibus_if.h" 22650462Swpaul 22736270Swpaul/* 22836270Swpaul * Various supported device vendors/types and their names. 22936270Swpaul */ 23036270Swpaul 23136270Swpaulstatic struct tl_type tl_devs[] = { 23236270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23336270Swpaul "Texas Instruments ThunderLAN" }, 23436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23536270Swpaul "Compaq Netelligent 10" }, 23636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 23736270Swpaul "Compaq Netelligent 10/100" }, 23836270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 23936270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24036270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24136270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24236270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24336270Swpaul "Compaq NetFlex-3/P Integrated" }, 24436270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24536270Swpaul "Compaq NetFlex-3/P" }, 24636270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 24736270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 24837626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 24937626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25037626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25137626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25237626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25337626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25437626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25537626Swpaul "Olicom OC-2183/2185" }, 25637626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 25737626Swpaul "Olicom OC-2325" }, 25837626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 25937626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26036270Swpaul { 0, 0, NULL } 26136270Swpaul}; 26236270Swpaul 263142407Simpstatic int tl_probe(device_t); 264142407Simpstatic int tl_attach(device_t); 265142407Simpstatic int tl_detach(device_t); 266142407Simpstatic int tl_intvec_rxeoc(void *, u_int32_t); 267142407Simpstatic int tl_intvec_txeoc(void *, u_int32_t); 268142407Simpstatic int tl_intvec_txeof(void *, u_int32_t); 269142407Simpstatic int tl_intvec_rxeof(void *, u_int32_t); 270142407Simpstatic int tl_intvec_adchk(void *, u_int32_t); 271142407Simpstatic int tl_intvec_netsts(void *, u_int32_t); 27236270Swpaul 273142407Simpstatic int tl_newbuf(struct tl_softc *, struct tl_chain_onefrag *); 274142407Simpstatic void tl_stats_update(void *); 275142407Simpstatic int tl_encap(struct tl_softc *, struct tl_chain *, struct mbuf *); 27636270Swpaul 277142407Simpstatic void tl_intr(void *); 278142407Simpstatic void tl_start(struct ifnet *); 279150171Sjhbstatic void tl_start_locked(struct ifnet *); 280142407Simpstatic int tl_ioctl(struct ifnet *, u_long, caddr_t); 281142407Simpstatic void tl_init(void *); 282150171Sjhbstatic void tl_init_locked(struct tl_softc *); 283142407Simpstatic void tl_stop(struct tl_softc *); 284199560Sjhbstatic void tl_watchdog(struct tl_softc *); 285188463Simpstatic int tl_shutdown(device_t); 286142407Simpstatic int tl_ifmedia_upd(struct ifnet *); 287142407Simpstatic void tl_ifmedia_sts(struct ifnet *, struct ifmediareq *); 28836270Swpaul 289142407Simpstatic u_int8_t tl_eeprom_putbyte(struct tl_softc *, int); 290142407Simpstatic u_int8_t tl_eeprom_getbyte(struct tl_softc *, int, u_int8_t *); 291142407Simpstatic int tl_read_eeprom(struct tl_softc *, caddr_t, int, int); 29236270Swpaul 293142407Simpstatic void tl_mii_sync(struct tl_softc *); 294142407Simpstatic void tl_mii_send(struct tl_softc *, u_int32_t, int); 295142407Simpstatic int tl_mii_readreg(struct tl_softc *, struct tl_mii_frame *); 296142407Simpstatic int tl_mii_writereg(struct tl_softc *, struct tl_mii_frame *); 297142407Simpstatic int tl_miibus_readreg(device_t, int, int); 298142407Simpstatic int tl_miibus_writereg(device_t, int, int, int); 299142407Simpstatic void tl_miibus_statchg(device_t); 30036270Swpaul 301142407Simpstatic void tl_setmode(struct tl_softc *, int); 302142407Simpstatic uint32_t tl_mchash(const uint8_t *); 303142407Simpstatic void tl_setmulti(struct tl_softc *); 304142407Simpstatic void tl_setfilt(struct tl_softc *, caddr_t, int); 305142407Simpstatic void tl_softreset(struct tl_softc *, int); 306142407Simpstatic void tl_hardreset(device_t); 307142407Simpstatic int tl_list_rx_init(struct tl_softc *); 308142407Simpstatic int tl_list_tx_init(struct tl_softc *); 30936270Swpaul 310142407Simpstatic u_int8_t tl_dio_read8(struct tl_softc *, int); 311142407Simpstatic u_int16_t tl_dio_read16(struct tl_softc *, int); 312142407Simpstatic u_int32_t tl_dio_read32(struct tl_softc *, int); 313142407Simpstatic void tl_dio_write8(struct tl_softc *, int, int); 314142407Simpstatic void tl_dio_write16(struct tl_softc *, int, int); 315142407Simpstatic void tl_dio_write32(struct tl_softc *, int, int); 316142407Simpstatic void tl_dio_setbit(struct tl_softc *, int, int); 317142407Simpstatic void tl_dio_clrbit(struct tl_softc *, int, int); 318142407Simpstatic void tl_dio_setbit16(struct tl_softc *, int, int); 319142407Simpstatic void tl_dio_clrbit16(struct tl_softc *, int, int); 32039583Swpaul 32149010Swpaul#ifdef TL_USEIOSPACE 32249010Swpaul#define TL_RES SYS_RES_IOPORT 32349010Swpaul#define TL_RID TL_PCI_LOIO 32449010Swpaul#else 32549010Swpaul#define TL_RES SYS_RES_MEMORY 32649010Swpaul#define TL_RID TL_PCI_LOMEM 32749010Swpaul#endif 32849010Swpaul 32948992Swpaulstatic device_method_t tl_methods[] = { 33048992Swpaul /* Device interface */ 33148992Swpaul DEVMETHOD(device_probe, tl_probe), 33248992Swpaul DEVMETHOD(device_attach, tl_attach), 33348992Swpaul DEVMETHOD(device_detach, tl_detach), 33448992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33550462Swpaul 33650462Swpaul /* bus interface */ 33750462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 33850462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 33950462Swpaul 34050462Swpaul /* MII interface */ 34150462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34250462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34350462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34450462Swpaul 34548992Swpaul { 0, 0 } 34648992Swpaul}; 34748992Swpaul 34848992Swpaulstatic driver_t tl_driver = { 34951455Swpaul "tl", 35048992Swpaul tl_methods, 35148992Swpaul sizeof(struct tl_softc) 35248992Swpaul}; 35348992Swpaul 35448992Swpaulstatic devclass_t tl_devclass; 35548992Swpaul 356113506SmdoddDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 35751473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 35848992Swpaul 35939583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 36041656Swpaul struct tl_softc *sc; 36141656Swpaul int reg; 36239583Swpaul{ 36339583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36439583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36539583Swpaul} 36639583Swpaul 36739583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 36841656Swpaul struct tl_softc *sc; 36941656Swpaul int reg; 37039583Swpaul{ 37139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37239583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37339583Swpaul} 37439583Swpaul 37539583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 37641656Swpaul struct tl_softc *sc; 37741656Swpaul int reg; 37839583Swpaul{ 37939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38039583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 38139583Swpaul} 38239583Swpaul 38339583Swpaulstatic void tl_dio_write8(sc, reg, val) 38441656Swpaul struct tl_softc *sc; 38541656Swpaul int reg; 38641656Swpaul int val; 38739583Swpaul{ 38839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38939583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 39039583Swpaul return; 39139583Swpaul} 39239583Swpaul 39339583Swpaulstatic void tl_dio_write16(sc, reg, val) 39441656Swpaul struct tl_softc *sc; 39541656Swpaul int reg; 39641656Swpaul int val; 39739583Swpaul{ 39839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39939583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 40039583Swpaul return; 40139583Swpaul} 40239583Swpaul 40339583Swpaulstatic void tl_dio_write32(sc, reg, val) 40441656Swpaul struct tl_softc *sc; 40541656Swpaul int reg; 40641656Swpaul int val; 40739583Swpaul{ 40839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40939583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 41039583Swpaul return; 41139583Swpaul} 41239583Swpaul 413102336Salfredstatic void 414102336Salfredtl_dio_setbit(sc, reg, bit) 41541656Swpaul struct tl_softc *sc; 41641656Swpaul int reg; 41741656Swpaul int bit; 41839583Swpaul{ 41939583Swpaul u_int8_t f; 42039583Swpaul 42139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42239583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42339583Swpaul f |= bit; 42439583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 42539583Swpaul 42639583Swpaul return; 42739583Swpaul} 42839583Swpaul 429102336Salfredstatic void 430102336Salfredtl_dio_clrbit(sc, reg, bit) 43141656Swpaul struct tl_softc *sc; 43241656Swpaul int reg; 43341656Swpaul int bit; 43439583Swpaul{ 43539583Swpaul u_int8_t f; 43639583Swpaul 43739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 43839583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 43939583Swpaul f &= ~bit; 44039583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44139583Swpaul 44239583Swpaul return; 44339583Swpaul} 44439583Swpaul 44539583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44641656Swpaul struct tl_softc *sc; 44741656Swpaul int reg; 44841656Swpaul int bit; 44939583Swpaul{ 45039583Swpaul u_int16_t f; 45139583Swpaul 45239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45339583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45439583Swpaul f |= bit; 45539583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45639583Swpaul 45739583Swpaul return; 45839583Swpaul} 45939583Swpaul 46039583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46141656Swpaul struct tl_softc *sc; 46241656Swpaul int reg; 46341656Swpaul int bit; 46439583Swpaul{ 46539583Swpaul u_int16_t f; 46639583Swpaul 46739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 46839583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 46939583Swpaul f &= ~bit; 47039583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47139583Swpaul 47239583Swpaul return; 47339583Swpaul} 47439583Swpaul 47536270Swpaul/* 47636270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 47736270Swpaul */ 47839583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 47939583Swpaul struct tl_softc *sc; 48041656Swpaul int byte; 48136270Swpaul{ 48236270Swpaul register int i, ack = 0; 48336270Swpaul 48436270Swpaul /* 48536270Swpaul * Make sure we're in TX mode. 48636270Swpaul */ 48739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 48836270Swpaul 48936270Swpaul /* 49036270Swpaul * Feed in each bit and stobe the clock. 49136270Swpaul */ 49236270Swpaul for (i = 0x80; i; i >>= 1) { 49336270Swpaul if (byte & i) { 49439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49536270Swpaul } else { 49639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 49736270Swpaul } 49839583Swpaul DELAY(1); 49939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50039583Swpaul DELAY(1); 50139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50236270Swpaul } 50336270Swpaul 50436270Swpaul /* 50536270Swpaul * Turn off TX mode. 50636270Swpaul */ 50739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 50836270Swpaul 50936270Swpaul /* 51036270Swpaul * Check for ack. 51136270Swpaul */ 51239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51339583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51536270Swpaul 51636270Swpaul return(ack); 51736270Swpaul} 51836270Swpaul 51936270Swpaul/* 52036270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52136270Swpaul */ 52239583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52339583Swpaul struct tl_softc *sc; 52441656Swpaul int addr; 52536270Swpaul u_int8_t *dest; 52636270Swpaul{ 52736270Swpaul register int i; 52836270Swpaul u_int8_t byte = 0; 529162315Sglebius device_t tl_dev = sc->tl_dev; 53036270Swpaul 53139583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53239583Swpaul 53336270Swpaul EEPROM_START; 53439583Swpaul 53536270Swpaul /* 53636270Swpaul * Send write control code to EEPROM. 53736270Swpaul */ 53839583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 539162315Sglebius device_printf(tl_dev, "failed to send write command, status: %x\n", 540105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54136270Swpaul return(1); 54239583Swpaul } 54336270Swpaul 54436270Swpaul /* 54536270Swpaul * Send address of byte we want to read. 54636270Swpaul */ 54739583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 548162315Sglebius device_printf(tl_dev, "failed to send address, status: %x\n", 549105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 55036270Swpaul return(1); 55139583Swpaul } 55236270Swpaul 55336270Swpaul EEPROM_STOP; 55436270Swpaul EEPROM_START; 55536270Swpaul /* 55636270Swpaul * Send read control code to EEPROM. 55736270Swpaul */ 55839583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 559162315Sglebius device_printf(tl_dev, "failed to send write command, status: %x\n", 560105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 56136270Swpaul return(1); 56239583Swpaul } 56336270Swpaul 56436270Swpaul /* 56536270Swpaul * Start reading bits from EEPROM. 56636270Swpaul */ 56739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56836270Swpaul for (i = 0x80; i; i >>= 1) { 56939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57039583Swpaul DELAY(1); 57139583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57236270Swpaul byte |= i; 57339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57436501Swpaul DELAY(1); 57536270Swpaul } 57636270Swpaul 57736270Swpaul EEPROM_STOP; 57836270Swpaul 57936270Swpaul /* 58036270Swpaul * No ACK generated for read, so just return byte. 58136270Swpaul */ 58236270Swpaul 58336270Swpaul *dest = byte; 58436270Swpaul 58536270Swpaul return(0); 58636270Swpaul} 58736270Swpaul 58839583Swpaul/* 58939583Swpaul * Read a sequence of bytes from the EEPROM. 59039583Swpaul */ 591102336Salfredstatic int 592102336Salfredtl_read_eeprom(sc, dest, off, cnt) 59339583Swpaul struct tl_softc *sc; 59439583Swpaul caddr_t dest; 59539583Swpaul int off; 59639583Swpaul int cnt; 59736270Swpaul{ 59839583Swpaul int err = 0, i; 59939583Swpaul u_int8_t byte = 0; 60039583Swpaul 60139583Swpaul for (i = 0; i < cnt; i++) { 60239583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60339583Swpaul if (err) 60439583Swpaul break; 60539583Swpaul *(dest + i) = byte; 60639583Swpaul } 60739583Swpaul 60839583Swpaul return(err ? 1 : 0); 60939583Swpaul} 61039583Swpaul 611102336Salfredstatic void 612102336Salfredtl_mii_sync(sc) 61339583Swpaul struct tl_softc *sc; 61439583Swpaul{ 61536270Swpaul register int i; 61636270Swpaul 61739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 61836270Swpaul 61936270Swpaul for (i = 0; i < 32; i++) { 62039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62236270Swpaul } 62336270Swpaul 62436270Swpaul return; 62536270Swpaul} 62636270Swpaul 627102336Salfredstatic void 628102336Salfredtl_mii_send(sc, bits, cnt) 62939583Swpaul struct tl_softc *sc; 63036270Swpaul u_int32_t bits; 63136270Swpaul int cnt; 63236270Swpaul{ 63336270Swpaul int i; 63436270Swpaul 63536270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63736270Swpaul if (bits & i) { 63839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 63936270Swpaul } else { 64039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64136270Swpaul } 64239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64336270Swpaul } 64436270Swpaul} 64536270Swpaul 646102336Salfredstatic int 647102336Salfredtl_mii_readreg(sc, frame) 64839583Swpaul struct tl_softc *sc; 64936270Swpaul struct tl_mii_frame *frame; 65036270Swpaul 65136270Swpaul{ 65267087Swpaul int i, ack; 65336270Swpaul int minten = 0; 65436270Swpaul 65539583Swpaul tl_mii_sync(sc); 65636270Swpaul 65736270Swpaul /* 65836270Swpaul * Set up frame for RX. 65936270Swpaul */ 66036270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66136270Swpaul frame->mii_opcode = TL_MII_READOP; 66236270Swpaul frame->mii_turnaround = 0; 66336270Swpaul frame->mii_data = 0; 66436270Swpaul 66536270Swpaul /* 66636270Swpaul * Turn off MII interrupt by forcing MINTEN low. 66736270Swpaul */ 66839583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 66936270Swpaul if (minten) { 67039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67136270Swpaul } 67236270Swpaul 67336270Swpaul /* 67436270Swpaul * Turn on data xmit. 67536270Swpaul */ 67639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 67736270Swpaul 67836270Swpaul /* 67936270Swpaul * Send command/address info. 68036270Swpaul */ 68139583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68239583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68339583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68439583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 68536270Swpaul 68636270Swpaul /* 68736270Swpaul * Turn off xmit. 68836270Swpaul */ 68939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69036270Swpaul 69136270Swpaul /* Idle bit */ 69239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69339583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69436270Swpaul 69536270Swpaul /* Check for ack */ 69639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69739583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 69836270Swpaul 69936270Swpaul /* Complete the cycle */ 70039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70136270Swpaul 70236270Swpaul /* 70336270Swpaul * Now try reading data bits. If the ack failed, we still 70436270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 70536270Swpaul */ 70636270Swpaul if (ack) { 70736270Swpaul for(i = 0; i < 16; i++) { 70839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 70939583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71036270Swpaul } 71136270Swpaul goto fail; 71236270Swpaul } 71336270Swpaul 71436270Swpaul for (i = 0x8000; i; i >>= 1) { 71539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71636270Swpaul if (!ack) { 71739583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 71836270Swpaul frame->mii_data |= i; 71936270Swpaul } 72039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72136270Swpaul } 72236270Swpaul 72336270Swpaulfail: 72436270Swpaul 72539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72736270Swpaul 72836270Swpaul /* Reenable interrupts */ 72936270Swpaul if (minten) { 73039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73136270Swpaul } 73236270Swpaul 73336270Swpaul if (ack) 73436270Swpaul return(1); 73536270Swpaul return(0); 73636270Swpaul} 73736270Swpaul 738102336Salfredstatic int 739102336Salfredtl_mii_writereg(sc, frame) 74039583Swpaul struct tl_softc *sc; 74136270Swpaul struct tl_mii_frame *frame; 74236270Swpaul 74336270Swpaul{ 74436270Swpaul int minten; 74536270Swpaul 74639583Swpaul tl_mii_sync(sc); 74736270Swpaul 74836270Swpaul /* 74936270Swpaul * Set up frame for TX. 75036270Swpaul */ 75136270Swpaul 75236270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 75336270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 75436270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 75536270Swpaul 75636270Swpaul /* 75736270Swpaul * Turn off MII interrupt by forcing MINTEN low. 75836270Swpaul */ 75939583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76036270Swpaul if (minten) { 76139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76236270Swpaul } 76336270Swpaul 76436270Swpaul /* 76536270Swpaul * Turn on data output. 76636270Swpaul */ 76739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 76836270Swpaul 76939583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77039583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77139583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77239583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 77339583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 77439583Swpaul tl_mii_send(sc, frame->mii_data, 16); 77536270Swpaul 77639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 77739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 77836270Swpaul 77936270Swpaul /* 78036270Swpaul * Turn off xmit. 78136270Swpaul */ 78239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 78336270Swpaul 78436270Swpaul /* Reenable interrupts */ 78536270Swpaul if (minten) 78639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 78736270Swpaul 78836270Swpaul return(0); 78936270Swpaul} 79036270Swpaul 791102336Salfredstatic int 792102336Salfredtl_miibus_readreg(dev, phy, reg) 79350462Swpaul device_t dev; 79450462Swpaul int phy, reg; 79550462Swpaul{ 79636270Swpaul struct tl_softc *sc; 79736270Swpaul struct tl_mii_frame frame; 79836270Swpaul 79950462Swpaul sc = device_get_softc(dev); 80036270Swpaul bzero((char *)&frame, sizeof(frame)); 80136270Swpaul 80250462Swpaul frame.mii_phyaddr = phy; 80336270Swpaul frame.mii_regaddr = reg; 80439583Swpaul tl_mii_readreg(sc, &frame); 80536270Swpaul 80636270Swpaul return(frame.mii_data); 80736270Swpaul} 80836270Swpaul 809102336Salfredstatic int 810102336Salfredtl_miibus_writereg(dev, phy, reg, data) 81150462Swpaul device_t dev; 81250462Swpaul int phy, reg, data; 81350462Swpaul{ 81436270Swpaul struct tl_softc *sc; 81536270Swpaul struct tl_mii_frame frame; 81636270Swpaul 81750462Swpaul sc = device_get_softc(dev); 81836270Swpaul bzero((char *)&frame, sizeof(frame)); 81936270Swpaul 82050462Swpaul frame.mii_phyaddr = phy; 82136270Swpaul frame.mii_regaddr = reg; 82236270Swpaul frame.mii_data = data; 82336270Swpaul 82439583Swpaul tl_mii_writereg(sc, &frame); 82536270Swpaul 82650462Swpaul return(0); 82736270Swpaul} 82836270Swpaul 829102336Salfredstatic void 830102336Salfredtl_miibus_statchg(dev) 83150462Swpaul device_t dev; 83250462Swpaul{ 83336270Swpaul struct tl_softc *sc; 83450462Swpaul struct mii_data *mii; 83536270Swpaul 83650462Swpaul sc = device_get_softc(dev); 83750462Swpaul mii = device_get_softc(sc->tl_miibus); 83836270Swpaul 83950462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 84050462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84136270Swpaul } else { 84250462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84336270Swpaul } 84436270Swpaul 84536270Swpaul return; 84636270Swpaul} 84736270Swpaul 84836270Swpaul/* 84950462Swpaul * Set modes for bitrate devices. 85036270Swpaul */ 851102336Salfredstatic void 852102336Salfredtl_setmode(sc, media) 85336270Swpaul struct tl_softc *sc; 85436270Swpaul int media; 85536270Swpaul{ 85650462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 85750462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 85836270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 85950462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86036270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 86150462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86336270Swpaul } else { 86450462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 86539583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 86636270Swpaul } 86736270Swpaul } 86836270Swpaul 86936270Swpaul return; 87036270Swpaul} 87136270Swpaul 87236464Swpaul/* 87336464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 87436464Swpaul * table. This hash is simply the address split into 6-bit chunks 87536464Swpaul * XOR'd, e.g. 87636464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 87736464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 87836464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 87936464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 88036464Swpaul */ 881123289Sobrienstatic uint32_t 882122625Sobrientl_mchash(addr) 883123289Sobrien const uint8_t *addr; 88436270Swpaul{ 885123289Sobrien int t; 88636270Swpaul 88736464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 88836464Swpaul (addr[2] ^ addr[5]); 88936464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 89036270Swpaul} 89136270Swpaul 89239583Swpaul/* 89339583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 89439583Swpaul * the multicast hash filter. The perfect filter can be programmed 89539583Swpaul * with up to four MAC addresses. The first one is always used to 89639583Swpaul * hold the station address, which leaves us free to use the other 89739583Swpaul * three for multicast addresses. 89839583Swpaul */ 899102336Salfredstatic void 900102336Salfredtl_setfilt(sc, addr, slot) 90139583Swpaul struct tl_softc *sc; 90241656Swpaul caddr_t addr; 90339583Swpaul int slot; 90439583Swpaul{ 90539583Swpaul int i; 90639583Swpaul u_int16_t regaddr; 90739583Swpaul 90839583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 90939583Swpaul 91039583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 91139583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 91239583Swpaul 91339583Swpaul return; 91439583Swpaul} 91539583Swpaul 91639583Swpaul/* 91739583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 91839583Swpaul * linked list. This is fine, except addresses are added from the head 91939583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 92039583Swpaul * group to always be in the perfect filter, but as more groups are added, 92139583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 92239583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 92339583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 92439583Swpaul * and into the hash table. 92539583Swpaul * 92639583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 92739583Swpaul * circular queue, we don't have the ability to just grab the tail of 92839583Swpaul * the list and traverse it backwards. Instead, we have to traverse 92939583Swpaul * the list once to find the tail, then traverse it again backwards to 93039583Swpaul * update the multicast filter. 93139583Swpaul */ 932102336Salfredstatic void 933102336Salfredtl_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 }; 941147256Sbrooks ifp = sc->tl_ifp; 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; 955195049Srwatson if_maddr_rlock(ifp); 95672084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 95736270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 95836270Swpaul continue; 95939583Swpaul /* 96039583Swpaul * Program the first three multicast groups 96139583Swpaul * into the perfect filter. For all others, 96239583Swpaul * use the hash table. 96339583Swpaul */ 96439583Swpaul if (i < 4) { 96539583Swpaul tl_setfilt(sc, 96639583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 96739583Swpaul i++; 96839583Swpaul continue; 96939583Swpaul } 97039583Swpaul 971122625Sobrien h = tl_mchash( 97236270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 97336270Swpaul if (h < 32) 97436270Swpaul hashes[0] |= (1 << h); 97536270Swpaul else 97636317Swpaul hashes[1] |= (1 << (h - 32)); 97736270Swpaul } 978195049Srwatson if_maddr_runlock(ifp); 97936270Swpaul } 98036270Swpaul 98139583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 98239583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 98336270Swpaul 98436270Swpaul return; 98536270Swpaul} 98636270Swpaul 98739583Swpaul/* 98839583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 98939583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99039583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 99139583Swpaul * experience this isn't necessary. 99239583Swpaul */ 993102336Salfredstatic void 994102336Salfredtl_hardreset(dev) 99550468Swpaul device_t dev; 99650468Swpaul{ 99739583Swpaul struct tl_softc *sc; 99839583Swpaul int i; 99950468Swpaul u_int16_t flags; 100039583Swpaul 100150468Swpaul sc = device_get_softc(dev); 100239583Swpaul 100350468Swpaul tl_mii_sync(sc); 100439583Swpaul 100550468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 100639583Swpaul 100750468Swpaul for (i = 0; i < MII_NPHY; i++) 100850468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 100939583Swpaul 101050468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 101139583Swpaul DELAY(50000); 101250468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 101339583Swpaul tl_mii_sync(sc); 101450468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 101539583Swpaul 101650468Swpaul DELAY(50000); 101739583Swpaul return; 101839583Swpaul} 101939583Swpaul 1020102336Salfredstatic void 1021102336Salfredtl_softreset(sc, internal) 102239583Swpaul struct tl_softc *sc; 102336270Swpaul int internal; 102436270Swpaul{ 102539583Swpaul u_int32_t cmd, dummy, i; 102636270Swpaul 102736270Swpaul /* Assert the adapter reset bit. */ 102839583Swpaul CMD_SET(sc, TL_CMD_ADRST); 102950468Swpaul 103036270Swpaul /* Turn off interrupts */ 103139583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 103236270Swpaul 103336270Swpaul /* First, clear the stats registers. */ 103439583Swpaul for (i = 0; i < 5; i++) 103539583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 103636270Swpaul 103736270Swpaul /* Clear Areg and Hash registers */ 103839583Swpaul for (i = 0; i < 8; i++) 103939583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104036270Swpaul 104136270Swpaul /* 104236270Swpaul * Set up Netconfig register. Enable one channel and 104336270Swpaul * one fragment mode. 104436270Swpaul */ 104539583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 104645155Swpaul if (internal && !sc->tl_bitrate) { 104739583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 104836270Swpaul } else { 104939583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105036270Swpaul } 105136270Swpaul 105245155Swpaul /* Handle cards with bitrate devices. */ 105345155Swpaul if (sc->tl_bitrate) 105445155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 105545155Swpaul 105636270Swpaul /* 105736270Swpaul * Load adapter irq pacing timer and tx threshold. 105836270Swpaul * We make the transmit threshold 1 initially but we may 105936270Swpaul * change that later. 106036270Swpaul */ 106139583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 106236270Swpaul cmd |= TL_CMD_NES; 106336270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 106439583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 106539583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 106636270Swpaul 106736270Swpaul /* Unreset the MII */ 106839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 106936270Swpaul 107036270Swpaul /* Take the adapter out of reset */ 107139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 107236270Swpaul 107336270Swpaul /* Wait for things to settle down a little. */ 107436270Swpaul DELAY(500); 107536270Swpaul 107636270Swpaul return; 107736270Swpaul} 107836270Swpaul 107936270Swpaul/* 108036270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 108139583Swpaul * against our list and return its name if we find a match. 108236270Swpaul */ 1083102336Salfredstatic int 1084102336Salfredtl_probe(dev) 108548992Swpaul device_t dev; 108636270Swpaul{ 108736270Swpaul struct tl_type *t; 108836270Swpaul 108936270Swpaul t = tl_devs; 109036270Swpaul 109136270Swpaul while(t->tl_name != NULL) { 109248992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 109348992Swpaul (pci_get_device(dev) == t->tl_did)) { 109448992Swpaul device_set_desc(dev, t->tl_name); 1095142398Simp return (BUS_PROBE_DEFAULT); 109648992Swpaul } 109736270Swpaul t++; 109836270Swpaul } 109936270Swpaul 110048992Swpaul return(ENXIO); 110136270Swpaul} 110236270Swpaul 1103102336Salfredstatic int 1104102336Salfredtl_attach(dev) 110548992Swpaul device_t dev; 110636270Swpaul{ 110739583Swpaul u_int16_t did, vid; 110839583Swpaul struct tl_type *t; 110939583Swpaul struct ifnet *ifp; 111039583Swpaul struct tl_softc *sc; 1111214264Smarius int error, flags, i, rid, unit; 1112147256Sbrooks u_char eaddr[6]; 111336270Swpaul 111448992Swpaul vid = pci_get_vendor(dev); 111548992Swpaul did = pci_get_device(dev); 111648992Swpaul sc = device_get_softc(dev); 1117162315Sglebius sc->tl_dev = dev; 111848992Swpaul unit = device_get_unit(dev); 111939583Swpaul 112039583Swpaul t = tl_devs; 112139583Swpaul while(t->tl_name != NULL) { 112239583Swpaul if (vid == t->tl_vid && did == t->tl_did) 112336270Swpaul break; 112439583Swpaul t++; 112539583Swpaul } 112636270Swpaul 112739583Swpaul if (t->tl_name == NULL) { 1128105599Sbrooks device_printf(dev, "unknown device!?\n"); 1129112878Sjhb return (ENXIO); 113036270Swpaul } 113136270Swpaul 113293818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 1133150171Sjhb MTX_DEF); 113469583Swpaul 113536270Swpaul /* 113636270Swpaul * Map control/status registers. 113736270Swpaul */ 113872813Swpaul pci_enable_busmaster(dev); 113936270Swpaul 114039583Swpaul#ifdef TL_USEIOSPACE 114139583Swpaul 114248992Swpaul rid = TL_PCI_LOIO; 1143127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1144127135Snjl RF_ACTIVE); 114548992Swpaul 114648992Swpaul /* 114748992Swpaul * Some cards have the I/O and memory mapped address registers 114848992Swpaul * reversed. Try both combinations before giving up. 114948992Swpaul */ 115048992Swpaul if (sc->tl_res == NULL) { 115148992Swpaul rid = TL_PCI_LOMEM; 1152127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1153127135Snjl RF_ACTIVE); 115445155Swpaul } 115539583Swpaul#else 115648992Swpaul rid = TL_PCI_LOMEM; 1157127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1158127135Snjl RF_ACTIVE); 115948992Swpaul if (sc->tl_res == NULL) { 116048992Swpaul rid = TL_PCI_LOIO; 1161127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1162127135Snjl RF_ACTIVE); 116336270Swpaul } 116439583Swpaul#endif 116536270Swpaul 116648992Swpaul if (sc->tl_res == NULL) { 1167105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 116848992Swpaul error = ENXIO; 116948992Swpaul goto fail; 117048992Swpaul } 117148992Swpaul 117239583Swpaul#ifdef notdef 117339583Swpaul /* 117439583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 117539583Swpaul * timer all the way up to its maximum value. I'm not sure 117639583Swpaul * if this is really necessary, but what the manual wants, 117739583Swpaul * the manual gets. 117839583Swpaul */ 117948992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 118039583Swpaul command |= 0x0000FF00; 118148992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 118239583Swpaul#endif 118336270Swpaul 118436270Swpaul /* Allocate interrupt */ 118548992Swpaul rid = 0; 1186127135Snjl sc->tl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 118748992Swpaul RF_SHAREABLE | RF_ACTIVE); 118848992Swpaul 118948992Swpaul if (sc->tl_irq == NULL) { 1190105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 119148992Swpaul error = ENXIO; 119236270Swpaul goto fail; 119336270Swpaul } 119436270Swpaul 119536270Swpaul /* 119651439Swpaul * Now allocate memory for the TX and RX lists. 119736270Swpaul */ 119851439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 119951657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 120039583Swpaul 120151439Swpaul if (sc->tl_ldata == NULL) { 1202105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 120348992Swpaul error = ENXIO; 120436270Swpaul goto fail; 120536270Swpaul } 120636270Swpaul 120739583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 120839583Swpaul 1209214264Smarius if (vid == COMPAQ_VENDORID || vid == TI_VENDORID) 121039583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 1211214264Smarius if (vid == OLICOM_VENDORID) 121239583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 121339583Swpaul 121439583Swpaul /* Reset the adapter. */ 121539583Swpaul tl_softreset(sc, 1); 121650468Swpaul tl_hardreset(dev); 121739583Swpaul tl_softreset(sc, 1); 121839583Swpaul 121938030Swpaul /* 122039583Swpaul * Get station address from the EEPROM. 122139583Swpaul */ 1222147256Sbrooks if (tl_read_eeprom(sc, eaddr, sc->tl_eeaddr, ETHER_ADDR_LEN)) { 1223105599Sbrooks device_printf(dev, "failed to read station address\n"); 122448992Swpaul error = ENXIO; 122539583Swpaul goto fail; 122639583Swpaul } 122739583Swpaul 122839583Swpaul /* 122939583Swpaul * XXX Olicom, in its desire to be different from the 123039583Swpaul * rest of the world, has done strange things with the 123139583Swpaul * encoding of the station address in the EEPROM. First 123239583Swpaul * of all, they store the address at offset 0xF8 rather 123339583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 123439583Swpaul * Second, they store the address in three 16-bit words in 123539583Swpaul * network byte order, as opposed to storing it sequentially 123639583Swpaul * like all the other ThunderLAN cards. In order to get 123739583Swpaul * the station address in a form that matches what the Olicom 123839583Swpaul * diagnostic utility specifies, we have to byte-swap each 123939583Swpaul * word. To make things even more confusing, neither 00:00:28 124039583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 124139583Swpaul */ 1242214264Smarius if (vid == OLICOM_VENDORID) { 124339583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 124439583Swpaul u_int16_t *p; 1245147256Sbrooks p = (u_int16_t *)&eaddr[i]; 124639583Swpaul *p = ntohs(*p); 124739583Swpaul } 124839583Swpaul } 124939583Swpaul 1250147256Sbrooks ifp = sc->tl_ifp = if_alloc(IFT_ETHER); 1251147256Sbrooks if (ifp == NULL) { 1252147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 1253147256Sbrooks error = ENOSPC; 1254147256Sbrooks goto fail; 1255147256Sbrooks } 125639583Swpaul ifp->if_softc = sc; 1257121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1258150171Sjhb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 125939583Swpaul ifp->if_ioctl = tl_ioctl; 126039583Swpaul ifp->if_start = tl_start; 126139583Swpaul ifp->if_init = tl_init; 126239583Swpaul ifp->if_mtu = ETHERMTU; 126351439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 1264169414Syar ifp->if_capabilities |= IFCAP_VLAN_MTU; 1265169414Syar ifp->if_capenable |= IFCAP_VLAN_MTU; 1266150171Sjhb callout_init_mtx(&sc->tl_stat_callout, &sc->tl_mtx, 0); 126739583Swpaul 126839583Swpaul /* Reset the adapter again. */ 126939583Swpaul tl_softreset(sc, 1); 127050468Swpaul tl_hardreset(dev); 127139583Swpaul tl_softreset(sc, 1); 127239583Swpaul 127336270Swpaul /* 127450462Swpaul * Do MII setup. If no PHYs are found, then this is a 127550462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 127650462Swpaul * and AUI/BNC. 1277213894Smarius * XXX mii_attach() can fail for reason different than 1278213894Smarius * no PHYs found! 127936270Swpaul */ 1280214264Smarius flags = 0; 1281214264Smarius if (vid == COMPAQ_VENDORID) { 1282214264Smarius if (did == COMPAQ_DEVICEID_NETEL_10_100_PROLIANT || 1283214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED || 1284214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P_BNC || 1285214264Smarius did == COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX) 1286214264Smarius flags |= MIIF_MACPRIV0; 1287214264Smarius if (did == COMPAQ_DEVICEID_NETEL_10 || 1288214264Smarius did == COMPAQ_DEVICEID_NETEL_10_100_DUAL || 1289214264Smarius did == COMPAQ_DEVICEID_NETFLEX_3P || 1290214264Smarius did == COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED) 1291214264Smarius flags |= MIIF_MACPRIV1; 1292214264Smarius } else if (vid == OLICOM_VENDORID && did == OLICOM_DEVICEID_OC2183) 1293214264Smarius flags |= MIIF_MACPRIV0 | MIIF_MACPRIV1; 1294213894Smarius if (mii_attach(dev, &sc->tl_miibus, ifp, tl_ifmedia_upd, 1295213894Smarius tl_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0)) { 129645155Swpaul struct ifmedia *ifm; 129745155Swpaul sc->tl_bitrate = 1; 129845155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 129945155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 130045155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 130145155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 130245155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 130345166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 130445155Swpaul /* Reset again, this time setting bitrate mode. */ 130545155Swpaul tl_softreset(sc, 1); 130645155Swpaul ifm = &sc->ifmedia; 130745155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 130845155Swpaul tl_ifmedia_upd(ifp); 130936270Swpaul } 131036270Swpaul 131139583Swpaul /* 131263090Sarchie * Call MI attach routine. 131339583Swpaul */ 1314147256Sbrooks ether_ifattach(ifp, eaddr); 131538030Swpaul 1316113609Snjl /* Hook interrupt last to avoid having to lock softc */ 1317150171Sjhb error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET | INTR_MPSAFE, 1318166901Spiso NULL, tl_intr, sc, &sc->tl_intrhand); 1319112872Snjl 1320112872Snjl if (error) { 1321112872Snjl device_printf(dev, "couldn't set up irq\n"); 1322113609Snjl ether_ifdetach(ifp); 1323112872Snjl goto fail; 1324112872Snjl } 1325112872Snjl 132636270Swpaulfail: 1327112872Snjl if (error) 1328112872Snjl tl_detach(dev); 1329112872Snjl 133048992Swpaul return(error); 133136270Swpaul} 133236270Swpaul 1333113609Snjl/* 1334113609Snjl * Shutdown hardware and free up resources. This can be called any 1335113609Snjl * time after the mutex has been initialized. It is called in both 1336113609Snjl * the error case in attach and the normal detach case so it needs 1337113609Snjl * to be careful about only freeing resources that have actually been 1338113609Snjl * allocated. 1339113609Snjl */ 1340102336Salfredstatic int 1341102336Salfredtl_detach(dev) 134248992Swpaul device_t dev; 134348992Swpaul{ 134448992Swpaul struct tl_softc *sc; 134548992Swpaul struct ifnet *ifp; 134648992Swpaul 134748992Swpaul sc = device_get_softc(dev); 1348112880Sjhb KASSERT(mtx_initialized(&sc->tl_mtx), ("tl mutex not initialized")); 1349147256Sbrooks ifp = sc->tl_ifp; 135048992Swpaul 1351113609Snjl /* These should only be active if attach succeeded */ 1352113812Simp if (device_is_attached(dev)) { 1353199560Sjhb ether_ifdetach(ifp); 1354150171Sjhb TL_LOCK(sc); 1355113609Snjl tl_stop(sc); 1356150171Sjhb TL_UNLOCK(sc); 1357150171Sjhb callout_drain(&sc->tl_stat_callout); 1358150213Sru } 1359113609Snjl if (sc->tl_miibus) 1360112872Snjl device_delete_child(dev, sc->tl_miibus); 1361113609Snjl bus_generic_detach(dev); 136248992Swpaul 1363112872Snjl if (sc->tl_ldata) 1364112872Snjl contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 136550462Swpaul if (sc->tl_bitrate) 136650462Swpaul ifmedia_removeall(&sc->ifmedia); 136748992Swpaul 1368112872Snjl if (sc->tl_intrhand) 1369112872Snjl bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 1370112872Snjl if (sc->tl_irq) 1371112872Snjl bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 1372112872Snjl if (sc->tl_res) 1373112872Snjl bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 137448992Swpaul 1375151297Sru if (ifp) 1376151297Sru if_free(ifp); 1377151297Sru 137867087Swpaul mtx_destroy(&sc->tl_mtx); 137948992Swpaul 138048992Swpaul return(0); 138148992Swpaul} 138248992Swpaul 138336270Swpaul/* 138436270Swpaul * Initialize the transmit lists. 138536270Swpaul */ 1386102336Salfredstatic int 1387102336Salfredtl_list_tx_init(sc) 138836270Swpaul struct tl_softc *sc; 138936270Swpaul{ 139036270Swpaul struct tl_chain_data *cd; 139136270Swpaul struct tl_list_data *ld; 139236270Swpaul int i; 139336270Swpaul 139436270Swpaul cd = &sc->tl_cdata; 139536270Swpaul ld = sc->tl_ldata; 139636270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 139736270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 139836270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 139936270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 140036270Swpaul else 140136270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 140236270Swpaul } 140336270Swpaul 140436270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 140536270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 140636270Swpaul sc->tl_txeoc = 1; 140736270Swpaul 140836270Swpaul return(0); 140936270Swpaul} 141036270Swpaul 141136270Swpaul/* 141236270Swpaul * Initialize the RX lists and allocate mbufs for them. 141336270Swpaul */ 1414102336Salfredstatic int 1415102336Salfredtl_list_rx_init(sc) 141636270Swpaul struct tl_softc *sc; 141736270Swpaul{ 141836270Swpaul struct tl_chain_data *cd; 141936270Swpaul struct tl_list_data *ld; 142036270Swpaul int i; 142136270Swpaul 142236270Swpaul cd = &sc->tl_cdata; 142336270Swpaul ld = sc->tl_ldata; 142436270Swpaul 142540795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 142636270Swpaul cd->tl_rx_chain[i].tl_ptr = 142737626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 142839583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 142939583Swpaul return(ENOBUFS); 143040795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 143136270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 143236270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 143336270Swpaul } else { 143436270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 143536270Swpaul ld->tl_rx_list[i].tlist_fptr = 143636270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 143736270Swpaul } 143836270Swpaul } 143936270Swpaul 144036270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 144136270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 144236270Swpaul 144336270Swpaul return(0); 144436270Swpaul} 144536270Swpaul 1446102336Salfredstatic int 1447102336Salfredtl_newbuf(sc, c) 144836270Swpaul struct tl_softc *sc; 144937626Swpaul struct tl_chain_onefrag *c; 145036270Swpaul{ 145136270Swpaul struct mbuf *m_new = NULL; 145236270Swpaul 1453150171Sjhb m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 145487846Sluigi if (m_new == NULL) 145536270Swpaul return(ENOBUFS); 145636270Swpaul 145736270Swpaul c->tl_mbuf = m_new; 145836270Swpaul c->tl_next = NULL; 145936270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 146036270Swpaul c->tl_ptr->tlist_fptr = 0; 146137626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 146237626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 146356060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 146436270Swpaul 146536270Swpaul return(0); 146636270Swpaul} 146736270Swpaul/* 146836270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 146936270Swpaul * tells us that a full ethernet frame has been captured and we need 147036270Swpaul * to handle it. 147136270Swpaul * 147236270Swpaul * Reception is done using 'lists' which consist of a header and a 147336270Swpaul * series of 10 data count/data address pairs that point to buffers. 147436270Swpaul * Initially you're supposed to create a list, populate it with pointers 147536270Swpaul * to buffers, then load the physical address of the list into the 147636270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 147736270Swpaul * frame into the buffers for you. 147836270Swpaul * 147936270Swpaul * To make things as fast as possible, we have the chip DMA directly 148036270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 148136270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 148236270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 148336270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 148436270Swpaul * the chip, it will always think it has an endless receive channel. 148536270Swpaul * 148636270Swpaul * If we happen to fall behind and the chip manages to fill up all of 148736270Swpaul * the buffers, it will generate an end of channel interrupt and wait 148836270Swpaul * for us to empty the chain and restart the receiver. 148936270Swpaul */ 1490102336Salfredstatic int 1491102336Salfredtl_intvec_rxeof(xsc, type) 149236270Swpaul void *xsc; 149336270Swpaul u_int32_t type; 149436270Swpaul{ 149536270Swpaul struct tl_softc *sc; 149636270Swpaul int r = 0, total_len = 0; 149736270Swpaul struct ether_header *eh; 149836270Swpaul struct mbuf *m; 149936270Swpaul struct ifnet *ifp; 150037626Swpaul struct tl_chain_onefrag *cur_rx; 150136270Swpaul 150236270Swpaul sc = xsc; 1503147256Sbrooks ifp = sc->tl_ifp; 150436270Swpaul 1505122689Ssam TL_LOCK_ASSERT(sc); 1506122689Ssam 150756060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 150856060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 150956060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 151056060Swpaul break; 151136270Swpaul r++; 151236270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 151336270Swpaul m = cur_rx->tl_mbuf; 151436270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 151536270Swpaul 151639583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 151739583Swpaul ifp->if_ierrors++; 151839583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 151939583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 152039583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 152139583Swpaul continue; 152239583Swpaul } 152336270Swpaul 152436270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 152536270Swpaul vtophys(cur_rx->tl_ptr); 152636270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 152736270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 152836270Swpaul 152937626Swpaul /* 153037626Swpaul * Note: when the ThunderLAN chip is in 'capture all 153137626Swpaul * frames' mode, it will receive its own transmissions. 153237626Swpaul * We drop don't need to process our own transmissions, 153337626Swpaul * so we drop them here and continue. 153437626Swpaul */ 1535106936Ssam eh = mtod(m, struct ether_header *); 153639583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 1537152315Sru if (!bcmp(eh->ether_shost, IF_LLADDR(sc->tl_ifp), 153837626Swpaul ETHER_ADDR_LEN)) { 153937626Swpaul m_freem(m); 154037626Swpaul continue; 154137626Swpaul } 154237626Swpaul 1543106936Ssam m->m_pkthdr.rcvif = ifp; 1544106936Ssam m->m_pkthdr.len = m->m_len = total_len; 1545106936Ssam 1546122689Ssam TL_UNLOCK(sc); 1547106936Ssam (*ifp->if_input)(ifp, m); 1548122689Ssam TL_LOCK(sc); 154936270Swpaul } 155036270Swpaul 155136270Swpaul return(r); 155236270Swpaul} 155336270Swpaul 155436270Swpaul/* 155536270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 155636270Swpaul * initialized or the adapter reached a list with a forward pointer 155736270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 155836270Swpaul * the card has hit the end of the receive buffer chain and we need to 155936270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 156036270Swpaul */ 1561102336Salfredstatic int 1562102336Salfredtl_intvec_rxeoc(xsc, type) 156336270Swpaul void *xsc; 156436270Swpaul u_int32_t type; 156536270Swpaul{ 156636270Swpaul struct tl_softc *sc; 156736270Swpaul int r; 156856060Swpaul struct tl_chain_data *cd; 156936270Swpaul 157056060Swpaul 157136270Swpaul sc = xsc; 157256060Swpaul cd = &sc->tl_cdata; 157336270Swpaul 157436270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 157536270Swpaul r = tl_intvec_rxeof(xsc, type); 157639583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 157736270Swpaul r = 1; 157856060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 157956060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 158039583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 158136270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 158236270Swpaul return(r); 158336270Swpaul} 158436270Swpaul 1585102336Salfredstatic int 1586102336Salfredtl_intvec_txeof(xsc, type) 158736270Swpaul void *xsc; 158836270Swpaul u_int32_t type; 158936270Swpaul{ 159036270Swpaul struct tl_softc *sc; 159136270Swpaul int r = 0; 159236270Swpaul struct tl_chain *cur_tx; 159336270Swpaul 159436270Swpaul sc = xsc; 159536270Swpaul 159636270Swpaul /* 159736270Swpaul * Go through our tx list and free mbufs for those 159836270Swpaul * frames that have been sent. 159936270Swpaul */ 160036270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 160136270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 160236270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 160336270Swpaul break; 160436270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 160536270Swpaul 160636270Swpaul r++; 160736270Swpaul m_freem(cur_tx->tl_mbuf); 160836270Swpaul cur_tx->tl_mbuf = NULL; 160936270Swpaul 161036270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 161136270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 161237626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 161337626Swpaul break; 161436270Swpaul } 161536270Swpaul 161636270Swpaul return(r); 161736270Swpaul} 161836270Swpaul 161936270Swpaul/* 162036270Swpaul * The transmit end of channel interrupt. The adapter triggers this 162136270Swpaul * interrupt to tell us it hit the end of the current transmit list. 162236270Swpaul * 162336270Swpaul * A note about this: it's possible for a condition to arise where 162436270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 162536270Swpaul * You have to avoid this since the chip expects things to go in a 162636270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 162736270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 162836270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 162936270Swpaul * interrupt should be received and acknowledged before any more frames 163036270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 163136270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 163236270Swpaul * it could attempt to issue a transmit command prematurely. 163336270Swpaul * 163436270Swpaul * To guard against this, tl_start() will only issue transmit commands 163536270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 163636270Swpaul * can set this flag once tl_start() has cleared it. 163736270Swpaul */ 1638102336Salfredstatic int 1639102336Salfredtl_intvec_txeoc(xsc, type) 164036270Swpaul void *xsc; 164136270Swpaul u_int32_t type; 164236270Swpaul{ 164336270Swpaul struct tl_softc *sc; 164436270Swpaul struct ifnet *ifp; 164536270Swpaul u_int32_t cmd; 164636270Swpaul 164736270Swpaul sc = xsc; 1648147256Sbrooks ifp = sc->tl_ifp; 164936270Swpaul 165036270Swpaul /* Clear the timeout timer. */ 1651199560Sjhb sc->tl_timer = 0; 165236270Swpaul 165336270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 1654148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 165536270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 165636270Swpaul sc->tl_txeoc = 1; 165736270Swpaul } else { 165836270Swpaul sc->tl_txeoc = 0; 165936270Swpaul /* First we have to ack the EOC interrupt. */ 166039583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 166136270Swpaul /* Then load the address of the next TX list. */ 166239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 166351439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 166436270Swpaul /* Restart TX channel. */ 166539583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 166636270Swpaul cmd &= ~TL_CMD_RT; 166736270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 166839583Swpaul CMD_PUT(sc, cmd); 166936270Swpaul return(0); 167036270Swpaul } 167136270Swpaul 167236270Swpaul return(1); 167336270Swpaul} 167436270Swpaul 1675102336Salfredstatic int 1676102336Salfredtl_intvec_adchk(xsc, type) 167736270Swpaul void *xsc; 167836270Swpaul u_int32_t type; 167936270Swpaul{ 168036270Swpaul struct tl_softc *sc; 168136270Swpaul 168236270Swpaul sc = xsc; 168336270Swpaul 168439627Swpaul if (type) 1685162315Sglebius device_printf(sc->tl_dev, "adapter check: %x\n", 168641656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 168736270Swpaul 168839583Swpaul tl_softreset(sc, 1); 168937626Swpaul tl_stop(sc); 1690150171Sjhb tl_init_locked(sc); 169139583Swpaul CMD_SET(sc, TL_CMD_INTSON); 169236270Swpaul 169336270Swpaul return(0); 169436270Swpaul} 169536270Swpaul 1696102336Salfredstatic int 1697102336Salfredtl_intvec_netsts(xsc, type) 169836270Swpaul void *xsc; 169936270Swpaul u_int32_t type; 170036270Swpaul{ 170136270Swpaul struct tl_softc *sc; 170236270Swpaul u_int16_t netsts; 170336270Swpaul 170436270Swpaul sc = xsc; 170536270Swpaul 170639583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 170739583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 170836270Swpaul 1709162315Sglebius device_printf(sc->tl_dev, "network status: %x\n", netsts); 171036270Swpaul 171136270Swpaul return(1); 171236270Swpaul} 171336270Swpaul 1714102336Salfredstatic void 1715102336Salfredtl_intr(xsc) 171639583Swpaul void *xsc; 171736270Swpaul{ 171836270Swpaul struct tl_softc *sc; 171936270Swpaul struct ifnet *ifp; 172036270Swpaul int r = 0; 172136270Swpaul u_int32_t type = 0; 172236270Swpaul u_int16_t ints = 0; 172336270Swpaul u_int8_t ivec = 0; 172436270Swpaul 172539583Swpaul sc = xsc; 172667087Swpaul TL_LOCK(sc); 172736270Swpaul 172836270Swpaul /* Disable interrupts */ 172939583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 173039583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 173136270Swpaul type = (ints << 16) & 0xFFFF0000; 173236270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 173336270Swpaul ints = (ints & TL_INT_MASK) >> 2; 173436270Swpaul 1735147256Sbrooks ifp = sc->tl_ifp; 173636270Swpaul 173736270Swpaul switch(ints) { 173836270Swpaul case (TL_INTR_INVALID): 173939583Swpaul#ifdef DIAGNOSTIC 1740162315Sglebius device_printf(sc->tl_dev, "got an invalid interrupt!\n"); 174139583Swpaul#endif 174239583Swpaul /* Re-enable interrupts but don't ack this one. */ 174339583Swpaul CMD_PUT(sc, type); 174439583Swpaul r = 0; 174536270Swpaul break; 174636270Swpaul case (TL_INTR_TXEOF): 174736270Swpaul r = tl_intvec_txeof((void *)sc, type); 174836270Swpaul break; 174936270Swpaul case (TL_INTR_TXEOC): 175036270Swpaul r = tl_intvec_txeoc((void *)sc, type); 175136270Swpaul break; 175236270Swpaul case (TL_INTR_STATOFLOW): 175339583Swpaul tl_stats_update(sc); 175439583Swpaul r = 1; 175536270Swpaul break; 175636270Swpaul case (TL_INTR_RXEOF): 175736270Swpaul r = tl_intvec_rxeof((void *)sc, type); 175836270Swpaul break; 175936270Swpaul case (TL_INTR_DUMMY): 1760162315Sglebius device_printf(sc->tl_dev, "got a dummy interrupt\n"); 176139583Swpaul r = 1; 176236270Swpaul break; 176336270Swpaul case (TL_INTR_ADCHK): 176436270Swpaul if (ivec) 176536270Swpaul r = tl_intvec_adchk((void *)sc, type); 176636270Swpaul else 176736270Swpaul r = tl_intvec_netsts((void *)sc, type); 176836270Swpaul break; 176936270Swpaul case (TL_INTR_RXEOC): 177036270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 177136270Swpaul break; 177236270Swpaul default: 1773162315Sglebius device_printf(sc->tl_dev, "bogus interrupt type\n"); 177436270Swpaul break; 177536270Swpaul } 177636270Swpaul 177736270Swpaul /* Re-enable interrupts */ 177837626Swpaul if (r) { 177939583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 178037626Swpaul } 178136270Swpaul 178237626Swpaul if (ifp->if_snd.ifq_head != NULL) 1783150171Sjhb tl_start_locked(ifp); 178437626Swpaul 178567087Swpaul TL_UNLOCK(sc); 178667087Swpaul 178736270Swpaul return; 178836270Swpaul} 178936270Swpaul 1790102336Salfredstatic void 1791102336Salfredtl_stats_update(xsc) 179236270Swpaul void *xsc; 179336270Swpaul{ 179436270Swpaul struct tl_softc *sc; 179536270Swpaul struct ifnet *ifp; 179636270Swpaul struct tl_stats tl_stats; 179750462Swpaul struct mii_data *mii; 179836270Swpaul u_int32_t *p; 179936270Swpaul 180036270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 180136270Swpaul 180236270Swpaul sc = xsc; 1803150171Sjhb TL_LOCK_ASSERT(sc); 1804147256Sbrooks ifp = sc->tl_ifp; 180536270Swpaul 180636270Swpaul p = (u_int32_t *)&tl_stats; 180736270Swpaul 180839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 180939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181039583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181139583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181239583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181339583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181436270Swpaul 181536270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 181636270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 181736270Swpaul tl_stats.tl_tx_multi_collision; 181836270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 181936270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 182036270Swpaul tl_rx_overrun(tl_stats); 182136270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 182236270Swpaul 182351439Swpaul if (tl_tx_underrun(tl_stats)) { 182451439Swpaul u_int8_t tx_thresh; 182551439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 182651439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 182751439Swpaul tx_thresh >>= 4; 182851439Swpaul tx_thresh++; 1829162315Sglebius device_printf(sc->tl_dev, "tx underrun -- increasing " 1830105599Sbrooks "tx threshold to %d bytes\n", 183151439Swpaul (64 * (tx_thresh * 4))); 183251439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 183351439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 183451439Swpaul } 183551439Swpaul } 183651439Swpaul 1837199560Sjhb if (sc->tl_timer > 0 && --sc->tl_timer == 0) 1838199560Sjhb tl_watchdog(sc); 1839199560Sjhb 1840150171Sjhb callout_reset(&sc->tl_stat_callout, hz, tl_stats_update, sc); 184136302Swpaul 184250462Swpaul if (!sc->tl_bitrate) { 184350462Swpaul mii = device_get_softc(sc->tl_miibus); 184450462Swpaul mii_tick(mii); 184550462Swpaul } 184650462Swpaul 184736302Swpaul return; 184836270Swpaul} 184936270Swpaul 185036270Swpaul/* 185136270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 185236270Swpaul * pointers to the fragment pointers. 185336270Swpaul */ 1854102336Salfredstatic int 1855102336Salfredtl_encap(sc, c, m_head) 185636270Swpaul struct tl_softc *sc; 185736270Swpaul struct tl_chain *c; 185836270Swpaul struct mbuf *m_head; 185936270Swpaul{ 186036270Swpaul int frag = 0; 186136270Swpaul struct tl_frag *f = NULL; 186236270Swpaul int total_len; 186336270Swpaul struct mbuf *m; 1864147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 186536270Swpaul 186636270Swpaul /* 186736270Swpaul * Start packing the mbufs in this chain into 186836270Swpaul * the fragment pointers. Stop when we run out 186936270Swpaul * of fragments or hit the end of the mbuf chain. 187036270Swpaul */ 187136270Swpaul m = m_head; 187236270Swpaul total_len = 0; 187336270Swpaul 187436270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 187536270Swpaul if (m->m_len != 0) { 187636270Swpaul if (frag == TL_MAXFRAGS) 187736270Swpaul break; 187836270Swpaul total_len+= m->m_len; 187936270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 188036270Swpaul vtophys(mtod(m, vm_offset_t)); 188136270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 188236270Swpaul frag++; 188336270Swpaul } 188436270Swpaul } 188536270Swpaul 188636270Swpaul /* 188736270Swpaul * Handle special cases. 188836270Swpaul * Special case #1: we used up all 10 fragments, but 188936270Swpaul * we have more mbufs left in the chain. Copy the 189036270Swpaul * data into an mbuf cluster. Note that we don't 189136270Swpaul * bother clearing the values in the other fragment 189236270Swpaul * pointers/counters; it wouldn't gain us anything, 189336270Swpaul * and would waste cycles. 189436270Swpaul */ 189536270Swpaul if (m != NULL) { 189636270Swpaul struct mbuf *m_new = NULL; 189736270Swpaul 1898111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 189936270Swpaul if (m_new == NULL) { 1900105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 190136270Swpaul return(1); 190236270Swpaul } 190336270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 1904111119Simp MCLGET(m_new, M_DONTWAIT); 190536270Swpaul if (!(m_new->m_flags & M_EXT)) { 190636270Swpaul m_freem(m_new); 1907105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 190836270Swpaul return(1); 190936270Swpaul } 191036270Swpaul } 191136270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 191236270Swpaul mtod(m_new, caddr_t)); 191336270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 191436270Swpaul m_freem(m_head); 191536270Swpaul m_head = m_new; 191636270Swpaul f = &c->tl_ptr->tl_frag[0]; 191736270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 191836270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 191936270Swpaul frag = 1; 192036270Swpaul } 192136270Swpaul 192236270Swpaul /* 192336270Swpaul * Special case #2: the frame is smaller than the minimum 192436270Swpaul * frame size. We have to pad it to make the chip happy. 192536270Swpaul */ 192636270Swpaul if (total_len < TL_MIN_FRAMELEN) { 192736270Swpaul if (frag == TL_MAXFRAGS) 1928105599Sbrooks if_printf(ifp, 1929105599Sbrooks "all frags filled but frame still to small!\n"); 193036270Swpaul f = &c->tl_ptr->tl_frag[frag]; 193136270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 193236270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 193336270Swpaul total_len += f->tlist_dcnt; 193436270Swpaul frag++; 193536270Swpaul } 193636270Swpaul 193736270Swpaul c->tl_mbuf = m_head; 193836270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 193936270Swpaul c->tl_ptr->tlist_frsize = total_len; 194036270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 194136270Swpaul c->tl_ptr->tlist_fptr = 0; 194236270Swpaul 194336270Swpaul return(0); 194436270Swpaul} 194536270Swpaul 194636270Swpaul/* 194736270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 194836270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 194936270Swpaul * copy of the pointers since the transmit list fragment pointers are 195036270Swpaul * physical addresses. 195136270Swpaul */ 1952102336Salfredstatic void 1953102336Salfredtl_start(ifp) 195436270Swpaul struct ifnet *ifp; 195536270Swpaul{ 195636270Swpaul struct tl_softc *sc; 1957150171Sjhb 1958150171Sjhb sc = ifp->if_softc; 1959150171Sjhb TL_LOCK(sc); 1960150171Sjhb tl_start_locked(ifp); 1961150171Sjhb TL_UNLOCK(sc); 1962150171Sjhb} 1963150171Sjhb 1964150171Sjhbstatic void 1965150171Sjhbtl_start_locked(ifp) 1966150171Sjhb struct ifnet *ifp; 1967150171Sjhb{ 1968150171Sjhb struct tl_softc *sc; 196936270Swpaul struct mbuf *m_head = NULL; 197036270Swpaul u_int32_t cmd; 197136270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 197236270Swpaul 197336270Swpaul sc = ifp->if_softc; 1974150171Sjhb TL_LOCK_ASSERT(sc); 197536270Swpaul 197636270Swpaul /* 197736270Swpaul * Check for an available queue slot. If there are none, 197836270Swpaul * punt. 197936270Swpaul */ 198036270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 1981148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 198236270Swpaul return; 198336270Swpaul } 198436270Swpaul 198536270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 198636270Swpaul 198736270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 198836270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 198936270Swpaul if (m_head == NULL) 199036270Swpaul break; 199136270Swpaul 199236270Swpaul /* Pick a chain member off the free list. */ 199336270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 199436270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 199536270Swpaul 199636270Swpaul cur_tx->tl_next = NULL; 199736270Swpaul 199836270Swpaul /* Pack the data into the list. */ 199936270Swpaul tl_encap(sc, cur_tx, m_head); 200036270Swpaul 200136270Swpaul /* Chain it together */ 200236270Swpaul if (prev != NULL) { 200336270Swpaul prev->tl_next = cur_tx; 200436270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 200536270Swpaul } 200636270Swpaul prev = cur_tx; 200736270Swpaul 200836270Swpaul /* 200936270Swpaul * If there's a BPF listener, bounce a copy of this frame 201036270Swpaul * to him. 201136270Swpaul */ 2012106936Ssam BPF_MTAP(ifp, cur_tx->tl_mbuf); 201336270Swpaul } 201436270Swpaul 201536270Swpaul /* 201641526Swpaul * If there are no packets queued, bail. 201741526Swpaul */ 2018150171Sjhb if (cur_tx == NULL) 201941526Swpaul return; 202041526Swpaul 202141526Swpaul /* 202236270Swpaul * That's all we can stands, we can't stands no more. 202336270Swpaul * If there are no other transfers pending, then issue the 202436270Swpaul * TX GO command to the adapter to start things moving. 202536270Swpaul * Otherwise, just leave the data in the queue and let 202636270Swpaul * the EOF/EOC interrupt handler send. 202736270Swpaul */ 202836270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 202936270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 203036270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 203139583Swpaul 203236270Swpaul if (sc->tl_txeoc) { 203336270Swpaul sc->tl_txeoc = 0; 203439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 203539583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 203636270Swpaul cmd &= ~TL_CMD_RT; 203736270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 203839583Swpaul CMD_PUT(sc, cmd); 203936270Swpaul } 204036270Swpaul } else { 204136270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 204242146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 204336270Swpaul } 204436270Swpaul 204536270Swpaul /* 204636270Swpaul * Set a timeout in case the chip goes out to lunch. 204736270Swpaul */ 2048199560Sjhb sc->tl_timer = 5; 204936270Swpaul 205036270Swpaul return; 205136270Swpaul} 205236270Swpaul 2053102336Salfredstatic void 2054102336Salfredtl_init(xsc) 205536270Swpaul void *xsc; 205636270Swpaul{ 205736270Swpaul struct tl_softc *sc = xsc; 2058150171Sjhb 2059150171Sjhb TL_LOCK(sc); 2060150171Sjhb tl_init_locked(sc); 2061150171Sjhb TL_UNLOCK(sc); 2062150171Sjhb} 2063150171Sjhb 2064150171Sjhbstatic void 2065150171Sjhbtl_init_locked(sc) 2066150171Sjhb struct tl_softc *sc; 2067150171Sjhb{ 2068147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 206950462Swpaul struct mii_data *mii; 207036270Swpaul 2071150171Sjhb TL_LOCK_ASSERT(sc); 207236270Swpaul 2073147256Sbrooks ifp = sc->tl_ifp; 207436270Swpaul 207536270Swpaul /* 207636270Swpaul * Cancel pending I/O. 207736270Swpaul */ 207836270Swpaul tl_stop(sc); 207936270Swpaul 208051439Swpaul /* Initialize TX FIFO threshold */ 208151439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 208251439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 208351439Swpaul 208451439Swpaul /* Set PCI burst size */ 208551439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 208651439Swpaul 208736270Swpaul /* 208836270Swpaul * Set 'capture all frames' bit for promiscuous mode. 208936270Swpaul */ 209039583Swpaul if (ifp->if_flags & IFF_PROMISC) 209139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 209239583Swpaul else 209339583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 209436270Swpaul 209536270Swpaul /* 209636270Swpaul * Set capture broadcast bit to capture broadcast frames. 209736270Swpaul */ 209839583Swpaul if (ifp->if_flags & IFF_BROADCAST) 209939583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210039583Swpaul else 210139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 210236270Swpaul 210350468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 210450468Swpaul 210536270Swpaul /* Init our MAC address */ 2106152315Sru tl_setfilt(sc, IF_LLADDR(sc->tl_ifp), 0); 210736270Swpaul 210839583Swpaul /* Init multicast filter, if needed. */ 210939583Swpaul tl_setmulti(sc); 211039583Swpaul 211136270Swpaul /* Init circular RX list. */ 211239583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2113162315Sglebius device_printf(sc->tl_dev, 2114105599Sbrooks "initialization failed: no memory for rx buffers\n"); 211539583Swpaul tl_stop(sc); 211636270Swpaul return; 211736270Swpaul } 211836270Swpaul 211936270Swpaul /* Init TX pointers. */ 212036270Swpaul tl_list_tx_init(sc); 212136270Swpaul 212239583Swpaul /* Enable PCI interrupts. */ 212339583Swpaul CMD_SET(sc, TL_CMD_INTSON); 212436270Swpaul 212536270Swpaul /* Load the address of the rx list */ 212639583Swpaul CMD_SET(sc, TL_CMD_RT); 212739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 212836270Swpaul 212950462Swpaul if (!sc->tl_bitrate) { 213050462Swpaul if (sc->tl_miibus != NULL) { 213150462Swpaul mii = device_get_softc(sc->tl_miibus); 213250462Swpaul mii_mediachg(mii); 213350462Swpaul } 2134113548Smdodd } else { 2135113548Smdodd tl_ifmedia_upd(ifp); 213650462Swpaul } 213738030Swpaul 213836270Swpaul /* Send the RX go command */ 213950468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 214036270Swpaul 2141148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 2142148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 214336270Swpaul 214436270Swpaul /* Start the stats update counter */ 2145150171Sjhb callout_reset(&sc->tl_stat_callout, hz, tl_stats_update, sc); 214636270Swpaul 214736270Swpaul return; 214836270Swpaul} 214936270Swpaul 215036270Swpaul/* 215136270Swpaul * Set media options. 215236270Swpaul */ 2153102336Salfredstatic int 2154102336Salfredtl_ifmedia_upd(ifp) 215536270Swpaul struct ifnet *ifp; 215636270Swpaul{ 215736270Swpaul struct tl_softc *sc; 215850462Swpaul struct mii_data *mii = NULL; 215936270Swpaul 216036270Swpaul sc = ifp->if_softc; 216136270Swpaul 2162150171Sjhb TL_LOCK(sc); 216350462Swpaul if (sc->tl_bitrate) 216450462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 216550462Swpaul else { 216650462Swpaul mii = device_get_softc(sc->tl_miibus); 216750462Swpaul mii_mediachg(mii); 216850462Swpaul } 2169150171Sjhb TL_UNLOCK(sc); 217036270Swpaul 217136270Swpaul return(0); 217236270Swpaul} 217336270Swpaul 217436270Swpaul/* 217536270Swpaul * Report current media status. 217636270Swpaul */ 2177102336Salfredstatic void 2178102336Salfredtl_ifmedia_sts(ifp, ifmr) 217936270Swpaul struct ifnet *ifp; 218036270Swpaul struct ifmediareq *ifmr; 218136270Swpaul{ 218236270Swpaul struct tl_softc *sc; 218350462Swpaul struct mii_data *mii; 218436270Swpaul 218536270Swpaul sc = ifp->if_softc; 218636270Swpaul 2187150171Sjhb TL_LOCK(sc); 218836270Swpaul ifmr->ifm_active = IFM_ETHER; 218936270Swpaul 219045155Swpaul if (sc->tl_bitrate) { 219145155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 219245155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 219345155Swpaul else 219445155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 219545155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 219645155Swpaul ifmr->ifm_active |= IFM_HDX; 219745155Swpaul else 219845155Swpaul ifmr->ifm_active |= IFM_FDX; 219945155Swpaul return; 220036270Swpaul } else { 220150462Swpaul mii = device_get_softc(sc->tl_miibus); 220250462Swpaul mii_pollstat(mii); 220350462Swpaul ifmr->ifm_active = mii->mii_media_active; 220450462Swpaul ifmr->ifm_status = mii->mii_media_status; 220536270Swpaul } 2206150171Sjhb TL_UNLOCK(sc); 220736270Swpaul 220836270Swpaul return; 220936270Swpaul} 221036270Swpaul 2211102336Salfredstatic int 2212102336Salfredtl_ioctl(ifp, command, data) 221336270Swpaul struct ifnet *ifp; 221436735Sdfr u_long command; 221536270Swpaul caddr_t data; 221636270Swpaul{ 221736270Swpaul struct tl_softc *sc = ifp->if_softc; 221836270Swpaul struct ifreq *ifr = (struct ifreq *) data; 2219150171Sjhb int error = 0; 222036270Swpaul 222136270Swpaul switch(command) { 222236270Swpaul case SIOCSIFFLAGS: 2223150171Sjhb TL_LOCK(sc); 222436270Swpaul if (ifp->if_flags & IFF_UP) { 2225148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING && 222650462Swpaul ifp->if_flags & IFF_PROMISC && 222750462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 222850462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 222950462Swpaul tl_setmulti(sc); 2230148887Srwatson } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 223150462Swpaul !(ifp->if_flags & IFF_PROMISC) && 223250462Swpaul sc->tl_if_flags & IFF_PROMISC) { 223350462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 223450462Swpaul tl_setmulti(sc); 223550462Swpaul } else 2236150171Sjhb tl_init_locked(sc); 223736270Swpaul } else { 2238148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 223936270Swpaul tl_stop(sc); 224036270Swpaul } 224136270Swpaul } 224250462Swpaul sc->tl_if_flags = ifp->if_flags; 2243150171Sjhb TL_UNLOCK(sc); 224436270Swpaul error = 0; 224536270Swpaul break; 224636270Swpaul case SIOCADDMULTI: 224736270Swpaul case SIOCDELMULTI: 2248150171Sjhb TL_LOCK(sc); 224936270Swpaul tl_setmulti(sc); 2250150171Sjhb TL_UNLOCK(sc); 225136270Swpaul error = 0; 225236270Swpaul break; 225336270Swpaul case SIOCSIFMEDIA: 225436270Swpaul case SIOCGIFMEDIA: 225550462Swpaul if (sc->tl_bitrate) 225650462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 225750462Swpaul else { 225850462Swpaul struct mii_data *mii; 225950462Swpaul mii = device_get_softc(sc->tl_miibus); 226050462Swpaul error = ifmedia_ioctl(ifp, ifr, 226150462Swpaul &mii->mii_media, command); 226250462Swpaul } 226336270Swpaul break; 226436270Swpaul default: 2265106936Ssam error = ether_ioctl(ifp, command, data); 226636270Swpaul break; 226736270Swpaul } 226836270Swpaul 226936270Swpaul return(error); 227036270Swpaul} 227136270Swpaul 2272102336Salfredstatic void 2273199560Sjhbtl_watchdog(sc) 2274199560Sjhb struct tl_softc *sc; 2275199560Sjhb{ 227636270Swpaul struct ifnet *ifp; 227736270Swpaul 2278199560Sjhb TL_LOCK_ASSERT(sc); 2279199560Sjhb ifp = sc->tl_ifp; 228036270Swpaul 2281105599Sbrooks if_printf(ifp, "device timeout\n"); 228236270Swpaul 228336270Swpaul ifp->if_oerrors++; 228436270Swpaul 228550468Swpaul tl_softreset(sc, 1); 2286150171Sjhb tl_init_locked(sc); 228736270Swpaul 228836270Swpaul return; 228936270Swpaul} 229036270Swpaul 229136270Swpaul/* 229236270Swpaul * Stop the adapter and free any mbufs allocated to the 229336270Swpaul * RX and TX lists. 229436270Swpaul */ 2295102336Salfredstatic void 2296102336Salfredtl_stop(sc) 229736270Swpaul struct tl_softc *sc; 229836270Swpaul{ 229936270Swpaul register int i; 230036270Swpaul struct ifnet *ifp; 230136270Swpaul 2302150171Sjhb TL_LOCK_ASSERT(sc); 230367087Swpaul 2304147256Sbrooks ifp = sc->tl_ifp; 230536270Swpaul 230636270Swpaul /* Stop the stats updater. */ 2307150171Sjhb callout_stop(&sc->tl_stat_callout); 230836270Swpaul 230936270Swpaul /* Stop the transmitter */ 231039583Swpaul CMD_CLR(sc, TL_CMD_RT); 231139583Swpaul CMD_SET(sc, TL_CMD_STOP); 231239583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231336270Swpaul 231436270Swpaul /* Stop the receiver */ 231539583Swpaul CMD_SET(sc, TL_CMD_RT); 231639583Swpaul CMD_SET(sc, TL_CMD_STOP); 231739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231836270Swpaul 231936270Swpaul /* 232036270Swpaul * Disable host interrupts. 232136270Swpaul */ 232239583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 232336270Swpaul 232436270Swpaul /* 232536270Swpaul * Clear list pointer. 232636270Swpaul */ 232739583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 232836270Swpaul 232936270Swpaul /* 233036270Swpaul * Free the RX lists. 233136270Swpaul */ 233236270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 233336270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 233436270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 233536270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 233636270Swpaul } 233736270Swpaul } 233836270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 233936270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 234036270Swpaul 234136270Swpaul /* 234236270Swpaul * Free the TX list buffers. 234336270Swpaul */ 234436270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 234536270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 234636270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 234736270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 234836270Swpaul } 234936270Swpaul } 235036270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 235136270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 235236270Swpaul 2353148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 235436270Swpaul 235536270Swpaul return; 235636270Swpaul} 235736270Swpaul 235836270Swpaul/* 235936270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 236036270Swpaul * get confused by errant DMAs when rebooting. 236136270Swpaul */ 2362188463Simpstatic int 2363102336Salfredtl_shutdown(dev) 236448992Swpaul device_t dev; 236536270Swpaul{ 236639583Swpaul struct tl_softc *sc; 236736270Swpaul 236848992Swpaul sc = device_get_softc(dev); 236936270Swpaul 2370150171Sjhb TL_LOCK(sc); 237139583Swpaul tl_stop(sc); 2372150171Sjhb TL_UNLOCK(sc); 237336270Swpaul 2374188463Simp return (0); 237536270Swpaul} 2376