if_tl.c revision 148887
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/pci/if_tl.c 148887 2005-08-09 10:20:02Z rwatson $"); 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 21836270Swpaul#include <pci/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 22451089Speter/* "controller miibus0" 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 *); 279142407Simpstatic int tl_ioctl(struct ifnet *, u_long, caddr_t); 280142407Simpstatic void tl_init(void *); 281142407Simpstatic void tl_stop(struct tl_softc *); 282142407Simpstatic void tl_watchdog(struct ifnet *); 283142407Simpstatic void tl_shutdown(device_t); 284142407Simpstatic int tl_ifmedia_upd(struct ifnet *); 285142407Simpstatic void tl_ifmedia_sts(struct ifnet *, struct ifmediareq *); 28636270Swpaul 287142407Simpstatic u_int8_t tl_eeprom_putbyte(struct tl_softc *, int); 288142407Simpstatic u_int8_t tl_eeprom_getbyte(struct tl_softc *, int, u_int8_t *); 289142407Simpstatic int tl_read_eeprom(struct tl_softc *, caddr_t, int, int); 29036270Swpaul 291142407Simpstatic void tl_mii_sync(struct tl_softc *); 292142407Simpstatic void tl_mii_send(struct tl_softc *, u_int32_t, int); 293142407Simpstatic int tl_mii_readreg(struct tl_softc *, struct tl_mii_frame *); 294142407Simpstatic int tl_mii_writereg(struct tl_softc *, struct tl_mii_frame *); 295142407Simpstatic int tl_miibus_readreg(device_t, int, int); 296142407Simpstatic int tl_miibus_writereg(device_t, int, int, int); 297142407Simpstatic void tl_miibus_statchg(device_t); 29836270Swpaul 299142407Simpstatic void tl_setmode(struct tl_softc *, int); 300142407Simpstatic uint32_t tl_mchash(const uint8_t *); 301142407Simpstatic void tl_setmulti(struct tl_softc *); 302142407Simpstatic void tl_setfilt(struct tl_softc *, caddr_t, int); 303142407Simpstatic void tl_softreset(struct tl_softc *, int); 304142407Simpstatic void tl_hardreset(device_t); 305142407Simpstatic int tl_list_rx_init(struct tl_softc *); 306142407Simpstatic int tl_list_tx_init(struct tl_softc *); 30736270Swpaul 308142407Simpstatic u_int8_t tl_dio_read8(struct tl_softc *, int); 309142407Simpstatic u_int16_t tl_dio_read16(struct tl_softc *, int); 310142407Simpstatic u_int32_t tl_dio_read32(struct tl_softc *, int); 311142407Simpstatic void tl_dio_write8(struct tl_softc *, int, int); 312142407Simpstatic void tl_dio_write16(struct tl_softc *, int, int); 313142407Simpstatic void tl_dio_write32(struct tl_softc *, int, int); 314142407Simpstatic void tl_dio_setbit(struct tl_softc *, int, int); 315142407Simpstatic void tl_dio_clrbit(struct tl_softc *, int, int); 316142407Simpstatic void tl_dio_setbit16(struct tl_softc *, int, int); 317142407Simpstatic void tl_dio_clrbit16(struct tl_softc *, int, int); 31839583Swpaul 31949010Swpaul#ifdef TL_USEIOSPACE 32049010Swpaul#define TL_RES SYS_RES_IOPORT 32149010Swpaul#define TL_RID TL_PCI_LOIO 32249010Swpaul#else 32349010Swpaul#define TL_RES SYS_RES_MEMORY 32449010Swpaul#define TL_RID TL_PCI_LOMEM 32549010Swpaul#endif 32649010Swpaul 32748992Swpaulstatic device_method_t tl_methods[] = { 32848992Swpaul /* Device interface */ 32948992Swpaul DEVMETHOD(device_probe, tl_probe), 33048992Swpaul DEVMETHOD(device_attach, tl_attach), 33148992Swpaul DEVMETHOD(device_detach, tl_detach), 33248992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 33350462Swpaul 33450462Swpaul /* bus interface */ 33550462Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 33650462Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 33750462Swpaul 33850462Swpaul /* MII interface */ 33950462Swpaul DEVMETHOD(miibus_readreg, tl_miibus_readreg), 34050462Swpaul DEVMETHOD(miibus_writereg, tl_miibus_writereg), 34150462Swpaul DEVMETHOD(miibus_statchg, tl_miibus_statchg), 34250462Swpaul 34348992Swpaul { 0, 0 } 34448992Swpaul}; 34548992Swpaul 34648992Swpaulstatic driver_t tl_driver = { 34751455Swpaul "tl", 34848992Swpaul tl_methods, 34948992Swpaul sizeof(struct tl_softc) 35048992Swpaul}; 35148992Swpaul 35248992Swpaulstatic devclass_t tl_devclass; 35348992Swpaul 354113506SmdoddDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 35551473SwpaulDRIVER_MODULE(miibus, tl, miibus_driver, miibus_devclass, 0, 0); 35648992Swpaul 35739583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 35841656Swpaul struct tl_softc *sc; 35941656Swpaul int reg; 36039583Swpaul{ 36139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 36239583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 36339583Swpaul} 36439583Swpaul 36539583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 36641656Swpaul struct tl_softc *sc; 36741656Swpaul int reg; 36839583Swpaul{ 36939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37039583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 37139583Swpaul} 37239583Swpaul 37339583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 37441656Swpaul struct tl_softc *sc; 37541656Swpaul int reg; 37639583Swpaul{ 37739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37839583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 37939583Swpaul} 38039583Swpaul 38139583Swpaulstatic void tl_dio_write8(sc, reg, val) 38241656Swpaul struct tl_softc *sc; 38341656Swpaul int reg; 38441656Swpaul int val; 38539583Swpaul{ 38639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38739583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 38839583Swpaul return; 38939583Swpaul} 39039583Swpaul 39139583Swpaulstatic void tl_dio_write16(sc, reg, val) 39241656Swpaul struct tl_softc *sc; 39341656Swpaul int reg; 39441656Swpaul int val; 39539583Swpaul{ 39639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39739583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 39839583Swpaul return; 39939583Swpaul} 40039583Swpaul 40139583Swpaulstatic void tl_dio_write32(sc, reg, val) 40241656Swpaul struct tl_softc *sc; 40341656Swpaul int reg; 40441656Swpaul int val; 40539583Swpaul{ 40639583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40739583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 40839583Swpaul return; 40939583Swpaul} 41039583Swpaul 411102336Salfredstatic void 412102336Salfredtl_dio_setbit(sc, reg, bit) 41341656Swpaul struct tl_softc *sc; 41441656Swpaul int reg; 41541656Swpaul int bit; 41639583Swpaul{ 41739583Swpaul u_int8_t f; 41839583Swpaul 41939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 42039583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 42139583Swpaul f |= bit; 42239583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 42339583Swpaul 42439583Swpaul return; 42539583Swpaul} 42639583Swpaul 427102336Salfredstatic void 428102336Salfredtl_dio_clrbit(sc, reg, bit) 42941656Swpaul struct tl_softc *sc; 43041656Swpaul int reg; 43141656Swpaul int bit; 43239583Swpaul{ 43339583Swpaul u_int8_t f; 43439583Swpaul 43539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 43639583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 43739583Swpaul f &= ~bit; 43839583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 43939583Swpaul 44039583Swpaul return; 44139583Swpaul} 44239583Swpaul 44339583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 44441656Swpaul struct tl_softc *sc; 44541656Swpaul int reg; 44641656Swpaul int bit; 44739583Swpaul{ 44839583Swpaul u_int16_t f; 44939583Swpaul 45039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 45139583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 45239583Swpaul f |= bit; 45339583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 45439583Swpaul 45539583Swpaul return; 45639583Swpaul} 45739583Swpaul 45839583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 45941656Swpaul struct tl_softc *sc; 46041656Swpaul int reg; 46141656Swpaul int bit; 46239583Swpaul{ 46339583Swpaul u_int16_t f; 46439583Swpaul 46539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 46639583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 46739583Swpaul f &= ~bit; 46839583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 46939583Swpaul 47039583Swpaul return; 47139583Swpaul} 47239583Swpaul 47336270Swpaul/* 47436270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 47536270Swpaul */ 47639583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 47739583Swpaul struct tl_softc *sc; 47841656Swpaul int byte; 47936270Swpaul{ 48036270Swpaul register int i, ack = 0; 48136270Swpaul 48236270Swpaul /* 48336270Swpaul * Make sure we're in TX mode. 48436270Swpaul */ 48539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 48636270Swpaul 48736270Swpaul /* 48836270Swpaul * Feed in each bit and stobe the clock. 48936270Swpaul */ 49036270Swpaul for (i = 0x80; i; i >>= 1) { 49136270Swpaul if (byte & i) { 49239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 49336270Swpaul } else { 49439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 49536270Swpaul } 49639583Swpaul DELAY(1); 49739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 49839583Swpaul DELAY(1); 49939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50036270Swpaul } 50136270Swpaul 50236270Swpaul /* 50336270Swpaul * Turn off TX mode. 50436270Swpaul */ 50539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 50636270Swpaul 50736270Swpaul /* 50836270Swpaul * Check for ack. 50936270Swpaul */ 51039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51139583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 51239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51336270Swpaul 51436270Swpaul return(ack); 51536270Swpaul} 51636270Swpaul 51736270Swpaul/* 51836270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 51936270Swpaul */ 52039583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 52139583Swpaul struct tl_softc *sc; 52241656Swpaul int addr; 52336270Swpaul u_int8_t *dest; 52436270Swpaul{ 52536270Swpaul register int i; 52636270Swpaul u_int8_t byte = 0; 527147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 52836270Swpaul 52939583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53039583Swpaul 53136270Swpaul EEPROM_START; 53239583Swpaul 53336270Swpaul /* 53436270Swpaul * Send write control code to EEPROM. 53536270Swpaul */ 53639583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 537105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 538105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 53936270Swpaul return(1); 54039583Swpaul } 54136270Swpaul 54236270Swpaul /* 54336270Swpaul * Send address of byte we want to read. 54436270Swpaul */ 54539583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 546105599Sbrooks if_printf(ifp, "failed to send address, status: %x\n", 547105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 54836270Swpaul return(1); 54939583Swpaul } 55036270Swpaul 55136270Swpaul EEPROM_STOP; 55236270Swpaul EEPROM_START; 55336270Swpaul /* 55436270Swpaul * Send read control code to EEPROM. 55536270Swpaul */ 55639583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 557105599Sbrooks if_printf(ifp, "failed to send write command, status: %x\n", 558105599Sbrooks tl_dio_read8(sc, TL_NETSIO)); 55936270Swpaul return(1); 56039583Swpaul } 56136270Swpaul 56236270Swpaul /* 56336270Swpaul * Start reading bits from EEPROM. 56436270Swpaul */ 56539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 56636270Swpaul for (i = 0x80; i; i >>= 1) { 56739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 56839583Swpaul DELAY(1); 56939583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57036270Swpaul byte |= i; 57139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57236501Swpaul DELAY(1); 57336270Swpaul } 57436270Swpaul 57536270Swpaul EEPROM_STOP; 57636270Swpaul 57736270Swpaul /* 57836270Swpaul * No ACK generated for read, so just return byte. 57936270Swpaul */ 58036270Swpaul 58136270Swpaul *dest = byte; 58236270Swpaul 58336270Swpaul return(0); 58436270Swpaul} 58536270Swpaul 58639583Swpaul/* 58739583Swpaul * Read a sequence of bytes from the EEPROM. 58839583Swpaul */ 589102336Salfredstatic int 590102336Salfredtl_read_eeprom(sc, dest, off, cnt) 59139583Swpaul struct tl_softc *sc; 59239583Swpaul caddr_t dest; 59339583Swpaul int off; 59439583Swpaul int cnt; 59536270Swpaul{ 59639583Swpaul int err = 0, i; 59739583Swpaul u_int8_t byte = 0; 59839583Swpaul 59939583Swpaul for (i = 0; i < cnt; i++) { 60039583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60139583Swpaul if (err) 60239583Swpaul break; 60339583Swpaul *(dest + i) = byte; 60439583Swpaul } 60539583Swpaul 60639583Swpaul return(err ? 1 : 0); 60739583Swpaul} 60839583Swpaul 609102336Salfredstatic void 610102336Salfredtl_mii_sync(sc) 61139583Swpaul struct tl_softc *sc; 61239583Swpaul{ 61336270Swpaul register int i; 61436270Swpaul 61539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 61636270Swpaul 61736270Swpaul for (i = 0; i < 32; i++) { 61839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 61939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62036270Swpaul } 62136270Swpaul 62236270Swpaul return; 62336270Swpaul} 62436270Swpaul 625102336Salfredstatic void 626102336Salfredtl_mii_send(sc, bits, cnt) 62739583Swpaul struct tl_softc *sc; 62836270Swpaul u_int32_t bits; 62936270Swpaul int cnt; 63036270Swpaul{ 63136270Swpaul int i; 63236270Swpaul 63336270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 63439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 63536270Swpaul if (bits & i) { 63639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 63736270Swpaul } else { 63839583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 63936270Swpaul } 64039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64136270Swpaul } 64236270Swpaul} 64336270Swpaul 644102336Salfredstatic int 645102336Salfredtl_mii_readreg(sc, frame) 64639583Swpaul struct tl_softc *sc; 64736270Swpaul struct tl_mii_frame *frame; 64836270Swpaul 64936270Swpaul{ 65067087Swpaul int i, ack; 65136270Swpaul int minten = 0; 65236270Swpaul 65367087Swpaul TL_LOCK(sc); 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 73367087Swpaul TL_UNLOCK(sc); 73436270Swpaul 73536270Swpaul if (ack) 73636270Swpaul return(1); 73736270Swpaul return(0); 73836270Swpaul} 73936270Swpaul 740102336Salfredstatic int 741102336Salfredtl_mii_writereg(sc, frame) 74239583Swpaul struct tl_softc *sc; 74336270Swpaul struct tl_mii_frame *frame; 74436270Swpaul 74536270Swpaul{ 74636270Swpaul int minten; 74736270Swpaul 74867087Swpaul TL_LOCK(sc); 74967087Swpaul 75039583Swpaul tl_mii_sync(sc); 75136270Swpaul 75236270Swpaul /* 75336270Swpaul * Set up frame for TX. 75436270Swpaul */ 75536270Swpaul 75636270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 75736270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 75836270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 75936270Swpaul 76036270Swpaul /* 76136270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76236270Swpaul */ 76339583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76436270Swpaul if (minten) { 76539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 76636270Swpaul } 76736270Swpaul 76836270Swpaul /* 76936270Swpaul * Turn on data output. 77036270Swpaul */ 77139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77236270Swpaul 77339583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77439583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77539583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 77639583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 77739583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 77839583Swpaul tl_mii_send(sc, frame->mii_data, 16); 77936270Swpaul 78039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78236270Swpaul 78336270Swpaul /* 78436270Swpaul * Turn off xmit. 78536270Swpaul */ 78639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 78736270Swpaul 78836270Swpaul /* Reenable interrupts */ 78936270Swpaul if (minten) 79039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79136270Swpaul 79267087Swpaul TL_UNLOCK(sc); 79336270Swpaul 79436270Swpaul return(0); 79536270Swpaul} 79636270Swpaul 797102336Salfredstatic int 798102336Salfredtl_miibus_readreg(dev, phy, reg) 79950462Swpaul device_t dev; 80050462Swpaul int phy, reg; 80150462Swpaul{ 80236270Swpaul struct tl_softc *sc; 80336270Swpaul struct tl_mii_frame frame; 80436270Swpaul 80550462Swpaul sc = device_get_softc(dev); 80636270Swpaul bzero((char *)&frame, sizeof(frame)); 80736270Swpaul 80850462Swpaul frame.mii_phyaddr = phy; 80936270Swpaul frame.mii_regaddr = reg; 81039583Swpaul tl_mii_readreg(sc, &frame); 81136270Swpaul 81236270Swpaul return(frame.mii_data); 81336270Swpaul} 81436270Swpaul 815102336Salfredstatic int 816102336Salfredtl_miibus_writereg(dev, phy, reg, data) 81750462Swpaul device_t dev; 81850462Swpaul int phy, reg, data; 81950462Swpaul{ 82036270Swpaul struct tl_softc *sc; 82136270Swpaul struct tl_mii_frame frame; 82236270Swpaul 82350462Swpaul sc = device_get_softc(dev); 82436270Swpaul bzero((char *)&frame, sizeof(frame)); 82536270Swpaul 82650462Swpaul frame.mii_phyaddr = phy; 82736270Swpaul frame.mii_regaddr = reg; 82836270Swpaul frame.mii_data = data; 82936270Swpaul 83039583Swpaul tl_mii_writereg(sc, &frame); 83136270Swpaul 83250462Swpaul return(0); 83336270Swpaul} 83436270Swpaul 835102336Salfredstatic void 836102336Salfredtl_miibus_statchg(dev) 83750462Swpaul device_t dev; 83850462Swpaul{ 83936270Swpaul struct tl_softc *sc; 84050462Swpaul struct mii_data *mii; 84136270Swpaul 84250462Swpaul sc = device_get_softc(dev); 84367087Swpaul TL_LOCK(sc); 84450462Swpaul mii = device_get_softc(sc->tl_miibus); 84536270Swpaul 84650462Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 84750462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 84836270Swpaul } else { 84950462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 85036270Swpaul } 85167087Swpaul TL_UNLOCK(sc); 85236270Swpaul 85336270Swpaul return; 85436270Swpaul} 85536270Swpaul 85636270Swpaul/* 85750462Swpaul * Set modes for bitrate devices. 85836270Swpaul */ 859102336Salfredstatic void 860102336Salfredtl_setmode(sc, media) 86136270Swpaul struct tl_softc *sc; 86236270Swpaul int media; 86336270Swpaul{ 86450462Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 86550462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86636270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 86750462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 86836270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 86950462Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87039583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87136270Swpaul } else { 87250462Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 87339583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 87436270Swpaul } 87536270Swpaul } 87636270Swpaul 87736270Swpaul return; 87836270Swpaul} 87936270Swpaul 88036464Swpaul/* 88136464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 88236464Swpaul * table. This hash is simply the address split into 6-bit chunks 88336464Swpaul * XOR'd, e.g. 88436464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 88536464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 88636464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 88736464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 88836464Swpaul */ 889123289Sobrienstatic uint32_t 890122625Sobrientl_mchash(addr) 891123289Sobrien const uint8_t *addr; 89236270Swpaul{ 893123289Sobrien int t; 89436270Swpaul 89536464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 89636464Swpaul (addr[2] ^ addr[5]); 89736464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 89836270Swpaul} 89936270Swpaul 90039583Swpaul/* 90139583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 90239583Swpaul * the multicast hash filter. The perfect filter can be programmed 90339583Swpaul * with up to four MAC addresses. The first one is always used to 90439583Swpaul * hold the station address, which leaves us free to use the other 90539583Swpaul * three for multicast addresses. 90639583Swpaul */ 907102336Salfredstatic void 908102336Salfredtl_setfilt(sc, addr, slot) 90939583Swpaul struct tl_softc *sc; 91041656Swpaul caddr_t addr; 91139583Swpaul int slot; 91239583Swpaul{ 91339583Swpaul int i; 91439583Swpaul u_int16_t regaddr; 91539583Swpaul 91639583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 91739583Swpaul 91839583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 91939583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 92039583Swpaul 92139583Swpaul return; 92239583Swpaul} 92339583Swpaul 92439583Swpaul/* 92539583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 92639583Swpaul * linked list. This is fine, except addresses are added from the head 92739583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 92839583Swpaul * group to always be in the perfect filter, but as more groups are added, 92939583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 93039583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 93139583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 93239583Swpaul * and into the hash table. 93339583Swpaul * 93439583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 93539583Swpaul * circular queue, we don't have the ability to just grab the tail of 93639583Swpaul * the list and traverse it backwards. Instead, we have to traverse 93739583Swpaul * the list once to find the tail, then traverse it again backwards to 93839583Swpaul * update the multicast filter. 93939583Swpaul */ 940102336Salfredstatic void 941102336Salfredtl_setmulti(sc) 94236270Swpaul struct tl_softc *sc; 94336270Swpaul{ 94436270Swpaul struct ifnet *ifp; 94536270Swpaul u_int32_t hashes[2] = { 0, 0 }; 94639583Swpaul int h, i; 94736270Swpaul struct ifmultiaddr *ifma; 94839583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 949147256Sbrooks ifp = sc->tl_ifp; 95036270Swpaul 95139583Swpaul /* First, zot all the existing filters. */ 95239583Swpaul for (i = 1; i < 4; i++) 95341656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 95439583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 95539583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 95639583Swpaul 95739583Swpaul /* Now program new ones. */ 95839583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 95936270Swpaul hashes[0] = 0xFFFFFFFF; 96036270Swpaul hashes[1] = 0xFFFFFFFF; 96136270Swpaul } else { 96239583Swpaul i = 1; 963148654Srwatson IF_ADDR_LOCK(ifp); 96472084Sphk TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { 96536270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 96636270Swpaul continue; 96739583Swpaul /* 96839583Swpaul * Program the first three multicast groups 96939583Swpaul * into the perfect filter. For all others, 97039583Swpaul * use the hash table. 97139583Swpaul */ 97239583Swpaul if (i < 4) { 97339583Swpaul tl_setfilt(sc, 97439583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 97539583Swpaul i++; 97639583Swpaul continue; 97739583Swpaul } 97839583Swpaul 979122625Sobrien h = tl_mchash( 98036270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 98136270Swpaul if (h < 32) 98236270Swpaul hashes[0] |= (1 << h); 98336270Swpaul else 98436317Swpaul hashes[1] |= (1 << (h - 32)); 98536270Swpaul } 986148654Srwatson IF_ADDR_UNLOCK(ifp); 98736270Swpaul } 98836270Swpaul 98939583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 99039583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 99136270Swpaul 99236270Swpaul return; 99336270Swpaul} 99436270Swpaul 99539583Swpaul/* 99639583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 99739583Swpaul * the internal PHY is powered up correctly. It also recommends a one 99839583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 99939583Swpaul * experience this isn't necessary. 100039583Swpaul */ 1001102336Salfredstatic void 1002102336Salfredtl_hardreset(dev) 100350468Swpaul device_t dev; 100450468Swpaul{ 100539583Swpaul struct tl_softc *sc; 100639583Swpaul int i; 100750468Swpaul u_int16_t flags; 100839583Swpaul 100950468Swpaul sc = device_get_softc(dev); 101039583Swpaul 101150468Swpaul tl_mii_sync(sc); 101239583Swpaul 101350468Swpaul flags = BMCR_LOOP|BMCR_ISO|BMCR_PDOWN; 101439583Swpaul 101550468Swpaul for (i = 0; i < MII_NPHY; i++) 101650468Swpaul tl_miibus_writereg(dev, i, MII_BMCR, flags); 101739583Swpaul 101850468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_ISO); 101939583Swpaul DELAY(50000); 102050468Swpaul tl_miibus_writereg(dev, 31, MII_BMCR, BMCR_LOOP|BMCR_ISO); 102139583Swpaul tl_mii_sync(sc); 102250468Swpaul while(tl_miibus_readreg(dev, 31, MII_BMCR) & BMCR_RESET); 102339583Swpaul 102450468Swpaul DELAY(50000); 102539583Swpaul return; 102639583Swpaul} 102739583Swpaul 1028102336Salfredstatic void 1029102336Salfredtl_softreset(sc, internal) 103039583Swpaul struct tl_softc *sc; 103136270Swpaul int internal; 103236270Swpaul{ 103339583Swpaul u_int32_t cmd, dummy, i; 103436270Swpaul 103536270Swpaul /* Assert the adapter reset bit. */ 103639583Swpaul CMD_SET(sc, TL_CMD_ADRST); 103750468Swpaul 103836270Swpaul /* Turn off interrupts */ 103939583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 104036270Swpaul 104136270Swpaul /* First, clear the stats registers. */ 104239583Swpaul for (i = 0; i < 5; i++) 104339583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 104436270Swpaul 104536270Swpaul /* Clear Areg and Hash registers */ 104639583Swpaul for (i = 0; i < 8; i++) 104739583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 104836270Swpaul 104936270Swpaul /* 105036270Swpaul * Set up Netconfig register. Enable one channel and 105136270Swpaul * one fragment mode. 105236270Swpaul */ 105339583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 105445155Swpaul if (internal && !sc->tl_bitrate) { 105539583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105636270Swpaul } else { 105739583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 105836270Swpaul } 105936270Swpaul 106045155Swpaul /* Handle cards with bitrate devices. */ 106145155Swpaul if (sc->tl_bitrate) 106245155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 106345155Swpaul 106436270Swpaul /* 106536270Swpaul * Load adapter irq pacing timer and tx threshold. 106636270Swpaul * We make the transmit threshold 1 initially but we may 106736270Swpaul * change that later. 106836270Swpaul */ 106939583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 107036270Swpaul cmd |= TL_CMD_NES; 107136270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 107239583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 107339583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 107436270Swpaul 107536270Swpaul /* Unreset the MII */ 107639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 107736270Swpaul 107836270Swpaul /* Take the adapter out of reset */ 107939583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 108036270Swpaul 108136270Swpaul /* Wait for things to settle down a little. */ 108236270Swpaul DELAY(500); 108336270Swpaul 108436270Swpaul return; 108536270Swpaul} 108636270Swpaul 108736270Swpaul/* 108836270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 108939583Swpaul * against our list and return its name if we find a match. 109036270Swpaul */ 1091102336Salfredstatic int 1092102336Salfredtl_probe(dev) 109348992Swpaul device_t dev; 109436270Swpaul{ 109536270Swpaul struct tl_type *t; 109636270Swpaul 109736270Swpaul t = tl_devs; 109836270Swpaul 109936270Swpaul while(t->tl_name != NULL) { 110048992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 110148992Swpaul (pci_get_device(dev) == t->tl_did)) { 110248992Swpaul device_set_desc(dev, t->tl_name); 1103142398Simp return (BUS_PROBE_DEFAULT); 110448992Swpaul } 110536270Swpaul t++; 110636270Swpaul } 110736270Swpaul 110848992Swpaul return(ENXIO); 110936270Swpaul} 111036270Swpaul 1111102336Salfredstatic int 1112102336Salfredtl_attach(dev) 111348992Swpaul device_t dev; 111436270Swpaul{ 111567087Swpaul int i; 111639583Swpaul u_int16_t did, vid; 111739583Swpaul struct tl_type *t; 111839583Swpaul struct ifnet *ifp; 111939583Swpaul struct tl_softc *sc; 112048992Swpaul int unit, error = 0, rid; 1121147256Sbrooks u_char eaddr[6]; 112236270Swpaul 112348992Swpaul vid = pci_get_vendor(dev); 112448992Swpaul did = pci_get_device(dev); 112548992Swpaul sc = device_get_softc(dev); 112648992Swpaul unit = device_get_unit(dev); 112739583Swpaul 112839583Swpaul t = tl_devs; 112939583Swpaul while(t->tl_name != NULL) { 113039583Swpaul if (vid == t->tl_vid && did == t->tl_did) 113136270Swpaul break; 113239583Swpaul t++; 113339583Swpaul } 113436270Swpaul 113539583Swpaul if (t->tl_name == NULL) { 1136105599Sbrooks device_printf(dev, "unknown device!?\n"); 1137112878Sjhb return (ENXIO); 113836270Swpaul } 113936270Swpaul 114093818Sjhb mtx_init(&sc->tl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 114193818Sjhb MTX_DEF | MTX_RECURSE); 114269583Swpaul 114336270Swpaul /* 114436270Swpaul * Map control/status registers. 114536270Swpaul */ 114672813Swpaul pci_enable_busmaster(dev); 114736270Swpaul 114839583Swpaul#ifdef TL_USEIOSPACE 114939583Swpaul 115048992Swpaul rid = TL_PCI_LOIO; 1151127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1152127135Snjl RF_ACTIVE); 115348992Swpaul 115448992Swpaul /* 115548992Swpaul * Some cards have the I/O and memory mapped address registers 115648992Swpaul * reversed. Try both combinations before giving up. 115748992Swpaul */ 115848992Swpaul if (sc->tl_res == NULL) { 115948992Swpaul rid = TL_PCI_LOMEM; 1160127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 1161127135Snjl RF_ACTIVE); 116245155Swpaul } 116339583Swpaul#else 116448992Swpaul rid = TL_PCI_LOMEM; 1165127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1166127135Snjl RF_ACTIVE); 116748992Swpaul if (sc->tl_res == NULL) { 116848992Swpaul rid = TL_PCI_LOIO; 1169127135Snjl sc->tl_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1170127135Snjl RF_ACTIVE); 117136270Swpaul } 117239583Swpaul#endif 117336270Swpaul 117448992Swpaul if (sc->tl_res == NULL) { 1175105599Sbrooks device_printf(dev, "couldn't map ports/memory\n"); 117648992Swpaul error = ENXIO; 117748992Swpaul goto fail; 117848992Swpaul } 117948992Swpaul 118048992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 118148992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 118248992Swpaul 118339583Swpaul#ifdef notdef 118439583Swpaul /* 118539583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 118639583Swpaul * timer all the way up to its maximum value. I'm not sure 118739583Swpaul * if this is really necessary, but what the manual wants, 118839583Swpaul * the manual gets. 118939583Swpaul */ 119048992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 119139583Swpaul command |= 0x0000FF00; 119248992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 119339583Swpaul#endif 119436270Swpaul 119536270Swpaul /* Allocate interrupt */ 119648992Swpaul rid = 0; 1197127135Snjl sc->tl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 119848992Swpaul RF_SHAREABLE | RF_ACTIVE); 119948992Swpaul 120048992Swpaul if (sc->tl_irq == NULL) { 1201105599Sbrooks device_printf(dev, "couldn't map interrupt\n"); 120248992Swpaul error = ENXIO; 120336270Swpaul goto fail; 120436270Swpaul } 120536270Swpaul 120636270Swpaul /* 120751439Swpaul * Now allocate memory for the TX and RX lists. 120836270Swpaul */ 120951439Swpaul sc->tl_ldata = contigmalloc(sizeof(struct tl_list_data), M_DEVBUF, 121051657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 121139583Swpaul 121251439Swpaul if (sc->tl_ldata == NULL) { 1213105599Sbrooks device_printf(dev, "no memory for list buffers!\n"); 121448992Swpaul error = ENXIO; 121536270Swpaul goto fail; 121636270Swpaul } 121736270Swpaul 121839583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 121939583Swpaul 122039583Swpaul sc->tl_dinfo = t; 122143235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 122239583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 122339583Swpaul if (t->tl_vid == OLICOM_VENDORID) 122439583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 122539583Swpaul 122639583Swpaul /* Reset the adapter. */ 122739583Swpaul tl_softreset(sc, 1); 122850468Swpaul tl_hardreset(dev); 122939583Swpaul tl_softreset(sc, 1); 123039583Swpaul 123138030Swpaul /* 123239583Swpaul * Get station address from the EEPROM. 123339583Swpaul */ 1234147256Sbrooks if (tl_read_eeprom(sc, eaddr, sc->tl_eeaddr, ETHER_ADDR_LEN)) { 1235105599Sbrooks device_printf(dev, "failed to read station address\n"); 123648992Swpaul error = ENXIO; 123739583Swpaul goto fail; 123839583Swpaul } 123939583Swpaul 124039583Swpaul /* 124139583Swpaul * XXX Olicom, in its desire to be different from the 124239583Swpaul * rest of the world, has done strange things with the 124339583Swpaul * encoding of the station address in the EEPROM. First 124439583Swpaul * of all, they store the address at offset 0xF8 rather 124539583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 124639583Swpaul * Second, they store the address in three 16-bit words in 124739583Swpaul * network byte order, as opposed to storing it sequentially 124839583Swpaul * like all the other ThunderLAN cards. In order to get 124939583Swpaul * the station address in a form that matches what the Olicom 125039583Swpaul * diagnostic utility specifies, we have to byte-swap each 125139583Swpaul * word. To make things even more confusing, neither 00:00:28 125239583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 125339583Swpaul */ 125439583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 125539583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 125639583Swpaul u_int16_t *p; 1257147256Sbrooks p = (u_int16_t *)&eaddr[i]; 125839583Swpaul *p = ntohs(*p); 125939583Swpaul } 126039583Swpaul } 126139583Swpaul 1262147256Sbrooks ifp = sc->tl_ifp = if_alloc(IFT_ETHER); 1263147256Sbrooks if (ifp == NULL) { 1264147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 1265147256Sbrooks error = ENOSPC; 1266147256Sbrooks goto fail; 1267147256Sbrooks } 126839583Swpaul ifp->if_softc = sc; 1269121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1270134442Srwatson ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | 1271134442Srwatson IFF_NEEDSGIANT; 127239583Swpaul ifp->if_ioctl = tl_ioctl; 127339583Swpaul ifp->if_start = tl_start; 127439583Swpaul ifp->if_watchdog = tl_watchdog; 127539583Swpaul ifp->if_init = tl_init; 127639583Swpaul ifp->if_mtu = ETHERMTU; 127751439Swpaul ifp->if_snd.ifq_maxlen = TL_TX_LIST_CNT - 1; 127839583Swpaul callout_handle_init(&sc->tl_stat_ch); 127939583Swpaul 128039583Swpaul /* Reset the adapter again. */ 128139583Swpaul tl_softreset(sc, 1); 128250468Swpaul tl_hardreset(dev); 128339583Swpaul tl_softreset(sc, 1); 128439583Swpaul 128536270Swpaul /* 128650462Swpaul * Do MII setup. If no PHYs are found, then this is a 128750462Swpaul * bitrate ThunderLAN chip that only supports 10baseT 128850462Swpaul * and AUI/BNC. 128936270Swpaul */ 129050462Swpaul if (mii_phy_probe(dev, &sc->tl_miibus, 129150462Swpaul tl_ifmedia_upd, tl_ifmedia_sts)) { 129245155Swpaul struct ifmedia *ifm; 129345155Swpaul sc->tl_bitrate = 1; 129445155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 129545155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 129645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 129745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 129845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 129945166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 130045155Swpaul /* Reset again, this time setting bitrate mode. */ 130145155Swpaul tl_softreset(sc, 1); 130245155Swpaul ifm = &sc->ifmedia; 130345155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 130445155Swpaul tl_ifmedia_upd(ifp); 130536270Swpaul } 130636270Swpaul 130739583Swpaul /* 130863090Sarchie * Call MI attach routine. 130939583Swpaul */ 1310147256Sbrooks ether_ifattach(ifp, eaddr); 131138030Swpaul 1312113609Snjl /* Hook interrupt last to avoid having to lock softc */ 1313112872Snjl error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 1314112872Snjl tl_intr, sc, &sc->tl_intrhand); 1315112872Snjl 1316112872Snjl if (error) { 1317112872Snjl device_printf(dev, "couldn't set up irq\n"); 1318113609Snjl ether_ifdetach(ifp); 1319147256Sbrooks if_free(ifp); 1320112872Snjl goto fail; 1321112872Snjl } 1322112872Snjl 132336270Swpaulfail: 1324112872Snjl if (error) 1325112872Snjl tl_detach(dev); 1326112872Snjl 132748992Swpaul return(error); 132836270Swpaul} 132936270Swpaul 1330113609Snjl/* 1331113609Snjl * Shutdown hardware and free up resources. This can be called any 1332113609Snjl * time after the mutex has been initialized. It is called in both 1333113609Snjl * the error case in attach and the normal detach case so it needs 1334113609Snjl * to be careful about only freeing resources that have actually been 1335113609Snjl * allocated. 1336113609Snjl */ 1337102336Salfredstatic int 1338102336Salfredtl_detach(dev) 133948992Swpaul device_t dev; 134048992Swpaul{ 134148992Swpaul struct tl_softc *sc; 134248992Swpaul struct ifnet *ifp; 134348992Swpaul 134448992Swpaul sc = device_get_softc(dev); 1345112880Sjhb KASSERT(mtx_initialized(&sc->tl_mtx), ("tl mutex not initialized")); 134667087Swpaul TL_LOCK(sc); 1347147256Sbrooks ifp = sc->tl_ifp; 134848992Swpaul 1349113609Snjl /* These should only be active if attach succeeded */ 1350113812Simp if (device_is_attached(dev)) { 1351113609Snjl tl_stop(sc); 1352112872Snjl ether_ifdetach(ifp); 1353147256Sbrooks if_free(ifp); 1354113609Snjl } 1355113609Snjl if (sc->tl_miibus) 1356112872Snjl device_delete_child(dev, sc->tl_miibus); 1357113609Snjl bus_generic_detach(dev); 135848992Swpaul 1359112872Snjl if (sc->tl_ldata) 1360112872Snjl contigfree(sc->tl_ldata, sizeof(struct tl_list_data), M_DEVBUF); 136150462Swpaul if (sc->tl_bitrate) 136250462Swpaul ifmedia_removeall(&sc->ifmedia); 136348992Swpaul 1364112872Snjl if (sc->tl_intrhand) 1365112872Snjl bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 1366112872Snjl if (sc->tl_irq) 1367112872Snjl bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 1368112872Snjl if (sc->tl_res) 1369112872Snjl bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 137048992Swpaul 137167087Swpaul TL_UNLOCK(sc); 137267087Swpaul mtx_destroy(&sc->tl_mtx); 137348992Swpaul 137448992Swpaul return(0); 137548992Swpaul} 137648992Swpaul 137736270Swpaul/* 137836270Swpaul * Initialize the transmit lists. 137936270Swpaul */ 1380102336Salfredstatic int 1381102336Salfredtl_list_tx_init(sc) 138236270Swpaul struct tl_softc *sc; 138336270Swpaul{ 138436270Swpaul struct tl_chain_data *cd; 138536270Swpaul struct tl_list_data *ld; 138636270Swpaul int i; 138736270Swpaul 138836270Swpaul cd = &sc->tl_cdata; 138936270Swpaul ld = sc->tl_ldata; 139036270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 139136270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 139236270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 139336270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 139436270Swpaul else 139536270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 139636270Swpaul } 139736270Swpaul 139836270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 139936270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 140036270Swpaul sc->tl_txeoc = 1; 140136270Swpaul 140236270Swpaul return(0); 140336270Swpaul} 140436270Swpaul 140536270Swpaul/* 140636270Swpaul * Initialize the RX lists and allocate mbufs for them. 140736270Swpaul */ 1408102336Salfredstatic int 1409102336Salfredtl_list_rx_init(sc) 141036270Swpaul struct tl_softc *sc; 141136270Swpaul{ 141236270Swpaul struct tl_chain_data *cd; 141336270Swpaul struct tl_list_data *ld; 141436270Swpaul int i; 141536270Swpaul 141636270Swpaul cd = &sc->tl_cdata; 141736270Swpaul ld = sc->tl_ldata; 141836270Swpaul 141940795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 142036270Swpaul cd->tl_rx_chain[i].tl_ptr = 142137626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 142239583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 142339583Swpaul return(ENOBUFS); 142440795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 142536270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 142636270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 142736270Swpaul } else { 142836270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 142936270Swpaul ld->tl_rx_list[i].tlist_fptr = 143036270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 143136270Swpaul } 143236270Swpaul } 143336270Swpaul 143436270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 143536270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 143636270Swpaul 143736270Swpaul return(0); 143836270Swpaul} 143936270Swpaul 1440102336Salfredstatic int 1441102336Salfredtl_newbuf(sc, c) 144236270Swpaul struct tl_softc *sc; 144337626Swpaul struct tl_chain_onefrag *c; 144436270Swpaul{ 144536270Swpaul struct mbuf *m_new = NULL; 144636270Swpaul 1447111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 144887846Sluigi if (m_new == NULL) 144936270Swpaul return(ENOBUFS); 145036270Swpaul 1451111119Simp MCLGET(m_new, M_DONTWAIT); 145236270Swpaul if (!(m_new->m_flags & M_EXT)) { 145336270Swpaul m_freem(m_new); 145436270Swpaul return(ENOBUFS); 145536270Swpaul } 145636270Swpaul 145745155Swpaul#ifdef __alpha__ 145845155Swpaul m_new->m_data += 2; 145945155Swpaul#endif 146045155Swpaul 146136270Swpaul c->tl_mbuf = m_new; 146236270Swpaul c->tl_next = NULL; 146336270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 146436270Swpaul c->tl_ptr->tlist_fptr = 0; 146537626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 146637626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 146756060Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 146836270Swpaul 146936270Swpaul return(0); 147036270Swpaul} 147136270Swpaul/* 147236270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 147336270Swpaul * tells us that a full ethernet frame has been captured and we need 147436270Swpaul * to handle it. 147536270Swpaul * 147636270Swpaul * Reception is done using 'lists' which consist of a header and a 147736270Swpaul * series of 10 data count/data address pairs that point to buffers. 147836270Swpaul * Initially you're supposed to create a list, populate it with pointers 147936270Swpaul * to buffers, then load the physical address of the list into the 148036270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 148136270Swpaul * frame into the buffers for you. 148236270Swpaul * 148336270Swpaul * To make things as fast as possible, we have the chip DMA directly 148436270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 148536270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 148636270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 148736270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 148836270Swpaul * the chip, it will always think it has an endless receive channel. 148936270Swpaul * 149036270Swpaul * If we happen to fall behind and the chip manages to fill up all of 149136270Swpaul * the buffers, it will generate an end of channel interrupt and wait 149236270Swpaul * for us to empty the chain and restart the receiver. 149336270Swpaul */ 1494102336Salfredstatic int 1495102336Salfredtl_intvec_rxeof(xsc, type) 149636270Swpaul void *xsc; 149736270Swpaul u_int32_t type; 149836270Swpaul{ 149936270Swpaul struct tl_softc *sc; 150036270Swpaul int r = 0, total_len = 0; 150136270Swpaul struct ether_header *eh; 150236270Swpaul struct mbuf *m; 150336270Swpaul struct ifnet *ifp; 150437626Swpaul struct tl_chain_onefrag *cur_rx; 150536270Swpaul 150636270Swpaul sc = xsc; 1507147256Sbrooks ifp = sc->tl_ifp; 150836270Swpaul 1509122689Ssam TL_LOCK_ASSERT(sc); 1510122689Ssam 151156060Swpaul while(sc->tl_cdata.tl_rx_head != NULL) { 151256060Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 151356060Swpaul if (!(cur_rx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 151456060Swpaul break; 151536270Swpaul r++; 151636270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 151736270Swpaul m = cur_rx->tl_mbuf; 151836270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 151936270Swpaul 152039583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 152139583Swpaul ifp->if_ierrors++; 152239583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 152339583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 152439583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 152539583Swpaul continue; 152639583Swpaul } 152736270Swpaul 152836270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 152936270Swpaul vtophys(cur_rx->tl_ptr); 153036270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 153136270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 153236270Swpaul 153337626Swpaul /* 153437626Swpaul * Note: when the ThunderLAN chip is in 'capture all 153537626Swpaul * frames' mode, it will receive its own transmissions. 153637626Swpaul * We drop don't need to process our own transmissions, 153737626Swpaul * so we drop them here and continue. 153837626Swpaul */ 1539106936Ssam eh = mtod(m, struct ether_header *); 154039583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 1541147256Sbrooks if (!bcmp(eh->ether_shost, IFP2ENADDR(sc->tl_ifp), 154237626Swpaul ETHER_ADDR_LEN)) { 154337626Swpaul m_freem(m); 154437626Swpaul continue; 154537626Swpaul } 154637626Swpaul 1547106936Ssam m->m_pkthdr.rcvif = ifp; 1548106936Ssam m->m_pkthdr.len = m->m_len = total_len; 1549106936Ssam 1550122689Ssam TL_UNLOCK(sc); 1551106936Ssam (*ifp->if_input)(ifp, m); 1552122689Ssam TL_LOCK(sc); 155336270Swpaul } 155436270Swpaul 155536270Swpaul return(r); 155636270Swpaul} 155736270Swpaul 155836270Swpaul/* 155936270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 156036270Swpaul * initialized or the adapter reached a list with a forward pointer 156136270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 156236270Swpaul * the card has hit the end of the receive buffer chain and we need to 156336270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 156436270Swpaul */ 1565102336Salfredstatic int 1566102336Salfredtl_intvec_rxeoc(xsc, type) 156736270Swpaul void *xsc; 156836270Swpaul u_int32_t type; 156936270Swpaul{ 157036270Swpaul struct tl_softc *sc; 157136270Swpaul int r; 157256060Swpaul struct tl_chain_data *cd; 157336270Swpaul 157456060Swpaul 157536270Swpaul sc = xsc; 157656060Swpaul cd = &sc->tl_cdata; 157736270Swpaul 157836270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 157936270Swpaul r = tl_intvec_rxeof(xsc, type); 158039583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 158136270Swpaul r = 1; 158256060Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 158356060Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 158439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 158536270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 158636270Swpaul return(r); 158736270Swpaul} 158836270Swpaul 1589102336Salfredstatic int 1590102336Salfredtl_intvec_txeof(xsc, type) 159136270Swpaul void *xsc; 159236270Swpaul u_int32_t type; 159336270Swpaul{ 159436270Swpaul struct tl_softc *sc; 159536270Swpaul int r = 0; 159636270Swpaul struct tl_chain *cur_tx; 159736270Swpaul 159836270Swpaul sc = xsc; 159936270Swpaul 160036270Swpaul /* 160136270Swpaul * Go through our tx list and free mbufs for those 160236270Swpaul * frames that have been sent. 160336270Swpaul */ 160436270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 160536270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 160636270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 160736270Swpaul break; 160836270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 160936270Swpaul 161036270Swpaul r++; 161136270Swpaul m_freem(cur_tx->tl_mbuf); 161236270Swpaul cur_tx->tl_mbuf = NULL; 161336270Swpaul 161436270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 161536270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 161637626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 161737626Swpaul break; 161836270Swpaul } 161936270Swpaul 162036270Swpaul return(r); 162136270Swpaul} 162236270Swpaul 162336270Swpaul/* 162436270Swpaul * The transmit end of channel interrupt. The adapter triggers this 162536270Swpaul * interrupt to tell us it hit the end of the current transmit list. 162636270Swpaul * 162736270Swpaul * A note about this: it's possible for a condition to arise where 162836270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 162936270Swpaul * You have to avoid this since the chip expects things to go in a 163036270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 163136270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 163236270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 163336270Swpaul * interrupt should be received and acknowledged before any more frames 163436270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 163536270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 163636270Swpaul * it could attempt to issue a transmit command prematurely. 163736270Swpaul * 163836270Swpaul * To guard against this, tl_start() will only issue transmit commands 163936270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 164036270Swpaul * can set this flag once tl_start() has cleared it. 164136270Swpaul */ 1642102336Salfredstatic int 1643102336Salfredtl_intvec_txeoc(xsc, type) 164436270Swpaul void *xsc; 164536270Swpaul u_int32_t type; 164636270Swpaul{ 164736270Swpaul struct tl_softc *sc; 164836270Swpaul struct ifnet *ifp; 164936270Swpaul u_int32_t cmd; 165036270Swpaul 165136270Swpaul sc = xsc; 1652147256Sbrooks ifp = sc->tl_ifp; 165336270Swpaul 165436270Swpaul /* Clear the timeout timer. */ 165536270Swpaul ifp->if_timer = 0; 165636270Swpaul 165736270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 1658148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 165936270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 166036270Swpaul sc->tl_txeoc = 1; 166136270Swpaul } else { 166236270Swpaul sc->tl_txeoc = 0; 166336270Swpaul /* First we have to ack the EOC interrupt. */ 166439583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 166536270Swpaul /* Then load the address of the next TX list. */ 166639583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 166751439Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 166836270Swpaul /* Restart TX channel. */ 166939583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 167036270Swpaul cmd &= ~TL_CMD_RT; 167136270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 167239583Swpaul CMD_PUT(sc, cmd); 167336270Swpaul return(0); 167436270Swpaul } 167536270Swpaul 167636270Swpaul return(1); 167736270Swpaul} 167836270Swpaul 1679102336Salfredstatic int 1680102336Salfredtl_intvec_adchk(xsc, type) 168136270Swpaul void *xsc; 168236270Swpaul u_int32_t type; 168336270Swpaul{ 168436270Swpaul struct tl_softc *sc; 168536270Swpaul 168636270Swpaul sc = xsc; 168736270Swpaul 168839627Swpaul if (type) 1689147256Sbrooks if_printf(sc->tl_ifp, "adapter check: %x\n", 169041656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 169136270Swpaul 169239583Swpaul tl_softreset(sc, 1); 169337626Swpaul tl_stop(sc); 169436270Swpaul tl_init(sc); 169539583Swpaul CMD_SET(sc, TL_CMD_INTSON); 169636270Swpaul 169736270Swpaul return(0); 169836270Swpaul} 169936270Swpaul 1700102336Salfredstatic int 1701102336Salfredtl_intvec_netsts(xsc, type) 170236270Swpaul void *xsc; 170336270Swpaul u_int32_t type; 170436270Swpaul{ 170536270Swpaul struct tl_softc *sc; 170636270Swpaul u_int16_t netsts; 170736270Swpaul 170836270Swpaul sc = xsc; 170936270Swpaul 171039583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 171139583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 171236270Swpaul 1713147256Sbrooks if_printf(sc->tl_ifp, "network status: %x\n", netsts); 171436270Swpaul 171536270Swpaul return(1); 171636270Swpaul} 171736270Swpaul 1718102336Salfredstatic void 1719102336Salfredtl_intr(xsc) 172039583Swpaul void *xsc; 172136270Swpaul{ 172236270Swpaul struct tl_softc *sc; 172336270Swpaul struct ifnet *ifp; 172436270Swpaul int r = 0; 172536270Swpaul u_int32_t type = 0; 172636270Swpaul u_int16_t ints = 0; 172736270Swpaul u_int8_t ivec = 0; 172836270Swpaul 172939583Swpaul sc = xsc; 173067087Swpaul TL_LOCK(sc); 173136270Swpaul 173236270Swpaul /* Disable interrupts */ 173339583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 173439583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 173536270Swpaul type = (ints << 16) & 0xFFFF0000; 173636270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 173736270Swpaul ints = (ints & TL_INT_MASK) >> 2; 173836270Swpaul 1739147256Sbrooks ifp = sc->tl_ifp; 174036270Swpaul 174136270Swpaul switch(ints) { 174236270Swpaul case (TL_INTR_INVALID): 174339583Swpaul#ifdef DIAGNOSTIC 1744105599Sbrooks if_printf(ifp, "got an invalid interrupt!\n"); 174539583Swpaul#endif 174639583Swpaul /* Re-enable interrupts but don't ack this one. */ 174739583Swpaul CMD_PUT(sc, type); 174839583Swpaul r = 0; 174936270Swpaul break; 175036270Swpaul case (TL_INTR_TXEOF): 175136270Swpaul r = tl_intvec_txeof((void *)sc, type); 175236270Swpaul break; 175336270Swpaul case (TL_INTR_TXEOC): 175436270Swpaul r = tl_intvec_txeoc((void *)sc, type); 175536270Swpaul break; 175636270Swpaul case (TL_INTR_STATOFLOW): 175739583Swpaul tl_stats_update(sc); 175839583Swpaul r = 1; 175936270Swpaul break; 176036270Swpaul case (TL_INTR_RXEOF): 176136270Swpaul r = tl_intvec_rxeof((void *)sc, type); 176236270Swpaul break; 176336270Swpaul case (TL_INTR_DUMMY): 1764105599Sbrooks if_printf(ifp, "got a dummy interrupt\n"); 176539583Swpaul r = 1; 176636270Swpaul break; 176736270Swpaul case (TL_INTR_ADCHK): 176836270Swpaul if (ivec) 176936270Swpaul r = tl_intvec_adchk((void *)sc, type); 177036270Swpaul else 177136270Swpaul r = tl_intvec_netsts((void *)sc, type); 177236270Swpaul break; 177336270Swpaul case (TL_INTR_RXEOC): 177436270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 177536270Swpaul break; 177636270Swpaul default: 1777105599Sbrooks if_printf(ifp, "bogus interrupt type\n"); 177836270Swpaul break; 177936270Swpaul } 178036270Swpaul 178136270Swpaul /* Re-enable interrupts */ 178237626Swpaul if (r) { 178339583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 178437626Swpaul } 178536270Swpaul 178637626Swpaul if (ifp->if_snd.ifq_head != NULL) 178737626Swpaul tl_start(ifp); 178837626Swpaul 178967087Swpaul TL_UNLOCK(sc); 179067087Swpaul 179136270Swpaul return; 179236270Swpaul} 179336270Swpaul 1794102336Salfredstatic void 1795102336Salfredtl_stats_update(xsc) 179636270Swpaul void *xsc; 179736270Swpaul{ 179836270Swpaul struct tl_softc *sc; 179936270Swpaul struct ifnet *ifp; 180036270Swpaul struct tl_stats tl_stats; 180150462Swpaul struct mii_data *mii; 180236270Swpaul u_int32_t *p; 180336270Swpaul 180436270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 180536270Swpaul 180636270Swpaul sc = xsc; 180767087Swpaul TL_LOCK(sc); 1808147256Sbrooks ifp = sc->tl_ifp; 180936270Swpaul 181036270Swpaul p = (u_int32_t *)&tl_stats; 181136270Swpaul 181239583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 181339583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181439583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181539583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181639583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181739583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 181836270Swpaul 181936270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 182036270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 182136270Swpaul tl_stats.tl_tx_multi_collision; 182236270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 182336270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 182436270Swpaul tl_rx_overrun(tl_stats); 182536270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 182636270Swpaul 182751439Swpaul if (tl_tx_underrun(tl_stats)) { 182851439Swpaul u_int8_t tx_thresh; 182951439Swpaul tx_thresh = tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_TXTHRESH; 183051439Swpaul if (tx_thresh != TL_AC_TXTHRESH_WHOLEPKT) { 183151439Swpaul tx_thresh >>= 4; 183251439Swpaul tx_thresh++; 1833105599Sbrooks if_printf(ifp, "tx underrun -- increasing " 1834105599Sbrooks "tx threshold to %d bytes\n", 183551439Swpaul (64 * (tx_thresh * 4))); 183651439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 183751439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, tx_thresh << 4); 183851439Swpaul } 183951439Swpaul } 184051439Swpaul 184136270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 184236302Swpaul 184350462Swpaul if (!sc->tl_bitrate) { 184450462Swpaul mii = device_get_softc(sc->tl_miibus); 184550462Swpaul mii_tick(mii); 184650462Swpaul } 184750462Swpaul 184867087Swpaul TL_UNLOCK(sc); 184948992Swpaul 185036302Swpaul return; 185136270Swpaul} 185236270Swpaul 185336270Swpaul/* 185436270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 185536270Swpaul * pointers to the fragment pointers. 185636270Swpaul */ 1857102336Salfredstatic int 1858102336Salfredtl_encap(sc, c, m_head) 185936270Swpaul struct tl_softc *sc; 186036270Swpaul struct tl_chain *c; 186136270Swpaul struct mbuf *m_head; 186236270Swpaul{ 186336270Swpaul int frag = 0; 186436270Swpaul struct tl_frag *f = NULL; 186536270Swpaul int total_len; 186636270Swpaul struct mbuf *m; 1867147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 186836270Swpaul 186936270Swpaul /* 187036270Swpaul * Start packing the mbufs in this chain into 187136270Swpaul * the fragment pointers. Stop when we run out 187236270Swpaul * of fragments or hit the end of the mbuf chain. 187336270Swpaul */ 187436270Swpaul m = m_head; 187536270Swpaul total_len = 0; 187636270Swpaul 187736270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 187836270Swpaul if (m->m_len != 0) { 187936270Swpaul if (frag == TL_MAXFRAGS) 188036270Swpaul break; 188136270Swpaul total_len+= m->m_len; 188236270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 188336270Swpaul vtophys(mtod(m, vm_offset_t)); 188436270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 188536270Swpaul frag++; 188636270Swpaul } 188736270Swpaul } 188836270Swpaul 188936270Swpaul /* 189036270Swpaul * Handle special cases. 189136270Swpaul * Special case #1: we used up all 10 fragments, but 189236270Swpaul * we have more mbufs left in the chain. Copy the 189336270Swpaul * data into an mbuf cluster. Note that we don't 189436270Swpaul * bother clearing the values in the other fragment 189536270Swpaul * pointers/counters; it wouldn't gain us anything, 189636270Swpaul * and would waste cycles. 189736270Swpaul */ 189836270Swpaul if (m != NULL) { 189936270Swpaul struct mbuf *m_new = NULL; 190036270Swpaul 1901111119Simp MGETHDR(m_new, M_DONTWAIT, MT_DATA); 190236270Swpaul if (m_new == NULL) { 1903105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 190436270Swpaul return(1); 190536270Swpaul } 190636270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 1907111119Simp MCLGET(m_new, M_DONTWAIT); 190836270Swpaul if (!(m_new->m_flags & M_EXT)) { 190936270Swpaul m_freem(m_new); 1910105599Sbrooks if_printf(ifp, "no memory for tx list\n"); 191136270Swpaul return(1); 191236270Swpaul } 191336270Swpaul } 191436270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 191536270Swpaul mtod(m_new, caddr_t)); 191636270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 191736270Swpaul m_freem(m_head); 191836270Swpaul m_head = m_new; 191936270Swpaul f = &c->tl_ptr->tl_frag[0]; 192036270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 192136270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 192236270Swpaul frag = 1; 192336270Swpaul } 192436270Swpaul 192536270Swpaul /* 192636270Swpaul * Special case #2: the frame is smaller than the minimum 192736270Swpaul * frame size. We have to pad it to make the chip happy. 192836270Swpaul */ 192936270Swpaul if (total_len < TL_MIN_FRAMELEN) { 193036270Swpaul if (frag == TL_MAXFRAGS) 1931105599Sbrooks if_printf(ifp, 1932105599Sbrooks "all frags filled but frame still to small!\n"); 193336270Swpaul f = &c->tl_ptr->tl_frag[frag]; 193436270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 193536270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 193636270Swpaul total_len += f->tlist_dcnt; 193736270Swpaul frag++; 193836270Swpaul } 193936270Swpaul 194036270Swpaul c->tl_mbuf = m_head; 194136270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 194236270Swpaul c->tl_ptr->tlist_frsize = total_len; 194336270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 194436270Swpaul c->tl_ptr->tlist_fptr = 0; 194536270Swpaul 194636270Swpaul return(0); 194736270Swpaul} 194836270Swpaul 194936270Swpaul/* 195036270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 195136270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 195236270Swpaul * copy of the pointers since the transmit list fragment pointers are 195336270Swpaul * physical addresses. 195436270Swpaul */ 1955102336Salfredstatic void 1956102336Salfredtl_start(ifp) 195736270Swpaul struct ifnet *ifp; 195836270Swpaul{ 195936270Swpaul struct tl_softc *sc; 196036270Swpaul struct mbuf *m_head = NULL; 196136270Swpaul u_int32_t cmd; 196236270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 196336270Swpaul 196436270Swpaul sc = ifp->if_softc; 196567087Swpaul TL_LOCK(sc); 196636270Swpaul 196736270Swpaul /* 196836270Swpaul * Check for an available queue slot. If there are none, 196936270Swpaul * punt. 197036270Swpaul */ 197136270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 1972148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 197367087Swpaul TL_UNLOCK(sc); 197436270Swpaul return; 197536270Swpaul } 197636270Swpaul 197736270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 197836270Swpaul 197936270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 198036270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 198136270Swpaul if (m_head == NULL) 198236270Swpaul break; 198336270Swpaul 198436270Swpaul /* Pick a chain member off the free list. */ 198536270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 198636270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 198736270Swpaul 198836270Swpaul cur_tx->tl_next = NULL; 198936270Swpaul 199036270Swpaul /* Pack the data into the list. */ 199136270Swpaul tl_encap(sc, cur_tx, m_head); 199236270Swpaul 199336270Swpaul /* Chain it together */ 199436270Swpaul if (prev != NULL) { 199536270Swpaul prev->tl_next = cur_tx; 199636270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 199736270Swpaul } 199836270Swpaul prev = cur_tx; 199936270Swpaul 200036270Swpaul /* 200136270Swpaul * If there's a BPF listener, bounce a copy of this frame 200236270Swpaul * to him. 200336270Swpaul */ 2004106936Ssam BPF_MTAP(ifp, cur_tx->tl_mbuf); 200536270Swpaul } 200636270Swpaul 200736270Swpaul /* 200841526Swpaul * If there are no packets queued, bail. 200941526Swpaul */ 201067087Swpaul if (cur_tx == NULL) { 201167087Swpaul TL_UNLOCK(sc); 201241526Swpaul return; 201367087Swpaul } 201441526Swpaul 201541526Swpaul /* 201636270Swpaul * That's all we can stands, we can't stands no more. 201736270Swpaul * If there are no other transfers pending, then issue the 201836270Swpaul * TX GO command to the adapter to start things moving. 201936270Swpaul * Otherwise, just leave the data in the queue and let 202036270Swpaul * the EOF/EOC interrupt handler send. 202136270Swpaul */ 202236270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 202336270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 202436270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 202539583Swpaul 202636270Swpaul if (sc->tl_txeoc) { 202736270Swpaul sc->tl_txeoc = 0; 202839583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 202939583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 203036270Swpaul cmd &= ~TL_CMD_RT; 203136270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 203239583Swpaul CMD_PUT(sc, cmd); 203336270Swpaul } 203436270Swpaul } else { 203536270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 203642146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 203736270Swpaul } 203836270Swpaul 203936270Swpaul /* 204036270Swpaul * Set a timeout in case the chip goes out to lunch. 204136270Swpaul */ 204236270Swpaul ifp->if_timer = 5; 204367087Swpaul TL_UNLOCK(sc); 204436270Swpaul 204536270Swpaul return; 204636270Swpaul} 204736270Swpaul 2048102336Salfredstatic void 2049102336Salfredtl_init(xsc) 205036270Swpaul void *xsc; 205136270Swpaul{ 205236270Swpaul struct tl_softc *sc = xsc; 2053147256Sbrooks struct ifnet *ifp = sc->tl_ifp; 205450462Swpaul struct mii_data *mii; 205536270Swpaul 205667087Swpaul TL_LOCK(sc); 205736270Swpaul 2058147256Sbrooks ifp = sc->tl_ifp; 205936270Swpaul 206036270Swpaul /* 206136270Swpaul * Cancel pending I/O. 206236270Swpaul */ 206336270Swpaul tl_stop(sc); 206436270Swpaul 206551439Swpaul /* Initialize TX FIFO threshold */ 206651439Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH); 206751439Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_TXTHRESH_16LONG); 206851439Swpaul 206951439Swpaul /* Set PCI burst size */ 207051439Swpaul tl_dio_write8(sc, TL_BSIZEREG, TL_RXBURST_16LONG|TL_TXBURST_16LONG); 207151439Swpaul 207236270Swpaul /* 207336270Swpaul * Set 'capture all frames' bit for promiscuous mode. 207436270Swpaul */ 207539583Swpaul if (ifp->if_flags & IFF_PROMISC) 207639583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 207739583Swpaul else 207839583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 207936270Swpaul 208036270Swpaul /* 208136270Swpaul * Set capture broadcast bit to capture broadcast frames. 208236270Swpaul */ 208339583Swpaul if (ifp->if_flags & IFF_BROADCAST) 208439583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 208539583Swpaul else 208639583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 208736270Swpaul 208850468Swpaul tl_dio_write16(sc, TL_MAXRX, MCLBYTES); 208950468Swpaul 209036270Swpaul /* Init our MAC address */ 2091147256Sbrooks tl_setfilt(sc, (caddr_t)&IFP2ENADDR(sc->tl_ifp), 0); 209236270Swpaul 209339583Swpaul /* Init multicast filter, if needed. */ 209439583Swpaul tl_setmulti(sc); 209539583Swpaul 209636270Swpaul /* Init circular RX list. */ 209739583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 2098105599Sbrooks if_printf(ifp, 2099105599Sbrooks "initialization failed: no memory for rx buffers\n"); 210039583Swpaul tl_stop(sc); 210167087Swpaul TL_UNLOCK(sc); 210236270Swpaul return; 210336270Swpaul } 210436270Swpaul 210536270Swpaul /* Init TX pointers. */ 210636270Swpaul tl_list_tx_init(sc); 210736270Swpaul 210839583Swpaul /* Enable PCI interrupts. */ 210939583Swpaul CMD_SET(sc, TL_CMD_INTSON); 211036270Swpaul 211136270Swpaul /* Load the address of the rx list */ 211239583Swpaul CMD_SET(sc, TL_CMD_RT); 211339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 211436270Swpaul 211550462Swpaul if (!sc->tl_bitrate) { 211650462Swpaul if (sc->tl_miibus != NULL) { 211750462Swpaul mii = device_get_softc(sc->tl_miibus); 211850462Swpaul mii_mediachg(mii); 211950462Swpaul } 2120113548Smdodd } else { 2121113548Smdodd tl_ifmedia_upd(ifp); 212250462Swpaul } 212338030Swpaul 212436270Swpaul /* Send the RX go command */ 212550468Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_NES|TL_CMD_RT); 212636270Swpaul 2127148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 2128148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 212936270Swpaul 213036270Swpaul /* Start the stats update counter */ 213136270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 213267087Swpaul TL_UNLOCK(sc); 213336270Swpaul 213436270Swpaul return; 213536270Swpaul} 213636270Swpaul 213736270Swpaul/* 213836270Swpaul * Set media options. 213936270Swpaul */ 2140102336Salfredstatic int 2141102336Salfredtl_ifmedia_upd(ifp) 214236270Swpaul struct ifnet *ifp; 214336270Swpaul{ 214436270Swpaul struct tl_softc *sc; 214550462Swpaul struct mii_data *mii = NULL; 214636270Swpaul 214736270Swpaul sc = ifp->if_softc; 214836270Swpaul 214950462Swpaul if (sc->tl_bitrate) 215050462Swpaul tl_setmode(sc, sc->ifmedia.ifm_media); 215150462Swpaul else { 215250462Swpaul mii = device_get_softc(sc->tl_miibus); 215350462Swpaul mii_mediachg(mii); 215450462Swpaul } 215536270Swpaul 215636270Swpaul return(0); 215736270Swpaul} 215836270Swpaul 215936270Swpaul/* 216036270Swpaul * Report current media status. 216136270Swpaul */ 2162102336Salfredstatic void 2163102336Salfredtl_ifmedia_sts(ifp, ifmr) 216436270Swpaul struct ifnet *ifp; 216536270Swpaul struct ifmediareq *ifmr; 216636270Swpaul{ 216736270Swpaul struct tl_softc *sc; 216850462Swpaul struct mii_data *mii; 216936270Swpaul 217036270Swpaul sc = ifp->if_softc; 217136270Swpaul 217236270Swpaul ifmr->ifm_active = IFM_ETHER; 217336270Swpaul 217445155Swpaul if (sc->tl_bitrate) { 217545155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 217645155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 217745155Swpaul else 217845155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 217945155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 218045155Swpaul ifmr->ifm_active |= IFM_HDX; 218145155Swpaul else 218245155Swpaul ifmr->ifm_active |= IFM_FDX; 218345155Swpaul return; 218436270Swpaul } else { 218550462Swpaul mii = device_get_softc(sc->tl_miibus); 218650462Swpaul mii_pollstat(mii); 218750462Swpaul ifmr->ifm_active = mii->mii_media_active; 218850462Swpaul ifmr->ifm_status = mii->mii_media_status; 218936270Swpaul } 219036270Swpaul 219136270Swpaul return; 219236270Swpaul} 219336270Swpaul 2194102336Salfredstatic int 2195102336Salfredtl_ioctl(ifp, command, data) 219636270Swpaul struct ifnet *ifp; 219736735Sdfr u_long command; 219836270Swpaul caddr_t data; 219936270Swpaul{ 220036270Swpaul struct tl_softc *sc = ifp->if_softc; 220136270Swpaul struct ifreq *ifr = (struct ifreq *) data; 220236270Swpaul int s, error = 0; 220336270Swpaul 220436270Swpaul s = splimp(); 220536270Swpaul 220636270Swpaul switch(command) { 220736270Swpaul case SIOCSIFFLAGS: 220836270Swpaul if (ifp->if_flags & IFF_UP) { 2209148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING && 221050462Swpaul ifp->if_flags & IFF_PROMISC && 221150462Swpaul !(sc->tl_if_flags & IFF_PROMISC)) { 221250462Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 221350462Swpaul tl_setmulti(sc); 2214148887Srwatson } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 221550462Swpaul !(ifp->if_flags & IFF_PROMISC) && 221650462Swpaul sc->tl_if_flags & IFF_PROMISC) { 221750462Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 221850462Swpaul tl_setmulti(sc); 221950462Swpaul } else 222050462Swpaul tl_init(sc); 222136270Swpaul } else { 2222148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 222336270Swpaul tl_stop(sc); 222436270Swpaul } 222536270Swpaul } 222650462Swpaul sc->tl_if_flags = ifp->if_flags; 222736270Swpaul error = 0; 222836270Swpaul break; 222936270Swpaul case SIOCADDMULTI: 223036270Swpaul case SIOCDELMULTI: 223136270Swpaul tl_setmulti(sc); 223236270Swpaul error = 0; 223336270Swpaul break; 223436270Swpaul case SIOCSIFMEDIA: 223536270Swpaul case SIOCGIFMEDIA: 223650462Swpaul if (sc->tl_bitrate) 223750462Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 223850462Swpaul else { 223950462Swpaul struct mii_data *mii; 224050462Swpaul mii = device_get_softc(sc->tl_miibus); 224150462Swpaul error = ifmedia_ioctl(ifp, ifr, 224250462Swpaul &mii->mii_media, command); 224350462Swpaul } 224436270Swpaul break; 224536270Swpaul default: 2246106936Ssam error = ether_ioctl(ifp, command, data); 224736270Swpaul break; 224836270Swpaul } 224936270Swpaul 225036270Swpaul (void)splx(s); 225136270Swpaul 225236270Swpaul return(error); 225336270Swpaul} 225436270Swpaul 2255102336Salfredstatic void 2256102336Salfredtl_watchdog(ifp) 225736270Swpaul struct ifnet *ifp; 225836270Swpaul{ 225936270Swpaul struct tl_softc *sc; 226036270Swpaul 226136270Swpaul sc = ifp->if_softc; 226236270Swpaul 2263105599Sbrooks if_printf(ifp, "device timeout\n"); 226436270Swpaul 226536270Swpaul ifp->if_oerrors++; 226636270Swpaul 226750468Swpaul tl_softreset(sc, 1); 226836270Swpaul tl_init(sc); 226936270Swpaul 227036270Swpaul return; 227136270Swpaul} 227236270Swpaul 227336270Swpaul/* 227436270Swpaul * Stop the adapter and free any mbufs allocated to the 227536270Swpaul * RX and TX lists. 227636270Swpaul */ 2277102336Salfredstatic void 2278102336Salfredtl_stop(sc) 227936270Swpaul struct tl_softc *sc; 228036270Swpaul{ 228136270Swpaul register int i; 228236270Swpaul struct ifnet *ifp; 228336270Swpaul 228467087Swpaul TL_LOCK(sc); 228567087Swpaul 2286147256Sbrooks ifp = sc->tl_ifp; 228736270Swpaul 228836270Swpaul /* Stop the stats updater. */ 228936270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 229036270Swpaul 229136270Swpaul /* Stop the transmitter */ 229239583Swpaul CMD_CLR(sc, TL_CMD_RT); 229339583Swpaul CMD_SET(sc, TL_CMD_STOP); 229439583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 229536270Swpaul 229636270Swpaul /* Stop the receiver */ 229739583Swpaul CMD_SET(sc, TL_CMD_RT); 229839583Swpaul CMD_SET(sc, TL_CMD_STOP); 229939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 230036270Swpaul 230136270Swpaul /* 230236270Swpaul * Disable host interrupts. 230336270Swpaul */ 230439583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 230536270Swpaul 230636270Swpaul /* 230736270Swpaul * Clear list pointer. 230836270Swpaul */ 230939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 231036270Swpaul 231136270Swpaul /* 231236270Swpaul * Free the RX lists. 231336270Swpaul */ 231436270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 231536270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 231636270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 231736270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 231836270Swpaul } 231936270Swpaul } 232036270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 232136270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 232236270Swpaul 232336270Swpaul /* 232436270Swpaul * Free the TX list buffers. 232536270Swpaul */ 232636270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 232736270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 232836270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 232936270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 233036270Swpaul } 233136270Swpaul } 233236270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 233336270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 233436270Swpaul 2335148887Srwatson ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 233667087Swpaul TL_UNLOCK(sc); 233736270Swpaul 233836270Swpaul return; 233936270Swpaul} 234036270Swpaul 234136270Swpaul/* 234236270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 234336270Swpaul * get confused by errant DMAs when rebooting. 234436270Swpaul */ 2345102336Salfredstatic void 2346102336Salfredtl_shutdown(dev) 234748992Swpaul device_t dev; 234836270Swpaul{ 234939583Swpaul struct tl_softc *sc; 235036270Swpaul 235148992Swpaul sc = device_get_softc(dev); 235236270Swpaul 235339583Swpaul tl_stop(sc); 235436270Swpaul 235536270Swpaul return; 235636270Swpaul} 2357