if_tl.c revision 49010
136270Swpaul/* 236270Swpaul * Copyright (c) 1997, 1998 336270Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 436270Swpaul * 536270Swpaul * Redistribution and use in source and binary forms, with or without 636270Swpaul * modification, are permitted provided that the following conditions 736270Swpaul * are met: 836270Swpaul * 1. Redistributions of source code must retain the above copyright 936270Swpaul * notice, this list of conditions and the following disclaimer. 1036270Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1136270Swpaul * notice, this list of conditions and the following disclaimer in the 1236270Swpaul * documentation and/or other materials provided with the distribution. 1336270Swpaul * 3. All advertising materials mentioning features or use of this software 1436270Swpaul * must display the following acknowledgement: 1536270Swpaul * This product includes software developed by Bill Paul. 1636270Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1736270Swpaul * may be used to endorse or promote products derived from this software 1836270Swpaul * without specific prior written permission. 1936270Swpaul * 2036270Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2136270Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2236270Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2336270Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2436270Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2536270Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2636270Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2736270Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2836270Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2936270Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3036270Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3136270Swpaul * 3248992Swpaul * $Id: if_tl.c,v 1.34 1999/07/06 19:23:29 des Exp $ 3336270Swpaul */ 3436270Swpaul 3536270Swpaul/* 3636270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3736270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3836270Swpaul * the National Semiconductor DP83840A physical interface and the 3936270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 4036270Swpaul * 4139583Swpaul * Written using the following four documents: 4236270Swpaul * 4336270Swpaul * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) 4436270Swpaul * National Semiconductor DP83840A data sheet (www.national.com) 4536270Swpaul * Microchip Technology 24C02C data sheet (www.microchip.com) 4639583Swpaul * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) 4736270Swpaul * 4836270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4936270Swpaul * Electrical Engineering Department 5036270Swpaul * Columbia University, New York City 5136270Swpaul */ 5236270Swpaul 5336270Swpaul/* 5436270Swpaul * Some notes about the ThunderLAN: 5536270Swpaul * 5636270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5736270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5839583Swpaul * independent interface (MII) bus. The MII allows the ThunderLAN chip to 5936270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 6036270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 6136270Swpaul * to act as a complete ethernet interface. 6236270Swpaul * 6336270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6436270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6536270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6639583Swpaul * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters 6739583Swpaul * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in 6839583Swpaul * concert with the ThunderLAN's internal PHY to provide full 10/100 6939583Swpaul * support. This is cheaper than using a standalone external PHY for both 7039583Swpaul * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. 7139583Swpaul * A serial EEPROM is also attached to the ThunderLAN chip to provide 7239583Swpaul * power-up default register settings and for storing the adapter's 7339583Swpaul * station address. Although not supported by this driver, the ThunderLAN 7439583Swpaul * chip can also be connected to token ring PHYs. 7536270Swpaul * 7636270Swpaul * The ThunderLAN has a set of registers which can be used to issue 7739583Swpaul * commands, acknowledge interrupts, and to manipulate other internal 7836270Swpaul * registers on its DIO bus. The primary registers can be accessed 7936270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 8036270Swpaul * depending on how the card is configured during the PCI probing 8136270Swpaul * phase. It is even possible to have both PIO and memory mapped 8236270Swpaul * access turned on at the same time. 8336270Swpaul * 8436270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 8536270Swpaul * using frame 'lists.' A list structure looks more or less like this: 8636270Swpaul * 8736270Swpaul * struct tl_frag { 8836270Swpaul * u_int32_t fragment_address; 8936270Swpaul * u_int32_t fragment_size; 9036270Swpaul * }; 9136270Swpaul * struct tl_list { 9236270Swpaul * u_int32_t forward_pointer; 9336270Swpaul * u_int16_t cstat; 9436270Swpaul * u_int16_t frame_size; 9536270Swpaul * struct tl_frag fragments[10]; 9636270Swpaul * }; 9736270Swpaul * 9836270Swpaul * The forward pointer in the list header can be either a 0 or the address 9936270Swpaul * of another list, which allows several lists to be linked together. Each 10036270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 10136270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 10236270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 10336270Swpaul * addresses are physical memory addresses, not virtual. Note also that 10436270Swpaul * a single ethernet frame can not span lists: if the host wants to 10536270Swpaul * transmit a frame and the frame data is split up over more than 10 10636270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 10736270Swpaul * 10836270Swpaul * To receive frames, the driver sets up a number of lists and populates 10936270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 11036270Swpaul * When a frame is received, the chip will DMA it into the memory regions 11136270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 11236270Swpaul * frame interrupt' when done. The driver may choose to use only one 11336270Swpaul * fragment per list; this may result is slighltly less efficient use 11436270Swpaul * of memory in exchange for improving performance. 11536270Swpaul * 11636270Swpaul * To transmit frames, the driver again sets up lists and fragment 11736270Swpaul * descriptors, only this time the buffers contain frame data that 11836270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 11936270Swpaul * has transfered the data into its on-board SRAM, it will trigger a 12036270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 12136270Swpaul * interrupt when it reaches the end of the list. 12236270Swpaul */ 12336270Swpaul 12436270Swpaul/* 12536270Swpaul * Some notes about this driver: 12636270Swpaul * 12736270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 12836270Swpaul * reception, transmission and interrupt handling. The simplest approach 12936270Swpaul * is to use one list each for transmission and reception. In this mode, 13036270Swpaul * the ThunderLAN will generate two interrupts for every received frame 13136270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 13236270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 13336270Swpaul * performance to have to handle so many interrupts. 13436270Swpaul * 13536270Swpaul * Initially I wanted to create a circular list of receive buffers so 13636270Swpaul * that the ThunderLAN chip would think there was an infinitely long 13736270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 13836270Swpaul * doesn't work correctly under heavy load: while the manual says the 13936270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 14036270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 14136270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 14236270Swpaul * is that the chip might traverse the entire circular list and then wrap 14336270Swpaul * around before you have a chance to do anything about it. Consequently, 14436270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 14536270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 14636270Swpaul * is shifted to the end of the list. This gives the appearance of an 14736270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 14836270Swpaul * the chip and allow all of the lists to be filled up. 14936270Swpaul * 15036270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 15136270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 15236270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 15336270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 15436270Swpaul * 15536270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 15636270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 15736270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 15836270Swpaul * queue several frames in one shot, and only have to handle a total 15936270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 16036270Swpaul * are transmitted. Frame transmission is done directly out of the 16136270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 16236270Swpaul * The driver simply sets up the fragment descriptors in the transmit 16336270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 16436270Swpaul * 16536270Swpaul * Note that since the RX and TX lists themselves are always used 16636270Swpaul * only by the driver, the are malloc()ed once at driver initialization 16736270Swpaul * time and never free()ed. 16836270Swpaul * 16936270Swpaul * Also, in order to remain as platform independent as possible, this 17036270Swpaul * driver uses memory mapped register access to manipulate the card 17136270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 17236270Swpaul * (and related) instructions which are specific to the i386 platform. 17336270Swpaul * 17436270Swpaul * Using these techniques, this driver achieves very high performance 17536270Swpaul * by minimizing the amount of interrupts generated during large 17636270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 17736270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 17836270Swpaul * itself thereby reducing the load on the host CPU. 17936270Swpaul */ 18036270Swpaul 18148645Sdes#include "bpf.h" 18236270Swpaul 18336270Swpaul#include <sys/param.h> 18436270Swpaul#include <sys/systm.h> 18536270Swpaul#include <sys/sockio.h> 18636270Swpaul#include <sys/mbuf.h> 18736270Swpaul#include <sys/malloc.h> 18836270Swpaul#include <sys/kernel.h> 18936270Swpaul#include <sys/socket.h> 19036270Swpaul 19136270Swpaul#include <net/if.h> 19236270Swpaul#include <net/if_arp.h> 19336270Swpaul#include <net/ethernet.h> 19436270Swpaul#include <net/if_dl.h> 19536270Swpaul#include <net/if_media.h> 19636270Swpaul 19748645Sdes#if NBPF > 0 19836270Swpaul#include <net/bpf.h> 19936270Swpaul#endif 20036270Swpaul 20136270Swpaul#include <vm/vm.h> /* for vtophys */ 20236270Swpaul#include <vm/pmap.h> /* for vtophys */ 20336270Swpaul#include <machine/clock.h> /* for DELAY */ 20445155Swpaul#include <machine/bus_memio.h> 20545155Swpaul#include <machine/bus_pio.h> 20645155Swpaul#include <machine/bus.h> 20748992Swpaul#include <machine/resource.h> 20848992Swpaul#include <sys/bus.h> 20948992Swpaul#include <sys/rman.h> 21036270Swpaul 21136270Swpaul#include <pci/pcireg.h> 21236270Swpaul#include <pci/pcivar.h> 21336270Swpaul 21439957Swpaul/* 21539957Swpaul * Default to using PIO register access mode to pacify certain 21639957Swpaul * laptop docking stations with built-in ThunderLAN chips that 21739957Swpaul * don't seem to handle memory mapped mode properly. 21839957Swpaul */ 21939957Swpaul#define TL_USEIOSPACE 22039957Swpaul 22139583Swpaul/* #define TL_BACKGROUND_AUTONEG */ 22239583Swpaul 22336270Swpaul#include <pci/if_tlreg.h> 22436270Swpaul 22541591Sarchie#if !defined(lint) 22641591Sarchiestatic const char rcsid[] = 22748992Swpaul "$Id: if_tl.c,v 1.34 1999/07/06 19:23:29 des Exp $"; 22836270Swpaul#endif 22936270Swpaul 23036270Swpaul/* 23136270Swpaul * Various supported device vendors/types and their names. 23236270Swpaul */ 23336270Swpaul 23436270Swpaulstatic struct tl_type tl_devs[] = { 23536270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 23636270Swpaul "Texas Instruments ThunderLAN" }, 23736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 23836270Swpaul "Compaq Netelligent 10" }, 23936270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 24036270Swpaul "Compaq Netelligent 10/100" }, 24136270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 24236270Swpaul "Compaq Netelligent 10/100 Proliant" }, 24336270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 24436270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 24536270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 24636270Swpaul "Compaq NetFlex-3/P Integrated" }, 24736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 24836270Swpaul "Compaq NetFlex-3/P" }, 24936270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 25036270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 25137626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, 25237626Swpaul "Compaq Netelligent 10/100 TX Embedded UTP" }, 25337626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, 25437626Swpaul "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, 25537626Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, 25637626Swpaul "Compaq Netelligent 10/100 TX UTP" }, 25737626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, 25837626Swpaul "Olicom OC-2183/2185" }, 25937626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, 26037626Swpaul "Olicom OC-2325" }, 26137626Swpaul { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, 26237626Swpaul "Olicom OC-2326 10/100 TX UTP" }, 26336270Swpaul { 0, 0, NULL } 26436270Swpaul}; 26536270Swpaul 26636270Swpaul/* 26736270Swpaul * Various supported PHY vendors/types and their names. Note that 26836270Swpaul * this driver will work with pretty much any MII-compliant PHY, 26936270Swpaul * so failure to positively identify the chip is not a fatal error. 27036270Swpaul */ 27136270Swpaul 27236270Swpaulstatic struct tl_type tl_phys[] = { 27336270Swpaul { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, 27436270Swpaul { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, 27536270Swpaul { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, 27636270Swpaul { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, 27736270Swpaul { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, 27836270Swpaul { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, 27936270Swpaul { 0, 0, "<MII-compliant physical interface>" } 28036270Swpaul}; 28136270Swpaul 28248992Swpaulstatic int tl_probe __P((device_t)); 28348992Swpaulstatic int tl_attach __P((device_t)); 28448992Swpaulstatic int tl_detach __P((device_t)); 28539583Swpaulstatic int tl_attach_phy __P((struct tl_softc *)); 28636270Swpaulstatic int tl_intvec_rxeoc __P((void *, u_int32_t)); 28736270Swpaulstatic int tl_intvec_txeoc __P((void *, u_int32_t)); 28836270Swpaulstatic int tl_intvec_txeof __P((void *, u_int32_t)); 28936270Swpaulstatic int tl_intvec_rxeof __P((void *, u_int32_t)); 29036270Swpaulstatic int tl_intvec_adchk __P((void *, u_int32_t)); 29136270Swpaulstatic int tl_intvec_netsts __P((void *, u_int32_t)); 29236270Swpaul 29337626Swpaulstatic int tl_newbuf __P((struct tl_softc *, 29437626Swpaul struct tl_chain_onefrag *)); 29536270Swpaulstatic void tl_stats_update __P((void *)); 29636270Swpaulstatic int tl_encap __P((struct tl_softc *, struct tl_chain *, 29736270Swpaul struct mbuf *)); 29836270Swpaul 29936270Swpaulstatic void tl_intr __P((void *)); 30036270Swpaulstatic void tl_start __P((struct ifnet *)); 30136735Sdfrstatic int tl_ioctl __P((struct ifnet *, u_long, caddr_t)); 30236270Swpaulstatic void tl_init __P((void *)); 30336270Swpaulstatic void tl_stop __P((struct tl_softc *)); 30436270Swpaulstatic void tl_watchdog __P((struct ifnet *)); 30548992Swpaulstatic void tl_shutdown __P((device_t)); 30636270Swpaulstatic int tl_ifmedia_upd __P((struct ifnet *)); 30736270Swpaulstatic void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 30836270Swpaul 30941656Swpaulstatic u_int8_t tl_eeprom_putbyte __P((struct tl_softc *, int)); 31039583Swpaulstatic u_int8_t tl_eeprom_getbyte __P((struct tl_softc *, 31141656Swpaul int, u_int8_t *)); 31239583Swpaulstatic int tl_read_eeprom __P((struct tl_softc *, caddr_t, int, int)); 31336270Swpaul 31439583Swpaulstatic void tl_mii_sync __P((struct tl_softc *)); 31539583Swpaulstatic void tl_mii_send __P((struct tl_softc *, u_int32_t, int)); 31639583Swpaulstatic int tl_mii_readreg __P((struct tl_softc *, struct tl_mii_frame *)); 31739583Swpaulstatic int tl_mii_writereg __P((struct tl_softc *, struct tl_mii_frame *)); 31836270Swpaulstatic u_int16_t tl_phy_readreg __P((struct tl_softc *, int)); 31941656Swpaulstatic void tl_phy_writereg __P((struct tl_softc *, int, int)); 32036270Swpaul 32136270Swpaulstatic void tl_autoneg __P((struct tl_softc *, int, int)); 32236270Swpaulstatic void tl_setmode __P((struct tl_softc *, int)); 32341656Swpaulstatic int tl_calchash __P((caddr_t)); 32436270Swpaulstatic void tl_setmulti __P((struct tl_softc *)); 32541656Swpaulstatic void tl_setfilt __P((struct tl_softc *, caddr_t, int)); 32639583Swpaulstatic void tl_softreset __P((struct tl_softc *, int)); 32739583Swpaulstatic void tl_hardreset __P((struct tl_softc *)); 32836270Swpaulstatic int tl_list_rx_init __P((struct tl_softc *)); 32936270Swpaulstatic int tl_list_tx_init __P((struct tl_softc *)); 33036270Swpaul 33141656Swpaulstatic u_int8_t tl_dio_read8 __P((struct tl_softc *, int)); 33241656Swpaulstatic u_int16_t tl_dio_read16 __P((struct tl_softc *, int)); 33341656Swpaulstatic u_int32_t tl_dio_read32 __P((struct tl_softc *, int)); 33441656Swpaulstatic void tl_dio_write8 __P((struct tl_softc *, int, int)); 33541656Swpaulstatic void tl_dio_write16 __P((struct tl_softc *, int, int)); 33641656Swpaulstatic void tl_dio_write32 __P((struct tl_softc *, int, int)); 33741656Swpaulstatic void tl_dio_setbit __P((struct tl_softc *, int, int)); 33841656Swpaulstatic void tl_dio_clrbit __P((struct tl_softc *, int, int)); 33941656Swpaulstatic void tl_dio_setbit16 __P((struct tl_softc *, int, int)); 34041656Swpaulstatic void tl_dio_clrbit16 __P((struct tl_softc *, int, int)); 34139583Swpaul 34249010Swpaul#ifdef TL_USEIOSPACE 34349010Swpaul#define TL_RES SYS_RES_IOPORT 34449010Swpaul#define TL_RID TL_PCI_LOIO 34549010Swpaul#else 34649010Swpaul#define TL_RES SYS_RES_MEMORY 34749010Swpaul#define TL_RID TL_PCI_LOMEM 34849010Swpaul#endif 34949010Swpaul 35048992Swpaulstatic device_method_t tl_methods[] = { 35148992Swpaul /* Device interface */ 35248992Swpaul DEVMETHOD(device_probe, tl_probe), 35348992Swpaul DEVMETHOD(device_attach, tl_attach), 35448992Swpaul DEVMETHOD(device_detach, tl_detach), 35548992Swpaul DEVMETHOD(device_shutdown, tl_shutdown), 35648992Swpaul { 0, 0 } 35748992Swpaul}; 35848992Swpaul 35948992Swpaulstatic driver_t tl_driver = { 36048992Swpaul "tl", 36148992Swpaul tl_methods, 36248992Swpaul sizeof(struct tl_softc) 36348992Swpaul}; 36448992Swpaul 36548992Swpaulstatic devclass_t tl_devclass; 36648992Swpaul 36748992SwpaulDRIVER_MODULE(tl, pci, tl_driver, tl_devclass, 0, 0); 36848992Swpaul 36939583Swpaulstatic u_int8_t tl_dio_read8(sc, reg) 37041656Swpaul struct tl_softc *sc; 37141656Swpaul int reg; 37239583Swpaul{ 37339583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 37439583Swpaul return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3))); 37539583Swpaul} 37639583Swpaul 37739583Swpaulstatic u_int16_t tl_dio_read16(sc, reg) 37841656Swpaul struct tl_softc *sc; 37941656Swpaul int reg; 38039583Swpaul{ 38139583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 38239583Swpaul return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3))); 38339583Swpaul} 38439583Swpaul 38539583Swpaulstatic u_int32_t tl_dio_read32(sc, reg) 38641656Swpaul struct tl_softc *sc; 38741656Swpaul int reg; 38839583Swpaul{ 38939583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39039583Swpaul return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3))); 39139583Swpaul} 39239583Swpaul 39339583Swpaulstatic void tl_dio_write8(sc, reg, val) 39441656Swpaul struct tl_softc *sc; 39541656Swpaul int reg; 39641656Swpaul int val; 39739583Swpaul{ 39839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 39939583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); 40039583Swpaul return; 40139583Swpaul} 40239583Swpaul 40339583Swpaulstatic void tl_dio_write16(sc, reg, val) 40441656Swpaul struct tl_softc *sc; 40541656Swpaul int reg; 40641656Swpaul int val; 40739583Swpaul{ 40839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 40939583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); 41039583Swpaul return; 41139583Swpaul} 41239583Swpaul 41339583Swpaulstatic void tl_dio_write32(sc, reg, val) 41441656Swpaul struct tl_softc *sc; 41541656Swpaul int reg; 41641656Swpaul int val; 41739583Swpaul{ 41839583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 41939583Swpaul CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); 42039583Swpaul return; 42139583Swpaul} 42239583Swpaul 42339583Swpaulstatic void tl_dio_setbit(sc, reg, bit) 42441656Swpaul struct tl_softc *sc; 42541656Swpaul int reg; 42641656Swpaul int bit; 42739583Swpaul{ 42839583Swpaul u_int8_t f; 42939583Swpaul 43039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 43139583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 43239583Swpaul f |= bit; 43339583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 43439583Swpaul 43539583Swpaul return; 43639583Swpaul} 43739583Swpaul 43839583Swpaulstatic void tl_dio_clrbit(sc, reg, bit) 43941656Swpaul struct tl_softc *sc; 44041656Swpaul int reg; 44141656Swpaul int bit; 44239583Swpaul{ 44339583Swpaul u_int8_t f; 44439583Swpaul 44539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 44639583Swpaul f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); 44739583Swpaul f &= ~bit; 44839583Swpaul CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); 44939583Swpaul 45039583Swpaul return; 45139583Swpaul} 45239583Swpaul 45339583Swpaulstatic void tl_dio_setbit16(sc, reg, bit) 45441656Swpaul struct tl_softc *sc; 45541656Swpaul int reg; 45641656Swpaul int bit; 45739583Swpaul{ 45839583Swpaul u_int16_t f; 45939583Swpaul 46039583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 46139583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 46239583Swpaul f |= bit; 46339583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 46439583Swpaul 46539583Swpaul return; 46639583Swpaul} 46739583Swpaul 46839583Swpaulstatic void tl_dio_clrbit16(sc, reg, bit) 46941656Swpaul struct tl_softc *sc; 47041656Swpaul int reg; 47141656Swpaul int bit; 47239583Swpaul{ 47339583Swpaul u_int16_t f; 47439583Swpaul 47539583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, reg); 47639583Swpaul f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); 47739583Swpaul f &= ~bit; 47839583Swpaul CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); 47939583Swpaul 48039583Swpaul return; 48139583Swpaul} 48239583Swpaul 48336270Swpaul/* 48436270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 48536270Swpaul */ 48639583Swpaulstatic u_int8_t tl_eeprom_putbyte(sc, byte) 48739583Swpaul struct tl_softc *sc; 48841656Swpaul int byte; 48936270Swpaul{ 49036270Swpaul register int i, ack = 0; 49136270Swpaul 49236270Swpaul /* 49336270Swpaul * Make sure we're in TX mode. 49436270Swpaul */ 49539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ETXEN); 49636270Swpaul 49736270Swpaul /* 49836270Swpaul * Feed in each bit and stobe the clock. 49936270Swpaul */ 50036270Swpaul for (i = 0x80; i; i >>= 1) { 50136270Swpaul if (byte & i) { 50239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_EDATA); 50336270Swpaul } else { 50439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_EDATA); 50536270Swpaul } 50639583Swpaul DELAY(1); 50739583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 50839583Swpaul DELAY(1); 50939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 51036270Swpaul } 51136270Swpaul 51236270Swpaul /* 51336270Swpaul * Turn off TX mode. 51436270Swpaul */ 51539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 51636270Swpaul 51736270Swpaul /* 51836270Swpaul * Check for ack. 51936270Swpaul */ 52039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 52139583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA; 52239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 52336270Swpaul 52436270Swpaul return(ack); 52536270Swpaul} 52636270Swpaul 52736270Swpaul/* 52836270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 52936270Swpaul */ 53039583Swpaulstatic u_int8_t tl_eeprom_getbyte(sc, addr, dest) 53139583Swpaul struct tl_softc *sc; 53241656Swpaul int addr; 53336270Swpaul u_int8_t *dest; 53436270Swpaul{ 53536270Swpaul register int i; 53636270Swpaul u_int8_t byte = 0; 53736270Swpaul 53839583Swpaul tl_dio_write8(sc, TL_NETSIO, 0); 53939583Swpaul 54036270Swpaul EEPROM_START; 54139583Swpaul 54236270Swpaul /* 54336270Swpaul * Send write control code to EEPROM. 54436270Swpaul */ 54539583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_WRITE)) { 54639583Swpaul printf("tl%d: failed to send write command, status: %x\n", 54739583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 54836270Swpaul return(1); 54939583Swpaul } 55036270Swpaul 55136270Swpaul /* 55236270Swpaul * Send address of byte we want to read. 55336270Swpaul */ 55439583Swpaul if (tl_eeprom_putbyte(sc, addr)) { 55539583Swpaul printf("tl%d: failed to send address, status: %x\n", 55639583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 55736270Swpaul return(1); 55839583Swpaul } 55936270Swpaul 56036270Swpaul EEPROM_STOP; 56136270Swpaul EEPROM_START; 56236270Swpaul /* 56336270Swpaul * Send read control code to EEPROM. 56436270Swpaul */ 56539583Swpaul if (tl_eeprom_putbyte(sc, EEPROM_CTL_READ)) { 56639583Swpaul printf("tl%d: failed to send write command, status: %x\n", 56739583Swpaul sc->tl_unit, tl_dio_read8(sc, TL_NETSIO)); 56836270Swpaul return(1); 56939583Swpaul } 57036270Swpaul 57136270Swpaul /* 57236270Swpaul * Start reading bits from EEPROM. 57336270Swpaul */ 57439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ETXEN); 57536270Swpaul for (i = 0x80; i; i >>= 1) { 57639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_ECLOK); 57739583Swpaul DELAY(1); 57839583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_EDATA) 57936270Swpaul byte |= i; 58039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_ECLOK); 58136501Swpaul DELAY(1); 58236270Swpaul } 58336270Swpaul 58436270Swpaul EEPROM_STOP; 58536270Swpaul 58636270Swpaul /* 58736270Swpaul * No ACK generated for read, so just return byte. 58836270Swpaul */ 58936270Swpaul 59036270Swpaul *dest = byte; 59136270Swpaul 59236270Swpaul return(0); 59336270Swpaul} 59436270Swpaul 59539583Swpaul/* 59639583Swpaul * Read a sequence of bytes from the EEPROM. 59739583Swpaul */ 59839583Swpaulstatic int tl_read_eeprom(sc, dest, off, cnt) 59939583Swpaul struct tl_softc *sc; 60039583Swpaul caddr_t dest; 60139583Swpaul int off; 60239583Swpaul int cnt; 60336270Swpaul{ 60439583Swpaul int err = 0, i; 60539583Swpaul u_int8_t byte = 0; 60639583Swpaul 60739583Swpaul for (i = 0; i < cnt; i++) { 60839583Swpaul err = tl_eeprom_getbyte(sc, off + i, &byte); 60939583Swpaul if (err) 61039583Swpaul break; 61139583Swpaul *(dest + i) = byte; 61239583Swpaul } 61339583Swpaul 61439583Swpaul return(err ? 1 : 0); 61539583Swpaul} 61639583Swpaul 61739583Swpaulstatic void tl_mii_sync(sc) 61839583Swpaul struct tl_softc *sc; 61939583Swpaul{ 62036270Swpaul register int i; 62136270Swpaul 62239583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 62336270Swpaul 62436270Swpaul for (i = 0; i < 32; i++) { 62539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 62639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 62736270Swpaul } 62836270Swpaul 62936270Swpaul return; 63036270Swpaul} 63136270Swpaul 63239583Swpaulstatic void tl_mii_send(sc, bits, cnt) 63339583Swpaul struct tl_softc *sc; 63436270Swpaul u_int32_t bits; 63536270Swpaul int cnt; 63636270Swpaul{ 63736270Swpaul int i; 63836270Swpaul 63936270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 64039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 64136270Swpaul if (bits & i) { 64239583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MDATA); 64336270Swpaul } else { 64439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MDATA); 64536270Swpaul } 64639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 64736270Swpaul } 64836270Swpaul} 64936270Swpaul 65039583Swpaulstatic int tl_mii_readreg(sc, frame) 65139583Swpaul struct tl_softc *sc; 65236270Swpaul struct tl_mii_frame *frame; 65336270Swpaul 65436270Swpaul{ 65536270Swpaul int i, ack, s; 65636270Swpaul int minten = 0; 65736270Swpaul 65836270Swpaul s = splimp(); 65936270Swpaul 66039583Swpaul tl_mii_sync(sc); 66136270Swpaul 66236270Swpaul /* 66336270Swpaul * Set up frame for RX. 66436270Swpaul */ 66536270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66636270Swpaul frame->mii_opcode = TL_MII_READOP; 66736270Swpaul frame->mii_turnaround = 0; 66836270Swpaul frame->mii_data = 0; 66936270Swpaul 67036270Swpaul /* 67136270Swpaul * Turn off MII interrupt by forcing MINTEN low. 67236270Swpaul */ 67339583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 67436270Swpaul if (minten) { 67539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 67636270Swpaul } 67736270Swpaul 67836270Swpaul /* 67936270Swpaul * Turn on data xmit. 68036270Swpaul */ 68139583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 68236270Swpaul 68336270Swpaul /* 68436270Swpaul * Send command/address info. 68536270Swpaul */ 68639583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 68739583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 68839583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 68939583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 69036270Swpaul 69136270Swpaul /* 69236270Swpaul * Turn off xmit. 69336270Swpaul */ 69439583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 69536270Swpaul 69636270Swpaul /* Idle bit */ 69739583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 69839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 69936270Swpaul 70036270Swpaul /* Check for ack */ 70139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 70239583Swpaul ack = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA; 70336270Swpaul 70436270Swpaul /* Complete the cycle */ 70539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 70636270Swpaul 70736270Swpaul /* 70836270Swpaul * Now try reading data bits. If the ack failed, we still 70936270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 71036270Swpaul */ 71136270Swpaul if (ack) { 71236270Swpaul for(i = 0; i < 16; i++) { 71339583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 71439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 71536270Swpaul } 71636270Swpaul goto fail; 71736270Swpaul } 71836270Swpaul 71936270Swpaul for (i = 0x8000; i; i >>= 1) { 72039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 72136270Swpaul if (!ack) { 72239583Swpaul if (tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MDATA) 72336270Swpaul frame->mii_data |= i; 72436270Swpaul } 72539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 72636270Swpaul } 72736270Swpaul 72836270Swpaulfail: 72936270Swpaul 73039583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 73139583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 73236270Swpaul 73336270Swpaul /* Reenable interrupts */ 73436270Swpaul if (minten) { 73539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 73636270Swpaul } 73736270Swpaul 73836270Swpaul splx(s); 73936270Swpaul 74036270Swpaul if (ack) 74136270Swpaul return(1); 74236270Swpaul return(0); 74336270Swpaul} 74436270Swpaul 74539583Swpaulstatic int tl_mii_writereg(sc, frame) 74639583Swpaul struct tl_softc *sc; 74736270Swpaul struct tl_mii_frame *frame; 74836270Swpaul 74936270Swpaul{ 75036270Swpaul int s; 75136270Swpaul int minten; 75236270Swpaul 75339583Swpaul tl_mii_sync(sc); 75436270Swpaul 75536270Swpaul s = splimp(); 75636270Swpaul /* 75736270Swpaul * Set up frame for TX. 75836270Swpaul */ 75936270Swpaul 76036270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 76136270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 76236270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 76336270Swpaul 76436270Swpaul /* 76536270Swpaul * Turn off MII interrupt by forcing MINTEN low. 76636270Swpaul */ 76739583Swpaul minten = tl_dio_read8(sc, TL_NETSIO) & TL_SIO_MINTEN; 76836270Swpaul if (minten) { 76939583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 77036270Swpaul } 77136270Swpaul 77236270Swpaul /* 77336270Swpaul * Turn on data output. 77436270Swpaul */ 77539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MTXEN); 77636270Swpaul 77739583Swpaul tl_mii_send(sc, frame->mii_stdelim, 2); 77839583Swpaul tl_mii_send(sc, frame->mii_opcode, 2); 77939583Swpaul tl_mii_send(sc, frame->mii_phyaddr, 5); 78039583Swpaul tl_mii_send(sc, frame->mii_regaddr, 5); 78139583Swpaul tl_mii_send(sc, frame->mii_turnaround, 2); 78239583Swpaul tl_mii_send(sc, frame->mii_data, 16); 78336270Swpaul 78439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MCLK); 78539583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MCLK); 78636270Swpaul 78736270Swpaul /* 78836270Swpaul * Turn off xmit. 78936270Swpaul */ 79039583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MTXEN); 79136270Swpaul 79236270Swpaul /* Reenable interrupts */ 79336270Swpaul if (minten) 79439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 79536270Swpaul 79636270Swpaul splx(s); 79736270Swpaul 79836270Swpaul return(0); 79936270Swpaul} 80036270Swpaul 80136270Swpaulstatic u_int16_t tl_phy_readreg(sc, reg) 80236270Swpaul struct tl_softc *sc; 80336270Swpaul int reg; 80436270Swpaul{ 80536270Swpaul struct tl_mii_frame frame; 80636270Swpaul 80736270Swpaul bzero((char *)&frame, sizeof(frame)); 80836270Swpaul 80936270Swpaul frame.mii_phyaddr = sc->tl_phy_addr; 81036270Swpaul frame.mii_regaddr = reg; 81139583Swpaul tl_mii_readreg(sc, &frame); 81236270Swpaul 81336270Swpaul /* Reenable MII interrupts, just in case. */ 81439583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 81536270Swpaul 81636270Swpaul return(frame.mii_data); 81736270Swpaul} 81836270Swpaul 81936270Swpaulstatic void tl_phy_writereg(sc, reg, data) 82036270Swpaul struct tl_softc *sc; 82141656Swpaul int reg; 82241656Swpaul int data; 82336270Swpaul{ 82436270Swpaul struct tl_mii_frame frame; 82536270Swpaul 82636270Swpaul bzero((char *)&frame, sizeof(frame)); 82736270Swpaul 82836270Swpaul frame.mii_phyaddr = sc->tl_phy_addr; 82936270Swpaul frame.mii_regaddr = reg; 83036270Swpaul frame.mii_data = data; 83136270Swpaul 83239583Swpaul tl_mii_writereg(sc, &frame); 83336270Swpaul 83436270Swpaul /* Reenable MII interrupts, just in case. */ 83539583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 83636270Swpaul 83736270Swpaul return; 83836270Swpaul} 83936270Swpaul 84036270Swpaul/* 84136270Swpaul * Initiate autonegotiation with a link partner. 84236270Swpaul * 84336270Swpaul * Note that the Texas Instruments ThunderLAN programmer's guide 84436270Swpaul * fails to mention one very important point about autonegotiation. 84536270Swpaul * Autonegotiation is done largely by the PHY, independent of the 84636270Swpaul * ThunderLAN chip itself: the PHY sets the flags in the BMCR 84736270Swpaul * register to indicate what modes were selected and if link status 84836270Swpaul * is good. In fact, the PHY does pretty much all of the work itself, 84936270Swpaul * except for one small detail. 85036270Swpaul * 85136270Swpaul * The PHY may negotiate a full-duplex of half-duplex link, and set 85236270Swpaul * the PHY_BMCR_DUPLEX bit accordingly, but the ThunderLAN's 'NetCommand' 85336270Swpaul * register _also_ has a half-duplex/full-duplex bit, and you MUST ALSO 85436270Swpaul * SET THIS BIT MANUALLY TO CORRESPOND TO THE MODE SELECTED FOR THE PHY! 85536270Swpaul * In other words, both the ThunderLAN chip and the PHY have to be 85636270Swpaul * programmed for full-duplex mode in order for full-duplex to actually 85736270Swpaul * work. So in order for autonegotiation to really work right, we have 85836270Swpaul * to wait for the link to come up, check the BMCR register, then set 85936270Swpaul * the ThunderLAN for full or half-duplex as needed. 86036270Swpaul * 86136270Swpaul * I struggled for two days to figure this out, so I'm making a point 86236270Swpaul * of drawing attention to this fact. I think it's very strange that 86336270Swpaul * the ThunderLAN doesn't automagically track the duplex state of the 86436270Swpaul * PHY, but there you have it. 86536270Swpaul * 86636270Swpaul * Also when, using a National Semiconductor DP83840A PHY, we have to 86736270Swpaul * allow a full three seconds for autonegotiation to complete. So what 86836270Swpaul * we do is flip the autonegotiation restart bit, then set a timeout 86936270Swpaul * to wake us up in three seconds to check the link state. 87039583Swpaul * 87139583Swpaul * Note that there are some versions of the Olicom 2326 that use a 87239583Swpaul * Micro Linear ML6692 100BaseTX PHY. This particular PHY is designed 87339583Swpaul * to provide 100BaseTX support only, but can be used with a controller 87439583Swpaul * that supports an internal 10Mbps PHY to provide a complete 87539583Swpaul * 10/100Mbps solution. However, the ML6692 does not have vendor and 87639583Swpaul * device ID registers, and hence always shows up with a vendor/device 87739583Swpaul * ID of 0. 87839583Swpaul * 87939583Swpaul * We detect this configuration by checking the phy vendor ID in the 88039583Swpaul * softc structure. If it's a zero, and we're negotiating a high-speed 88139583Swpaul * mode, then we turn off the internal PHY. If it's a zero and we've 88239583Swpaul * negotiated a high-speed mode, we turn on the internal PHY. Note 88339583Swpaul * that to make things even more fun, we have to make extra sure that 88439583Swpaul * the loopback bit in the internal PHY's control register is turned 88539583Swpaul * off. 88636270Swpaul */ 88736270Swpaulstatic void tl_autoneg(sc, flag, verbose) 88836270Swpaul struct tl_softc *sc; 88936270Swpaul int flag; 89036270Swpaul int verbose; 89136270Swpaul{ 89238030Swpaul u_int16_t phy_sts = 0, media = 0, advert, ability; 89336270Swpaul struct ifnet *ifp; 89436270Swpaul struct ifmedia *ifm; 89536270Swpaul 89636270Swpaul ifm = &sc->ifmedia; 89736270Swpaul ifp = &sc->arpcom.ac_if; 89836270Swpaul 89936270Swpaul /* 90036270Swpaul * First, see if autoneg is supported. If not, there's 90136270Swpaul * no point in continuing. 90236270Swpaul */ 90336270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 90436270Swpaul if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { 90536270Swpaul if (verbose) 90636270Swpaul printf("tl%d: autonegotiation not supported\n", 90736270Swpaul sc->tl_unit); 90836270Swpaul return; 90936270Swpaul } 91036270Swpaul 91136270Swpaul switch (flag) { 91236270Swpaul case TL_FLAG_FORCEDELAY: 91336270Swpaul /* 91436270Swpaul * XXX Never use this option anywhere but in the probe 91536270Swpaul * routine: making the kernel stop dead in its tracks 91636270Swpaul * for three whole seconds after we've gone multi-user 91736270Swpaul * is really bad manners. 91836270Swpaul */ 91938030Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 92038030Swpaul DELAY(500); 92136270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMCR); 92236270Swpaul phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; 92336270Swpaul tl_phy_writereg(sc, PHY_BMCR, phy_sts); 92439583Swpaul DELAY(5000000); 92536270Swpaul break; 92636270Swpaul case TL_FLAG_SCHEDDELAY: 92737626Swpaul /* 92837626Swpaul * Wait for the transmitter to go idle before starting 92937626Swpaul * an autoneg session, otherwise tl_start() may clobber 93037626Swpaul * our timeout, and we don't want to allow transmission 93137626Swpaul * during an autoneg session since that can screw it up. 93237626Swpaul */ 93337626Swpaul if (!sc->tl_txeoc) { 93437626Swpaul sc->tl_want_auto = 1; 93537626Swpaul return; 93637626Swpaul } 93738030Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 93838030Swpaul DELAY(500); 93936270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMCR); 94036270Swpaul phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; 94136270Swpaul tl_phy_writereg(sc, PHY_BMCR, phy_sts); 94239583Swpaul ifp->if_timer = 5; 94336270Swpaul sc->tl_autoneg = 1; 94437626Swpaul sc->tl_want_auto = 0; 94536270Swpaul return; 94636270Swpaul case TL_FLAG_DELAYTIMEO: 94736270Swpaul ifp->if_timer = 0; 94836270Swpaul sc->tl_autoneg = 0; 94936270Swpaul break; 95036270Swpaul default: 95139583Swpaul printf("tl%d: invalid autoneg flag: %d\n", sc->tl_unit, flag); 95236270Swpaul return; 95336270Swpaul } 95436270Swpaul 95536270Swpaul /* 95636270Swpaul * Read the BMSR register twice: the LINKSTAT bit is a 95736270Swpaul * latching bit. 95836270Swpaul */ 95936270Swpaul tl_phy_readreg(sc, PHY_BMSR); 96036270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 96136270Swpaul if (phy_sts & PHY_BMSR_AUTONEGCOMP) { 96236270Swpaul if (verbose) 96336270Swpaul printf("tl%d: autoneg complete, ", sc->tl_unit); 96436270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 96536270Swpaul } else { 96636270Swpaul if (verbose) 96736270Swpaul printf("tl%d: autoneg not complete, ", sc->tl_unit); 96836270Swpaul } 96936270Swpaul 97036270Swpaul /* Link is good. Report modes and set duplex mode. */ 97136270Swpaul if (phy_sts & PHY_BMSR_LINKSTAT) { 97236270Swpaul if (verbose) 97336270Swpaul printf("link status good "); 97438030Swpaul 97538030Swpaul advert = tl_phy_readreg(sc, TL_PHY_ANAR); 97638030Swpaul ability = tl_phy_readreg(sc, TL_PHY_LPAR); 97736270Swpaul media = tl_phy_readreg(sc, PHY_BMCR); 97836270Swpaul 97938030Swpaul /* 98038030Swpaul * Be sure to turn off the ISOLATE and 98138030Swpaul * LOOPBACK bits in the control register, 98238030Swpaul * otherwise we may not be able to communicate. 98338030Swpaul */ 98438030Swpaul media &= ~(PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); 98536270Swpaul /* Set the DUPLEX bit in the NetCmd register accordingly. */ 98638030Swpaul if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { 98738030Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_T4; 98839583Swpaul media |= PHY_BMCR_SPEEDSEL; 98939583Swpaul media &= ~PHY_BMCR_DUPLEX; 99036270Swpaul if (verbose) 99138030Swpaul printf("(100baseT4)\n"); 99238030Swpaul } else if (advert & PHY_ANAR_100BTXFULL && 99338030Swpaul ability & PHY_ANAR_100BTXFULL) { 99438030Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; 99538030Swpaul media |= PHY_BMCR_SPEEDSEL; 99638030Swpaul media |= PHY_BMCR_DUPLEX; 99736270Swpaul if (verbose) 99838030Swpaul printf("(full-duplex, 100Mbps)\n"); 99938030Swpaul } else if (advert & PHY_ANAR_100BTXHALF && 100038030Swpaul ability & PHY_ANAR_100BTXHALF) { 100138030Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; 100238030Swpaul media |= PHY_BMCR_SPEEDSEL; 100338030Swpaul media &= ~PHY_BMCR_DUPLEX; 100436270Swpaul if (verbose) 100538030Swpaul printf("(half-duplex, 100Mbps)\n"); 100638030Swpaul } else if (advert & PHY_ANAR_10BTFULL && 100738030Swpaul ability & PHY_ANAR_10BTFULL) { 100838030Swpaul ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; 100938030Swpaul media &= ~PHY_BMCR_SPEEDSEL; 101038030Swpaul media |= PHY_BMCR_DUPLEX; 101138030Swpaul if (verbose) 101238030Swpaul printf("(full-duplex, 10Mbps)\n"); 101336270Swpaul } else { 101438030Swpaul ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; 101538030Swpaul media &= ~PHY_BMCR_SPEEDSEL; 101638030Swpaul media &= ~PHY_BMCR_DUPLEX; 101736270Swpaul if (verbose) 101838030Swpaul printf("(half-duplex, 10Mbps)\n"); 101936270Swpaul } 102039583Swpaul 102139583Swpaul if (media & PHY_BMCR_DUPLEX) 102239583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 102339583Swpaul else 102439583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 102539583Swpaul 102636270Swpaul media &= ~PHY_BMCR_AUTONEGENBL; 102736270Swpaul tl_phy_writereg(sc, PHY_BMCR, media); 102836270Swpaul } else { 102936270Swpaul if (verbose) 103036270Swpaul printf("no carrier\n"); 103136270Swpaul } 103236270Swpaul 103338030Swpaul tl_init(sc); 103438030Swpaul 103537626Swpaul if (sc->tl_tx_pend) { 103637626Swpaul sc->tl_autoneg = 0; 103737626Swpaul sc->tl_tx_pend = 0; 103837626Swpaul tl_start(ifp); 103937626Swpaul } 104037626Swpaul 104136270Swpaul return; 104236270Swpaul} 104336270Swpaul 104436270Swpaul/* 104536270Swpaul * Set speed and duplex mode. Also program autoneg advertisements 104636270Swpaul * accordingly. 104736270Swpaul */ 104836270Swpaulstatic void tl_setmode(sc, media) 104936270Swpaul struct tl_softc *sc; 105036270Swpaul int media; 105136270Swpaul{ 105239583Swpaul u_int16_t bmcr; 105336270Swpaul 105445155Swpaul if (sc->tl_bitrate) { 105545155Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 105645155Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 105745155Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 105845155Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD1); 105945155Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 106045155Swpaul tl_dio_clrbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 106145155Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 106245155Swpaul } else { 106345155Swpaul tl_dio_setbit(sc, TL_ACOMMIT, TL_AC_MTXD3); 106445155Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 106545155Swpaul } 106645155Swpaul } 106745155Swpaul return; 106845155Swpaul } 106945155Swpaul 107036270Swpaul bmcr = tl_phy_readreg(sc, PHY_BMCR); 107136270Swpaul 107236270Swpaul bmcr &= ~(PHY_BMCR_SPEEDSEL|PHY_BMCR_DUPLEX|PHY_BMCR_AUTONEGENBL| 107338030Swpaul PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); 107436270Swpaul 107536270Swpaul if (IFM_SUBTYPE(media) == IFM_LOOP) 107636270Swpaul bmcr |= PHY_BMCR_LOOPBK; 107736270Swpaul 107836270Swpaul if (IFM_SUBTYPE(media) == IFM_AUTO) 107936270Swpaul bmcr |= PHY_BMCR_AUTONEGENBL; 108036270Swpaul 108139583Swpaul /* 108239583Swpaul * The ThunderLAN's internal PHY has an AUI transceiver 108339583Swpaul * that can be selected. This is usually attached to a 108439583Swpaul * 10base2/BNC port. In order to activate this port, we 108539583Swpaul * have to set the AUISEL bit in the internal PHY's 108639583Swpaul * special control register. 108739583Swpaul */ 108838030Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) { 108939583Swpaul u_int16_t addr, ctl; 109039583Swpaul addr = sc->tl_phy_addr; 109139583Swpaul sc->tl_phy_addr = TL_PHYADDR_MAX; 109239583Swpaul ctl = tl_phy_readreg(sc, TL_PHY_CTL); 109336270Swpaul ctl |= PHY_CTL_AUISEL; 109438030Swpaul tl_phy_writereg(sc, TL_PHY_CTL, ctl); 109539583Swpaul tl_phy_writereg(sc, PHY_BMCR, bmcr); 109639583Swpaul sc->tl_phy_addr = addr; 109739583Swpaul bmcr |= PHY_BMCR_ISOLATE; 109839583Swpaul } else { 109939583Swpaul u_int16_t addr, ctl; 110039583Swpaul addr = sc->tl_phy_addr; 110139583Swpaul sc->tl_phy_addr = TL_PHYADDR_MAX; 110239583Swpaul ctl = tl_phy_readreg(sc, TL_PHY_CTL); 110339583Swpaul ctl &= ~PHY_CTL_AUISEL; 110439583Swpaul tl_phy_writereg(sc, TL_PHY_CTL, ctl); 110539583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE); 110639583Swpaul sc->tl_phy_addr = addr; 110739583Swpaul bmcr &= ~PHY_BMCR_ISOLATE; 110838030Swpaul } 110936270Swpaul 111036270Swpaul if (IFM_SUBTYPE(media) == IFM_100_TX) { 111136270Swpaul bmcr |= PHY_BMCR_SPEEDSEL; 111236270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 111336270Swpaul bmcr |= PHY_BMCR_DUPLEX; 111439583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 111536270Swpaul } else { 111636270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 111739583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 111836270Swpaul } 111936270Swpaul } 112036270Swpaul 112136270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 112236270Swpaul bmcr &= ~PHY_BMCR_SPEEDSEL; 112336270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 112436270Swpaul bmcr |= PHY_BMCR_DUPLEX; 112539583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 112636270Swpaul } else { 112736270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 112839583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 112936270Swpaul } 113036270Swpaul } 113136270Swpaul 113236270Swpaul tl_phy_writereg(sc, PHY_BMCR, bmcr); 113336270Swpaul 113438030Swpaul tl_init(sc); 113538030Swpaul 113636270Swpaul return; 113736270Swpaul} 113836270Swpaul 113936464Swpaul/* 114036464Swpaul * Calculate the hash of a MAC address for programming the multicast hash 114136464Swpaul * table. This hash is simply the address split into 6-bit chunks 114236464Swpaul * XOR'd, e.g. 114336464Swpaul * byte: 000000|00 1111|1111 22|222222|333333|33 4444|4444 55|555555 114436464Swpaul * bit: 765432|10 7654|3210 76|543210|765432|10 7654|3210 76|543210 114536464Swpaul * Bytes 0-2 and 3-5 are symmetrical, so are folded together. Then 114636464Swpaul * the folded 24-bit value is split into 6-bit portions and XOR'd. 114736464Swpaul */ 114836270Swpaulstatic int tl_calchash(addr) 114941656Swpaul caddr_t addr; 115036270Swpaul{ 115137626Swpaul int t; 115236270Swpaul 115336464Swpaul t = (addr[0] ^ addr[3]) << 16 | (addr[1] ^ addr[4]) << 8 | 115436464Swpaul (addr[2] ^ addr[5]); 115536464Swpaul return ((t >> 18) ^ (t >> 12) ^ (t >> 6) ^ t) & 0x3f; 115636270Swpaul} 115736270Swpaul 115839583Swpaul/* 115939583Swpaul * The ThunderLAN has a perfect MAC address filter in addition to 116039583Swpaul * the multicast hash filter. The perfect filter can be programmed 116139583Swpaul * with up to four MAC addresses. The first one is always used to 116239583Swpaul * hold the station address, which leaves us free to use the other 116339583Swpaul * three for multicast addresses. 116439583Swpaul */ 116539583Swpaulstatic void tl_setfilt(sc, addr, slot) 116639583Swpaul struct tl_softc *sc; 116741656Swpaul caddr_t addr; 116839583Swpaul int slot; 116939583Swpaul{ 117039583Swpaul int i; 117139583Swpaul u_int16_t regaddr; 117239583Swpaul 117339583Swpaul regaddr = TL_AREG0_B5 + (slot * ETHER_ADDR_LEN); 117439583Swpaul 117539583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 117639583Swpaul tl_dio_write8(sc, regaddr + i, *(addr + i)); 117739583Swpaul 117839583Swpaul return; 117939583Swpaul} 118039583Swpaul 118139583Swpaul/* 118239583Swpaul * XXX In FreeBSD 3.0, multicast addresses are managed using a doubly 118339583Swpaul * linked list. This is fine, except addresses are added from the head 118439583Swpaul * end of the list. We want to arrange for 224.0.0.1 (the "all hosts") 118539583Swpaul * group to always be in the perfect filter, but as more groups are added, 118639583Swpaul * the 224.0.0.1 entry (which is always added first) gets pushed down 118739583Swpaul * the list and ends up at the tail. So after 3 or 4 multicast groups 118839583Swpaul * are added, the all-hosts entry gets pushed out of the perfect filter 118939583Swpaul * and into the hash table. 119039583Swpaul * 119139583Swpaul * Because the multicast list is a doubly-linked list as opposed to a 119239583Swpaul * circular queue, we don't have the ability to just grab the tail of 119339583Swpaul * the list and traverse it backwards. Instead, we have to traverse 119439583Swpaul * the list once to find the tail, then traverse it again backwards to 119539583Swpaul * update the multicast filter. 119639583Swpaul */ 119736270Swpaulstatic void tl_setmulti(sc) 119836270Swpaul struct tl_softc *sc; 119936270Swpaul{ 120036270Swpaul struct ifnet *ifp; 120136270Swpaul u_int32_t hashes[2] = { 0, 0 }; 120239583Swpaul int h, i; 120336270Swpaul struct ifmultiaddr *ifma; 120439583Swpaul u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; 120536270Swpaul ifp = &sc->arpcom.ac_if; 120636270Swpaul 120739583Swpaul /* First, zot all the existing filters. */ 120839583Swpaul for (i = 1; i < 4; i++) 120941656Swpaul tl_setfilt(sc, (caddr_t)&dummy, i); 121039583Swpaul tl_dio_write32(sc, TL_HASH1, 0); 121139583Swpaul tl_dio_write32(sc, TL_HASH2, 0); 121239583Swpaul 121339583Swpaul /* Now program new ones. */ 121439583Swpaul if (ifp->if_flags & IFF_ALLMULTI) { 121536270Swpaul hashes[0] = 0xFFFFFFFF; 121636270Swpaul hashes[1] = 0xFFFFFFFF; 121736270Swpaul } else { 121839583Swpaul i = 1; 121939583Swpaul /* First find the tail of the list. */ 122036270Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 122136270Swpaul ifma = ifma->ifma_link.le_next) { 122239583Swpaul if (ifma->ifma_link.le_next == NULL) 122339583Swpaul break; 122439583Swpaul } 122539583Swpaul /* Now traverse the list backwards. */ 122639583Swpaul for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs; 122739583Swpaul ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) { 122836270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 122936270Swpaul continue; 123039583Swpaul /* 123139583Swpaul * Program the first three multicast groups 123239583Swpaul * into the perfect filter. For all others, 123339583Swpaul * use the hash table. 123439583Swpaul */ 123539583Swpaul if (i < 4) { 123639583Swpaul tl_setfilt(sc, 123739583Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); 123839583Swpaul i++; 123939583Swpaul continue; 124039583Swpaul } 124139583Swpaul 124236270Swpaul h = tl_calchash( 124336270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 124436270Swpaul if (h < 32) 124536270Swpaul hashes[0] |= (1 << h); 124636270Swpaul else 124736317Swpaul hashes[1] |= (1 << (h - 32)); 124836270Swpaul } 124936270Swpaul } 125036270Swpaul 125139583Swpaul tl_dio_write32(sc, TL_HASH1, hashes[0]); 125239583Swpaul tl_dio_write32(sc, TL_HASH2, hashes[1]); 125336270Swpaul 125436270Swpaul return; 125536270Swpaul} 125636270Swpaul 125739583Swpaul/* 125839583Swpaul * This routine is recommended by the ThunderLAN manual to insure that 125939583Swpaul * the internal PHY is powered up correctly. It also recommends a one 126039583Swpaul * second pause at the end to 'wait for the clocks to start' but in my 126139583Swpaul * experience this isn't necessary. 126239583Swpaul */ 126339583Swpaulstatic void tl_hardreset(sc) 126439583Swpaul struct tl_softc *sc; 126539583Swpaul{ 126639583Swpaul int i; 126739583Swpaul u_int16_t old_addr, flags; 126839583Swpaul 126939583Swpaul old_addr = sc->tl_phy_addr; 127039583Swpaul 127139583Swpaul for (i = 0; i < TL_PHYADDR_MAX + 1; i++) { 127239583Swpaul sc->tl_phy_addr = i; 127339583Swpaul tl_mii_sync(sc); 127439583Swpaul } 127539583Swpaul 127639583Swpaul flags = PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE|PHY_BMCR_PWRDOWN; 127739583Swpaul 127839583Swpaul for (i = 0; i < TL_PHYADDR_MAX + 1; i++) { 127939583Swpaul sc->tl_phy_addr = i; 128039583Swpaul tl_phy_writereg(sc, PHY_BMCR, flags); 128139583Swpaul } 128239583Swpaul 128339583Swpaul sc->tl_phy_addr = TL_PHYADDR_MAX; 128439583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE); 128539583Swpaul 128639583Swpaul DELAY(50000); 128739583Swpaul 128839583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_LOOPBK|PHY_BMCR_ISOLATE); 128939583Swpaul 129039583Swpaul tl_mii_sync(sc); 129139583Swpaul 129239583Swpaul while(tl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); 129339583Swpaul 129439583Swpaul sc->tl_phy_addr = old_addr; 129539583Swpaul 129639583Swpaul return; 129739583Swpaul} 129839583Swpaul 129939583Swpaulstatic void tl_softreset(sc, internal) 130039583Swpaul struct tl_softc *sc; 130136270Swpaul int internal; 130236270Swpaul{ 130339583Swpaul u_int32_t cmd, dummy, i; 130436270Swpaul 130536270Swpaul /* Assert the adapter reset bit. */ 130639583Swpaul CMD_SET(sc, TL_CMD_ADRST); 130736270Swpaul /* Turn off interrupts */ 130839583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 130936270Swpaul 131036270Swpaul /* First, clear the stats registers. */ 131139583Swpaul for (i = 0; i < 5; i++) 131239583Swpaul dummy = tl_dio_read32(sc, TL_TXGOODFRAMES); 131336270Swpaul 131436270Swpaul /* Clear Areg and Hash registers */ 131539583Swpaul for (i = 0; i < 8; i++) 131639583Swpaul tl_dio_write32(sc, TL_AREG0_B5, 0x00000000); 131736270Swpaul 131836270Swpaul /* 131936270Swpaul * Set up Netconfig register. Enable one channel and 132036270Swpaul * one fragment mode. 132136270Swpaul */ 132239583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 132345155Swpaul if (internal && !sc->tl_bitrate) { 132439583Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 132536270Swpaul } else { 132639583Swpaul tl_dio_clrbit16(sc, TL_NETCONFIG, TL_CFG_PHYEN); 132736270Swpaul } 132836270Swpaul 132945155Swpaul /* Handle cards with bitrate devices. */ 133045155Swpaul if (sc->tl_bitrate) 133145155Swpaul tl_dio_setbit16(sc, TL_NETCONFIG, TL_CFG_BITRATE); 133245155Swpaul 133336270Swpaul /* Set PCI burst size */ 133439583Swpaul tl_dio_write8(sc, TL_BSIZEREG, 0x33); 133536270Swpaul 133636270Swpaul /* 133736270Swpaul * Load adapter irq pacing timer and tx threshold. 133836270Swpaul * We make the transmit threshold 1 initially but we may 133936270Swpaul * change that later. 134036270Swpaul */ 134139583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 134236270Swpaul cmd |= TL_CMD_NES; 134336270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 134439583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTHR | TX_THR)); 134539583Swpaul CMD_PUT(sc, cmd | (TL_CMD_LDTMR | 0x00000003)); 134636270Swpaul 134736270Swpaul /* Unreset the MII */ 134839583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_NMRST); 134936270Swpaul 135036270Swpaul /* Clear status register */ 135139583Swpaul tl_dio_setbit16(sc, TL_NETSTS, TL_STS_MIRQ); 135239583Swpaul tl_dio_setbit16(sc, TL_NETSTS, TL_STS_HBEAT); 135339583Swpaul tl_dio_setbit16(sc, TL_NETSTS, TL_STS_TXSTOP); 135439583Swpaul tl_dio_setbit16(sc, TL_NETSTS, TL_STS_RXSTOP); 135536270Swpaul 135636270Swpaul /* Enable network status interrupts for everything. */ 135739583Swpaul tl_dio_setbit(sc, TL_NETMASK, TL_MASK_MASK7|TL_MASK_MASK6| 135836270Swpaul TL_MASK_MASK5|TL_MASK_MASK4); 135936270Swpaul 136036270Swpaul /* Take the adapter out of reset */ 136139583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NRESET|TL_CMD_NWRAP); 136236270Swpaul 136336270Swpaul /* Wait for things to settle down a little. */ 136436270Swpaul DELAY(500); 136536270Swpaul 136636270Swpaul return; 136736270Swpaul} 136836270Swpaul 136936270Swpaul/* 137036270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 137139583Swpaul * against our list and return its name if we find a match. 137236270Swpaul */ 137348992Swpaulstatic int tl_probe(dev) 137448992Swpaul device_t dev; 137536270Swpaul{ 137636270Swpaul struct tl_type *t; 137736270Swpaul 137836270Swpaul t = tl_devs; 137936270Swpaul 138036270Swpaul while(t->tl_name != NULL) { 138148992Swpaul if ((pci_get_vendor(dev) == t->tl_vid) && 138248992Swpaul (pci_get_device(dev) == t->tl_did)) { 138348992Swpaul device_set_desc(dev, t->tl_name); 138448992Swpaul return(0); 138548992Swpaul } 138636270Swpaul t++; 138736270Swpaul } 138836270Swpaul 138948992Swpaul return(ENXIO); 139036270Swpaul} 139136270Swpaul 139236270Swpaul/* 139336270Swpaul * Do the interface setup and attach for a PHY on a particular 139436270Swpaul * ThunderLAN chip. Also also set up interrupt vectors. 139536270Swpaul */ 139639583Swpaulstatic int tl_attach_phy(sc) 139739583Swpaul struct tl_softc *sc; 139836270Swpaul{ 139936270Swpaul int phy_ctl; 140036270Swpaul struct tl_type *p = tl_phys; 140139583Swpaul int media = IFM_ETHER|IFM_100_TX|IFM_FDX; 140239583Swpaul struct ifnet *ifp; 140336270Swpaul 140439583Swpaul ifp = &sc->arpcom.ac_if; 140536270Swpaul 140639583Swpaul sc->tl_phy_did = tl_phy_readreg(sc, TL_PHY_DEVID); 140739583Swpaul sc->tl_phy_vid = tl_phy_readreg(sc, TL_PHY_VENID); 140839583Swpaul sc->tl_phy_sts = tl_phy_readreg(sc, TL_PHY_GENSTS); 140939583Swpaul phy_ctl = tl_phy_readreg(sc, TL_PHY_GENCTL); 141036270Swpaul 141136270Swpaul /* 141236270Swpaul * PHY revision numbers tend to vary a bit. Our algorithm here 141336270Swpaul * is to check everything but the 8 least significant bits. 141436270Swpaul */ 141536270Swpaul while(p->tl_vid) { 141636270Swpaul if (sc->tl_phy_vid == p->tl_vid && 141736270Swpaul (sc->tl_phy_did | 0x000F) == p->tl_did) { 141836270Swpaul sc->tl_pinfo = p; 141936270Swpaul break; 142036270Swpaul } 142136270Swpaul p++; 142236270Swpaul } 142336270Swpaul if (sc->tl_pinfo == NULL) { 142436270Swpaul sc->tl_pinfo = &tl_phys[PHY_UNKNOWN]; 142536270Swpaul } 142636270Swpaul 142736270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4 || 142836270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXFULL || 142936270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 143036270Swpaul ifp->if_baudrate = 100000000; 143136270Swpaul else 143236270Swpaul ifp->if_baudrate = 10000000; 143336270Swpaul 143439583Swpaul if (bootverbose) { 143539583Swpaul printf("tl%d: phy at mii address %d\n", sc->tl_unit, 143639583Swpaul sc->tl_phy_addr); 143736270Swpaul 143839583Swpaul printf("tl%d: %s ", sc->tl_unit, sc->tl_pinfo->tl_name); 143939583Swpaul } 144036270Swpaul 144136270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4 || 144236270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXHALF || 144346568Speter sc->tl_phy_sts & PHY_BMSR_100BTXHALF) { 144439583Swpaul if (bootverbose) 144539583Swpaul printf("10/100Mbps "); 144646568Speter } else { 144736270Swpaul media &= ~IFM_100_TX; 144836270Swpaul media |= IFM_10_T; 144939583Swpaul if (bootverbose) 145039583Swpaul printf("10Mbps "); 145136270Swpaul } 145236270Swpaul 145336270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL || 145446568Speter sc->tl_phy_sts & PHY_BMSR_10BTFULL) { 145539583Swpaul if (bootverbose) 145639583Swpaul printf("full duplex "); 145746568Speter } else { 145839583Swpaul if (bootverbose) 145939583Swpaul printf("half duplex "); 146036270Swpaul media &= ~IFM_FDX; 146136270Swpaul } 146236270Swpaul 146336270Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) { 146436270Swpaul media = IFM_ETHER|IFM_AUTO; 146539583Swpaul if (bootverbose) 146639583Swpaul printf("autonegotiating\n"); 146736270Swpaul } else 146839583Swpaul if (bootverbose) 146939583Swpaul printf("\n"); 147036270Swpaul 147136270Swpaul /* If this isn't a known PHY, print the PHY indentifier info. */ 147239583Swpaul if (sc->tl_pinfo->tl_vid == 0 && bootverbose) 147336270Swpaul printf("tl%d: vendor id: %04x product id: %04x\n", 147436270Swpaul sc->tl_unit, sc->tl_phy_vid, sc->tl_phy_did); 147536270Swpaul 147636270Swpaul /* Set up ifmedia data and callbacks. */ 147736270Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 147836270Swpaul 147936270Swpaul /* 148036270Swpaul * All ThunderLANs support at least 10baseT half duplex. 148136270Swpaul * They also support AUI selection if used in 10Mb/s modes. 148236270Swpaul */ 148336270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 148436270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 148536270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 148636270Swpaul 148736270Swpaul /* Some ThunderLAN PHYs support autonegotiation. */ 148836270Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) 148936270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 149036270Swpaul 149136270Swpaul /* Some support 10baseT full duplex. */ 149236270Swpaul if (sc->tl_phy_sts & PHY_BMSR_10BTFULL) 149336270Swpaul ifmedia_add(&sc->ifmedia, 149436270Swpaul IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 149536270Swpaul 149636270Swpaul /* Some support 100BaseTX half duplex. */ 149736270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 149836270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); 149936270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 150036270Swpaul ifmedia_add(&sc->ifmedia, 150136270Swpaul IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); 150236270Swpaul 150336270Swpaul /* Some support 100BaseTX full duplex. */ 150436270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL) 150536270Swpaul ifmedia_add(&sc->ifmedia, 150636270Swpaul IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); 150736270Swpaul 150836270Swpaul /* Some also support 100BaseT4. */ 150936270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4) 151036270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); 151136270Swpaul 151236270Swpaul /* Set default media. */ 151336270Swpaul ifmedia_set(&sc->ifmedia, media); 151436270Swpaul 151536270Swpaul /* 151636270Swpaul * Kick off an autonegotiation session if this PHY supports it. 151736270Swpaul * This is necessary to make sure the chip's duplex mode matches 151836270Swpaul * the PHY's duplex mode. It may not: once enabled, the PHY may 151936270Swpaul * autonegotiate full-duplex mode with its link partner, but the 152036270Swpaul * ThunderLAN chip defaults to half-duplex and stays there unless 152136270Swpaul * told otherwise. 152236270Swpaul */ 152338030Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) { 152438030Swpaul tl_init(sc); 152539583Swpaul#ifdef TL_BACKGROUND_AUTONEG 152638030Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 152739583Swpaul#else 152848999Swpaul if (cold) 152948999Swpaul tl_autoneg(sc, TL_FLAG_FORCEDELAY, 1); 153048999Swpaul else 153148999Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 153239583Swpaul#endif 153338030Swpaul } 153436270Swpaul 153536270Swpaul return(0); 153636270Swpaul} 153736270Swpaul 153848992Swpaulstatic int tl_attach(dev) 153948992Swpaul device_t dev; 154036270Swpaul{ 154136270Swpaul int s, i, phys = 0; 154236270Swpaul u_int32_t command; 154339583Swpaul u_int16_t did, vid; 154439583Swpaul struct tl_type *t; 154539583Swpaul struct ifnet *ifp; 154639583Swpaul struct tl_softc *sc; 154739583Swpaul unsigned int round; 154839583Swpaul caddr_t roundptr; 154948992Swpaul int unit, error = 0, rid; 155036270Swpaul 155136270Swpaul s = splimp(); 155236270Swpaul 155348992Swpaul vid = pci_get_vendor(dev); 155448992Swpaul did = pci_get_device(dev); 155548992Swpaul sc = device_get_softc(dev); 155648992Swpaul unit = device_get_unit(dev); 155748992Swpaul bzero(sc, sizeof(struct tl_softc)); 155839583Swpaul 155939583Swpaul t = tl_devs; 156039583Swpaul while(t->tl_name != NULL) { 156139583Swpaul if (vid == t->tl_vid && did == t->tl_did) 156236270Swpaul break; 156339583Swpaul t++; 156439583Swpaul } 156536270Swpaul 156639583Swpaul if (t->tl_name == NULL) { 156739583Swpaul printf("tl%d: unknown device!?\n", unit); 156836270Swpaul goto fail; 156936270Swpaul } 157036270Swpaul 157136270Swpaul /* 157236270Swpaul * Map control/status registers. 157336270Swpaul */ 157448992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 157539583Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 157648992Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 157748992Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 157836270Swpaul 157939583Swpaul#ifdef TL_USEIOSPACE 158039583Swpaul if (!(command & PCIM_CMD_PORTEN)) { 158139583Swpaul printf("tl%d: failed to enable I/O ports!\n", unit); 158248992Swpaul error = ENXIO; 158339583Swpaul goto fail; 158439583Swpaul } 158539583Swpaul 158648992Swpaul rid = TL_PCI_LOIO; 158748992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 158848992Swpaul 0, ~0, 1, RF_ACTIVE); 158948992Swpaul 159048992Swpaul /* 159148992Swpaul * Some cards have the I/O and memory mapped address registers 159248992Swpaul * reversed. Try both combinations before giving up. 159348992Swpaul */ 159448992Swpaul if (sc->tl_res == NULL) { 159548992Swpaul rid = TL_PCI_LOMEM; 159648992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 159748992Swpaul 0, ~0, 1, RF_ACTIVE); 159845155Swpaul } 159939583Swpaul#else 160036270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 160139583Swpaul printf("tl%d: failed to enable memory mapping!\n", unit); 160248992Swpaul error = ENXIO; 160336270Swpaul goto fail; 160436270Swpaul } 160536270Swpaul 160648992Swpaul rid = TL_PCI_LOMEM; 160748992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 160848992Swpaul 0, ~0, 1, RF_ACTIVE); 160948992Swpaul if (sc->tl_res == NULL) { 161048992Swpaul rid = TL_PCI_LOIO; 161148992Swpaul sc->tl_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 161248992Swpaul 0, ~0, 1, RF_ACTIVE); 161336270Swpaul } 161439583Swpaul#endif 161536270Swpaul 161648992Swpaul if (sc->tl_res == NULL) { 161748992Swpaul printf("tl%d: couldn't map ports/memory\n", unit); 161848992Swpaul error = ENXIO; 161948992Swpaul goto fail; 162048992Swpaul } 162148992Swpaul 162248992Swpaul sc->tl_btag = rman_get_bustag(sc->tl_res); 162348992Swpaul sc->tl_bhandle = rman_get_bushandle(sc->tl_res); 162448992Swpaul 162539583Swpaul#ifdef notdef 162639583Swpaul /* 162739583Swpaul * The ThunderLAN manual suggests jacking the PCI latency 162839583Swpaul * timer all the way up to its maximum value. I'm not sure 162939583Swpaul * if this is really necessary, but what the manual wants, 163039583Swpaul * the manual gets. 163139583Swpaul */ 163248992Swpaul command = pci_read_config(dev, TL_PCI_LATENCY_TIMER, 4); 163339583Swpaul command |= 0x0000FF00; 163448992Swpaul pci_write_config(dev, TL_PCI_LATENCY_TIMER, command, 4); 163539583Swpaul#endif 163636270Swpaul 163736270Swpaul /* Allocate interrupt */ 163848992Swpaul rid = 0; 163948992Swpaul sc->tl_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 164048992Swpaul RF_SHAREABLE | RF_ACTIVE); 164148992Swpaul 164248992Swpaul if (sc->tl_irq == NULL) { 164349010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 164439583Swpaul printf("tl%d: couldn't map interrupt\n", unit); 164548992Swpaul error = ENXIO; 164636270Swpaul goto fail; 164736270Swpaul } 164836270Swpaul 164948992Swpaul error = bus_setup_intr(dev, sc->tl_irq, INTR_TYPE_NET, 165048992Swpaul tl_intr, sc, &sc->tl_intrhand); 165148992Swpaul 165248992Swpaul if (error) { 165349010Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_res); 165449010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 165548992Swpaul printf("tl%d: couldn't set up irq\n", unit); 165648992Swpaul goto fail; 165748992Swpaul } 165848992Swpaul 165936270Swpaul /* 166039583Swpaul * Now allocate memory for the TX and RX lists. Note that 166139583Swpaul * we actually allocate 8 bytes more than we really need: 166239583Swpaul * this is because we need to adjust the final address to 166339583Swpaul * be aligned on a quadword (64-bit) boundary in order to 166439583Swpaul * make the chip happy. If the list structures aren't properly 166539583Swpaul * aligned, DMA fails and the chip generates an adapter check 166639583Swpaul * interrupt and has to be reset. If you set up the softc struct 166739583Swpaul * just right you can sort of obtain proper alignment 'by chance.' 166839583Swpaul * But I don't want to depend on this, so instead the alignment 166939583Swpaul * is forced here. 167036270Swpaul */ 167139583Swpaul sc->tl_ldata_ptr = malloc(sizeof(struct tl_list_data) + 8, 167239583Swpaul M_DEVBUF, M_NOWAIT); 167339583Swpaul 167439583Swpaul if (sc->tl_ldata_ptr == NULL) { 167549010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 167648992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 167749010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 167839583Swpaul printf("tl%d: no memory for list buffers!\n", unit); 167948992Swpaul error = ENXIO; 168036270Swpaul goto fail; 168136270Swpaul } 168236270Swpaul 168336270Swpaul /* 168439583Swpaul * Convoluted but satisfies my ANSI sensibilities. GCC lets 168539583Swpaul * you do casts on the LHS of an assignment, but ANSI doesn't 168639583Swpaul * allow that. 168738030Swpaul */ 168839583Swpaul sc->tl_ldata = (struct tl_list_data *)sc->tl_ldata_ptr; 168948443Speter round = (uintptr_t)sc->tl_ldata_ptr & 0xF; 169039583Swpaul roundptr = sc->tl_ldata_ptr; 169139583Swpaul for (i = 0; i < 8; i++) { 169239583Swpaul if (round % 8) { 169339583Swpaul round++; 169439583Swpaul roundptr++; 169539583Swpaul } else 169639583Swpaul break; 169738030Swpaul } 169839583Swpaul sc->tl_ldata = (struct tl_list_data *)roundptr; 169938030Swpaul 170039583Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 170139583Swpaul 170239583Swpaul sc->tl_unit = unit; 170339583Swpaul sc->tl_dinfo = t; 170443235Swpaul if (t->tl_vid == COMPAQ_VENDORID || t->tl_vid == TI_VENDORID) 170539583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR; 170639583Swpaul if (t->tl_vid == OLICOM_VENDORID) 170739583Swpaul sc->tl_eeaddr = TL_EEPROM_EADDR_OC; 170839583Swpaul 170939583Swpaul /* Reset the adapter. */ 171039583Swpaul tl_softreset(sc, 1); 171139583Swpaul tl_hardreset(sc); 171239583Swpaul tl_softreset(sc, 1); 171339583Swpaul 171438030Swpaul /* 171539583Swpaul * Get station address from the EEPROM. 171639583Swpaul */ 171739583Swpaul if (tl_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, 171839583Swpaul sc->tl_eeaddr, ETHER_ADDR_LEN)) { 171949010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 172048992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 172149010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 172248992Swpaul free(sc->tl_ldata_ptr, M_DEVBUF); 172339583Swpaul printf("tl%d: failed to read station address\n", unit); 172448992Swpaul error = ENXIO; 172539583Swpaul goto fail; 172639583Swpaul } 172739583Swpaul 172839583Swpaul /* 172939583Swpaul * XXX Olicom, in its desire to be different from the 173039583Swpaul * rest of the world, has done strange things with the 173139583Swpaul * encoding of the station address in the EEPROM. First 173239583Swpaul * of all, they store the address at offset 0xF8 rather 173339583Swpaul * than at 0x83 like the ThunderLAN manual suggests. 173439583Swpaul * Second, they store the address in three 16-bit words in 173539583Swpaul * network byte order, as opposed to storing it sequentially 173639583Swpaul * like all the other ThunderLAN cards. In order to get 173739583Swpaul * the station address in a form that matches what the Olicom 173839583Swpaul * diagnostic utility specifies, we have to byte-swap each 173939583Swpaul * word. To make things even more confusing, neither 00:00:28 174039583Swpaul * nor 00:00:24 appear in the IEEE OUI database. 174139583Swpaul */ 174239583Swpaul if (sc->tl_dinfo->tl_vid == OLICOM_VENDORID) { 174339583Swpaul for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 174439583Swpaul u_int16_t *p; 174539583Swpaul p = (u_int16_t *)&sc->arpcom.ac_enaddr[i]; 174639583Swpaul *p = ntohs(*p); 174739583Swpaul } 174839583Swpaul } 174939583Swpaul 175039583Swpaul /* 175136270Swpaul * A ThunderLAN chip was detected. Inform the world. 175236270Swpaul */ 175339583Swpaul printf("tl%d: Ethernet address: %6D\n", unit, 175439583Swpaul sc->arpcom.ac_enaddr, ":"); 175536270Swpaul 175639583Swpaul ifp = &sc->arpcom.ac_if; 175739583Swpaul ifp->if_softc = sc; 175839583Swpaul ifp->if_unit = sc->tl_unit; 175939583Swpaul ifp->if_name = "tl"; 176039583Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 176139583Swpaul ifp->if_ioctl = tl_ioctl; 176239583Swpaul ifp->if_output = ether_output; 176339583Swpaul ifp->if_start = tl_start; 176439583Swpaul ifp->if_watchdog = tl_watchdog; 176539583Swpaul ifp->if_init = tl_init; 176639583Swpaul ifp->if_mtu = ETHERMTU; 176746561Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 176839583Swpaul callout_handle_init(&sc->tl_stat_ch); 176939583Swpaul 177039583Swpaul /* Reset the adapter again. */ 177139583Swpaul tl_softreset(sc, 1); 177239583Swpaul tl_hardreset(sc); 177339583Swpaul tl_softreset(sc, 1); 177439583Swpaul 177536270Swpaul /* 177636270Swpaul * Now attach the ThunderLAN's PHYs. There will always 177736270Swpaul * be at least one PHY; if the PHY address is 0x1F, then 177839583Swpaul * it's the internal one. 177936270Swpaul */ 178036270Swpaul 178136270Swpaul for (i = TL_PHYADDR_MIN; i < TL_PHYADDR_MAX + 1; i++) { 178239583Swpaul sc->tl_phy_addr = i; 178339583Swpaul if (bootverbose) 178439583Swpaul printf("tl%d: looking for phy at addr %x\n", unit, i); 178539583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 178636270Swpaul DELAY(500); 178739583Swpaul while(tl_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_RESET); 178839583Swpaul sc->tl_phy_sts = tl_phy_readreg(sc, PHY_BMSR); 178939583Swpaul if (bootverbose) 179039583Swpaul printf("tl%d: status: %x\n", unit, sc->tl_phy_sts); 179139583Swpaul if (!sc->tl_phy_sts) 179236270Swpaul continue; 179339583Swpaul if (tl_attach_phy(sc)) { 179449010Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 179548992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 179649010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 179748992Swpaul free(sc->tl_ldata_ptr, M_DEVBUF); 179839583Swpaul printf("tl%d: failed to attach a phy %d\n", unit, i); 179948992Swpaul error = ENXIO; 180036270Swpaul goto fail; 180136270Swpaul } 180236270Swpaul phys++; 180336270Swpaul if (phys && i != TL_PHYADDR_MAX) 180436270Swpaul break; 180536270Swpaul } 180636270Swpaul 180745155Swpaul /* 180845155Swpaul * If no MII-based PHYs were detected, then this is a 180945155Swpaul * TNETE110 device with a bit rate PHY. There's no autoneg 181045155Swpaul * support, so just default to 10baseT mode. 181145155Swpaul */ 181236270Swpaul if (!phys) { 181345155Swpaul struct ifmedia *ifm; 181445155Swpaul sc->tl_bitrate = 1; 181545155Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 181645155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 181745155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 181845155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 181945155Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 182045166Swpaul ifmedia_set(&sc->ifmedia, IFM_ETHER|IFM_10_T); 182145155Swpaul /* Reset again, this time setting bitrate mode. */ 182245155Swpaul tl_softreset(sc, 1); 182345155Swpaul ifm = &sc->ifmedia; 182445155Swpaul ifm->ifm_media = ifm->ifm_cur->ifm_media; 182545155Swpaul tl_ifmedia_upd(ifp); 182636270Swpaul } 182736270Swpaul 182839627Swpaul tl_intvec_adchk((void *)sc, 0); 182939627Swpaul tl_stop(sc); 183039627Swpaul 183139583Swpaul /* 183239627Swpaul * Attempt to clear any stray interrupts 183339627Swpaul * that may be lurking. 183439627Swpaul */ 183539627Swpaul tl_intr((void *)sc); 183639627Swpaul 183739627Swpaul /* 183839583Swpaul * Call MI attach routines. 183939583Swpaul */ 184039583Swpaul if_attach(ifp); 184139583Swpaul ether_ifattach(ifp); 184238030Swpaul 184348645Sdes#if NBPF > 0 184439583Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 184539583Swpaul#endif 184639583Swpaul 184736270Swpaulfail: 184836270Swpaul splx(s); 184948992Swpaul return(error); 185036270Swpaul} 185136270Swpaul 185248992Swpaulstatic int tl_detach(dev) 185348992Swpaul device_t dev; 185448992Swpaul{ 185548992Swpaul struct tl_softc *sc; 185648992Swpaul struct ifnet *ifp; 185748992Swpaul int s; 185848992Swpaul 185948992Swpaul s = splimp(); 186048992Swpaul 186148992Swpaul sc = device_get_softc(dev); 186248992Swpaul ifp = &sc->arpcom.ac_if; 186348992Swpaul 186448992Swpaul tl_stop(sc); 186548992Swpaul if_detach(ifp); 186648992Swpaul 186748992Swpaul free(sc->tl_ldata_ptr, M_DEVBUF); 186848992Swpaul ifmedia_removeall(&sc->ifmedia); 186948992Swpaul 187048992Swpaul bus_teardown_intr(dev, sc->tl_irq, sc->tl_intrhand); 187148992Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->tl_irq); 187249010Swpaul bus_release_resource(dev, TL_RES, TL_RID, sc->tl_res); 187348992Swpaul 187448992Swpaul splx(s); 187548992Swpaul 187648992Swpaul return(0); 187748992Swpaul} 187848992Swpaul 187936270Swpaul/* 188036270Swpaul * Initialize the transmit lists. 188136270Swpaul */ 188236270Swpaulstatic int tl_list_tx_init(sc) 188336270Swpaul struct tl_softc *sc; 188436270Swpaul{ 188536270Swpaul struct tl_chain_data *cd; 188636270Swpaul struct tl_list_data *ld; 188736270Swpaul int i; 188836270Swpaul 188936270Swpaul cd = &sc->tl_cdata; 189036270Swpaul ld = sc->tl_ldata; 189136270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 189236270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 189336270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 189436270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 189536270Swpaul else 189636270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 189736270Swpaul } 189836270Swpaul 189936270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 190036270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 190136270Swpaul sc->tl_txeoc = 1; 190236270Swpaul 190336270Swpaul return(0); 190436270Swpaul} 190536270Swpaul 190636270Swpaul/* 190736270Swpaul * Initialize the RX lists and allocate mbufs for them. 190836270Swpaul */ 190936270Swpaulstatic int tl_list_rx_init(sc) 191036270Swpaul struct tl_softc *sc; 191136270Swpaul{ 191236270Swpaul struct tl_chain_data *cd; 191336270Swpaul struct tl_list_data *ld; 191436270Swpaul int i; 191536270Swpaul 191636270Swpaul cd = &sc->tl_cdata; 191736270Swpaul ld = sc->tl_ldata; 191836270Swpaul 191940795Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 192036270Swpaul cd->tl_rx_chain[i].tl_ptr = 192137626Swpaul (struct tl_list_onefrag *)&ld->tl_rx_list[i]; 192239583Swpaul if (tl_newbuf(sc, &cd->tl_rx_chain[i]) == ENOBUFS) 192339583Swpaul return(ENOBUFS); 192440795Swpaul if (i == (TL_RX_LIST_CNT - 1)) { 192536270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 192636270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 192736270Swpaul } else { 192836270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 192936270Swpaul ld->tl_rx_list[i].tlist_fptr = 193036270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 193136270Swpaul } 193236270Swpaul } 193336270Swpaul 193436270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 193536270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 193636270Swpaul 193736270Swpaul return(0); 193836270Swpaul} 193936270Swpaul 194036270Swpaulstatic int tl_newbuf(sc, c) 194136270Swpaul struct tl_softc *sc; 194237626Swpaul struct tl_chain_onefrag *c; 194336270Swpaul{ 194436270Swpaul struct mbuf *m_new = NULL; 194536270Swpaul 194636270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 194736270Swpaul if (m_new == NULL) { 194839583Swpaul printf("tl%d: no memory for rx list -- packet dropped!", 194936270Swpaul sc->tl_unit); 195036270Swpaul return(ENOBUFS); 195136270Swpaul } 195236270Swpaul 195336270Swpaul MCLGET(m_new, M_DONTWAIT); 195436270Swpaul if (!(m_new->m_flags & M_EXT)) { 195539583Swpaul printf("tl%d: no memory for rx list -- packet dropped!", 195639583Swpaul sc->tl_unit); 195736270Swpaul m_freem(m_new); 195836270Swpaul return(ENOBUFS); 195936270Swpaul } 196036270Swpaul 196145155Swpaul#ifdef __alpha__ 196245155Swpaul m_new->m_data += 2; 196345155Swpaul#endif 196445155Swpaul 196536270Swpaul c->tl_mbuf = m_new; 196636270Swpaul c->tl_next = NULL; 196736270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 196836270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 196936270Swpaul c->tl_ptr->tlist_fptr = 0; 197037626Swpaul c->tl_ptr->tl_frag.tlist_dadr = vtophys(mtod(m_new, caddr_t)); 197137626Swpaul c->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 197236270Swpaul 197336270Swpaul return(0); 197436270Swpaul} 197536270Swpaul/* 197636270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 197736270Swpaul * tells us that a full ethernet frame has been captured and we need 197836270Swpaul * to handle it. 197936270Swpaul * 198036270Swpaul * Reception is done using 'lists' which consist of a header and a 198136270Swpaul * series of 10 data count/data address pairs that point to buffers. 198236270Swpaul * Initially you're supposed to create a list, populate it with pointers 198336270Swpaul * to buffers, then load the physical address of the list into the 198436270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 198536270Swpaul * frame into the buffers for you. 198636270Swpaul * 198736270Swpaul * To make things as fast as possible, we have the chip DMA directly 198836270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 198936270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 199036270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 199136270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 199236270Swpaul * the chip, it will always think it has an endless receive channel. 199336270Swpaul * 199436270Swpaul * If we happen to fall behind and the chip manages to fill up all of 199536270Swpaul * the buffers, it will generate an end of channel interrupt and wait 199636270Swpaul * for us to empty the chain and restart the receiver. 199736270Swpaul */ 199836270Swpaulstatic int tl_intvec_rxeof(xsc, type) 199936270Swpaul void *xsc; 200036270Swpaul u_int32_t type; 200136270Swpaul{ 200236270Swpaul struct tl_softc *sc; 200336270Swpaul int r = 0, total_len = 0; 200436270Swpaul struct ether_header *eh; 200536270Swpaul struct mbuf *m; 200636270Swpaul struct ifnet *ifp; 200737626Swpaul struct tl_chain_onefrag *cur_rx; 200836270Swpaul 200936270Swpaul sc = xsc; 201036270Swpaul ifp = &sc->arpcom.ac_if; 201136270Swpaul 201236270Swpaul while(sc->tl_cdata.tl_rx_head->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP){ 201336270Swpaul r++; 201436270Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 201536270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 201636270Swpaul m = cur_rx->tl_mbuf; 201736270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 201836270Swpaul 201939583Swpaul if (tl_newbuf(sc, cur_rx) == ENOBUFS) { 202039583Swpaul ifp->if_ierrors++; 202139583Swpaul cur_rx->tl_ptr->tlist_frsize = MCLBYTES; 202239583Swpaul cur_rx->tl_ptr->tlist_cstat = TL_CSTAT_READY; 202339583Swpaul cur_rx->tl_ptr->tl_frag.tlist_dcnt = MCLBYTES; 202439583Swpaul continue; 202539583Swpaul } 202636270Swpaul 202736270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 202836270Swpaul vtophys(cur_rx->tl_ptr); 202936270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 203036270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 203136270Swpaul 203236270Swpaul eh = mtod(m, struct ether_header *); 203336270Swpaul m->m_pkthdr.rcvif = ifp; 203436270Swpaul 203537626Swpaul /* 203637626Swpaul * Note: when the ThunderLAN chip is in 'capture all 203737626Swpaul * frames' mode, it will receive its own transmissions. 203837626Swpaul * We drop don't need to process our own transmissions, 203937626Swpaul * so we drop them here and continue. 204037626Swpaul */ 204139583Swpaul /*if (ifp->if_flags & IFF_PROMISC && */ 204239583Swpaul if (!bcmp(eh->ether_shost, sc->arpcom.ac_enaddr, 204337626Swpaul ETHER_ADDR_LEN)) { 204437626Swpaul m_freem(m); 204537626Swpaul continue; 204637626Swpaul } 204737626Swpaul 204848645Sdes#if NBPF > 0 204936270Swpaul /* 205036270Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 205136270Swpaul * don't pass it up to the ether_input() layer unless it's 205236270Swpaul * a broadcast packet, multicast packet, matches our ethernet 205336270Swpaul * address or the interface is in promiscuous mode. If we don't 205436270Swpaul * want the packet, just forget it. We leave the mbuf in place 205536270Swpaul * since it can be used again later. 205636270Swpaul */ 205736270Swpaul if (ifp->if_bpf) { 205836270Swpaul m->m_pkthdr.len = m->m_len = total_len; 205936270Swpaul bpf_mtap(ifp, m); 206036270Swpaul if (ifp->if_flags & IFF_PROMISC && 206136270Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 206236270Swpaul ETHER_ADDR_LEN) && 206336270Swpaul (eh->ether_dhost[0] & 1) == 0)) { 206436270Swpaul m_freem(m); 206536270Swpaul continue; 206636270Swpaul } 206736270Swpaul } 206836270Swpaul#endif 206936270Swpaul /* Remove header from mbuf and pass it on. */ 207036270Swpaul m->m_pkthdr.len = m->m_len = 207136270Swpaul total_len - sizeof(struct ether_header); 207236270Swpaul m->m_data += sizeof(struct ether_header); 207336270Swpaul ether_input(ifp, eh, m); 207436270Swpaul } 207536270Swpaul 207636270Swpaul return(r); 207736270Swpaul} 207836270Swpaul 207936270Swpaul/* 208036270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 208136270Swpaul * initialized or the adapter reached a list with a forward pointer 208236270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 208336270Swpaul * the card has hit the end of the receive buffer chain and we need to 208436270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 208536270Swpaul */ 208636270Swpaulstatic int tl_intvec_rxeoc(xsc, type) 208736270Swpaul void *xsc; 208836270Swpaul u_int32_t type; 208936270Swpaul{ 209036270Swpaul struct tl_softc *sc; 209136270Swpaul int r; 209236270Swpaul 209336270Swpaul sc = xsc; 209436270Swpaul 209536270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 209636270Swpaul r = tl_intvec_rxeof(xsc, type); 209739583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | (type & ~(0x00100000))); 209836270Swpaul r = 1; 209939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(sc->tl_cdata.tl_rx_head->tl_ptr)); 210036270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 210136270Swpaul return(r); 210236270Swpaul} 210336270Swpaul 210436270Swpaulstatic int tl_intvec_txeof(xsc, type) 210536270Swpaul void *xsc; 210636270Swpaul u_int32_t type; 210736270Swpaul{ 210836270Swpaul struct tl_softc *sc; 210936270Swpaul int r = 0; 211036270Swpaul struct tl_chain *cur_tx; 211136270Swpaul 211236270Swpaul sc = xsc; 211336270Swpaul 211436270Swpaul /* 211536270Swpaul * Go through our tx list and free mbufs for those 211636270Swpaul * frames that have been sent. 211736270Swpaul */ 211836270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 211936270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 212036270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 212136270Swpaul break; 212236270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 212336270Swpaul 212436270Swpaul r++; 212536270Swpaul m_freem(cur_tx->tl_mbuf); 212636270Swpaul cur_tx->tl_mbuf = NULL; 212736270Swpaul 212836270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 212936270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 213037626Swpaul if (!cur_tx->tl_ptr->tlist_fptr) 213137626Swpaul break; 213236270Swpaul } 213336270Swpaul 213436270Swpaul return(r); 213536270Swpaul} 213636270Swpaul 213736270Swpaul/* 213836270Swpaul * The transmit end of channel interrupt. The adapter triggers this 213936270Swpaul * interrupt to tell us it hit the end of the current transmit list. 214036270Swpaul * 214136270Swpaul * A note about this: it's possible for a condition to arise where 214236270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 214336270Swpaul * You have to avoid this since the chip expects things to go in a 214436270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 214536270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 214636270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 214736270Swpaul * interrupt should be received and acknowledged before any more frames 214836270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 214936270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 215036270Swpaul * it could attempt to issue a transmit command prematurely. 215136270Swpaul * 215236270Swpaul * To guard against this, tl_start() will only issue transmit commands 215336270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 215436270Swpaul * can set this flag once tl_start() has cleared it. 215536270Swpaul */ 215636270Swpaulstatic int tl_intvec_txeoc(xsc, type) 215736270Swpaul void *xsc; 215836270Swpaul u_int32_t type; 215936270Swpaul{ 216036270Swpaul struct tl_softc *sc; 216136270Swpaul struct ifnet *ifp; 216236270Swpaul u_int32_t cmd; 216336270Swpaul 216436270Swpaul sc = xsc; 216536270Swpaul ifp = &sc->arpcom.ac_if; 216636270Swpaul 216736270Swpaul /* Clear the timeout timer. */ 216836270Swpaul ifp->if_timer = 0; 216936270Swpaul 217036270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 217136270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 217236270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 217336270Swpaul sc->tl_txeoc = 1; 217437626Swpaul /* 217537626Swpaul * If we just drained the TX queue and 217637626Swpaul * there's an autoneg request waiting, set 217737626Swpaul * it in motion. This will block the transmitter 217837626Swpaul * until the autoneg session completes which will 217937626Swpaul * no doubt piss off any processes waiting to 218037626Swpaul * transmit, but that's the way the ball bounces. 218137626Swpaul */ 218237626Swpaul if (sc->tl_want_auto) 218337626Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 218436270Swpaul } else { 218536270Swpaul sc->tl_txeoc = 0; 218636270Swpaul /* First we have to ack the EOC interrupt. */ 218739583Swpaul CMD_PUT(sc, TL_CMD_ACK | 0x00000001 | type); 218836270Swpaul /* Then load the address of the next TX list. */ 218939583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 219039583Swpaul vtophys(sc->tl_cdata.tl_tx_head->tl_ptr)); 219136270Swpaul /* Restart TX channel. */ 219239583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 219336270Swpaul cmd &= ~TL_CMD_RT; 219436270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 219539583Swpaul CMD_PUT(sc, cmd); 219636270Swpaul return(0); 219736270Swpaul } 219836270Swpaul 219936270Swpaul return(1); 220036270Swpaul} 220136270Swpaul 220236270Swpaulstatic int tl_intvec_adchk(xsc, type) 220336270Swpaul void *xsc; 220436270Swpaul u_int32_t type; 220536270Swpaul{ 220636270Swpaul struct tl_softc *sc; 220737626Swpaul u_int16_t bmcr, ctl; 220836270Swpaul 220936270Swpaul sc = xsc; 221036270Swpaul 221139627Swpaul if (type) 221239627Swpaul printf("tl%d: adapter check: %x\n", sc->tl_unit, 221341656Swpaul (unsigned int)CSR_READ_4(sc, TL_CH_PARM)); 221436270Swpaul 221537626Swpaul /* 221637626Swpaul * Before resetting the adapter, try reading the PHY 221737626Swpaul * settings so we can put them back later. This is 221837626Swpaul * necessary to keep the chip operating at the same 221937626Swpaul * speed and duplex settings after the reset completes. 222037626Swpaul */ 222145155Swpaul if (!sc->tl_bitrate) { 222237626Swpaul bmcr = tl_phy_readreg(sc, PHY_BMCR); 222337626Swpaul ctl = tl_phy_readreg(sc, TL_PHY_CTL); 222439583Swpaul tl_softreset(sc, 1); 222537626Swpaul tl_phy_writereg(sc, PHY_BMCR, bmcr); 222637626Swpaul tl_phy_writereg(sc, TL_PHY_CTL, ctl); 222737626Swpaul if (bmcr & PHY_BMCR_DUPLEX) { 222839583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 222937626Swpaul } else { 223039583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_DUPLEX); 223137626Swpaul } 223245155Swpaul } 223337626Swpaul tl_stop(sc); 223436270Swpaul tl_init(sc); 223539583Swpaul CMD_SET(sc, TL_CMD_INTSON); 223636270Swpaul 223736270Swpaul return(0); 223836270Swpaul} 223936270Swpaul 224036270Swpaulstatic int tl_intvec_netsts(xsc, type) 224136270Swpaul void *xsc; 224236270Swpaul u_int32_t type; 224336270Swpaul{ 224436270Swpaul struct tl_softc *sc; 224536270Swpaul u_int16_t netsts; 224636270Swpaul 224736270Swpaul sc = xsc; 224836270Swpaul 224939583Swpaul netsts = tl_dio_read16(sc, TL_NETSTS); 225039583Swpaul tl_dio_write16(sc, TL_NETSTS, netsts); 225136270Swpaul 225236270Swpaul printf("tl%d: network status: %x\n", sc->tl_unit, netsts); 225336270Swpaul 225436270Swpaul return(1); 225536270Swpaul} 225636270Swpaul 225739583Swpaulstatic void tl_intr(xsc) 225839583Swpaul void *xsc; 225936270Swpaul{ 226036270Swpaul struct tl_softc *sc; 226136270Swpaul struct ifnet *ifp; 226236270Swpaul int r = 0; 226336270Swpaul u_int32_t type = 0; 226436270Swpaul u_int16_t ints = 0; 226536270Swpaul u_int8_t ivec = 0; 226636270Swpaul 226739583Swpaul sc = xsc; 226836270Swpaul 226936270Swpaul /* Disable interrupts */ 227039583Swpaul ints = CSR_READ_2(sc, TL_HOST_INT); 227139583Swpaul CSR_WRITE_2(sc, TL_HOST_INT, ints); 227236270Swpaul type = (ints << 16) & 0xFFFF0000; 227336270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 227436270Swpaul ints = (ints & TL_INT_MASK) >> 2; 227536270Swpaul 227636270Swpaul ifp = &sc->arpcom.ac_if; 227736270Swpaul 227836270Swpaul switch(ints) { 227936270Swpaul case (TL_INTR_INVALID): 228039583Swpaul#ifdef DIAGNOSTIC 228139583Swpaul printf("tl%d: got an invalid interrupt!\n", sc->tl_unit); 228239583Swpaul#endif 228339583Swpaul /* Re-enable interrupts but don't ack this one. */ 228439583Swpaul CMD_PUT(sc, type); 228539583Swpaul r = 0; 228636270Swpaul break; 228736270Swpaul case (TL_INTR_TXEOF): 228836270Swpaul r = tl_intvec_txeof((void *)sc, type); 228936270Swpaul break; 229036270Swpaul case (TL_INTR_TXEOC): 229136270Swpaul r = tl_intvec_txeoc((void *)sc, type); 229236270Swpaul break; 229336270Swpaul case (TL_INTR_STATOFLOW): 229439583Swpaul tl_stats_update(sc); 229539583Swpaul r = 1; 229636270Swpaul break; 229736270Swpaul case (TL_INTR_RXEOF): 229836270Swpaul r = tl_intvec_rxeof((void *)sc, type); 229936270Swpaul break; 230036270Swpaul case (TL_INTR_DUMMY): 230139583Swpaul printf("tl%d: got a dummy interrupt\n", sc->tl_unit); 230239583Swpaul r = 1; 230336270Swpaul break; 230436270Swpaul case (TL_INTR_ADCHK): 230536270Swpaul if (ivec) 230636270Swpaul r = tl_intvec_adchk((void *)sc, type); 230736270Swpaul else 230836270Swpaul r = tl_intvec_netsts((void *)sc, type); 230936270Swpaul break; 231036270Swpaul case (TL_INTR_RXEOC): 231136270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 231236270Swpaul break; 231336270Swpaul default: 231436270Swpaul printf("tl%d: bogus interrupt type\n", ifp->if_unit); 231536270Swpaul break; 231636270Swpaul } 231736270Swpaul 231836270Swpaul /* Re-enable interrupts */ 231937626Swpaul if (r) { 232039583Swpaul CMD_PUT(sc, TL_CMD_ACK | r | type); 232137626Swpaul } 232236270Swpaul 232337626Swpaul if (ifp->if_snd.ifq_head != NULL) 232437626Swpaul tl_start(ifp); 232537626Swpaul 232636270Swpaul return; 232736270Swpaul} 232836270Swpaul 232936270Swpaulstatic void tl_stats_update(xsc) 233036270Swpaul void *xsc; 233136270Swpaul{ 233236270Swpaul struct tl_softc *sc; 233336270Swpaul struct ifnet *ifp; 233436270Swpaul struct tl_stats tl_stats; 233536270Swpaul u_int32_t *p; 233648992Swpaul int s; 233736270Swpaul 233848992Swpaul s = splimp(); 233948992Swpaul 234036270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 234136270Swpaul 234236270Swpaul sc = xsc; 234336270Swpaul ifp = &sc->arpcom.ac_if; 234436270Swpaul 234536270Swpaul p = (u_int32_t *)&tl_stats; 234636270Swpaul 234739583Swpaul CSR_WRITE_2(sc, TL_DIO_ADDR, TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 234839583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 234939583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 235039583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 235139583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 235239583Swpaul *p++ = CSR_READ_4(sc, TL_DIO_DATA); 235336270Swpaul 235436270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 235536270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 235636270Swpaul tl_stats.tl_tx_multi_collision; 235736270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 235836270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 235936270Swpaul tl_rx_overrun(tl_stats); 236036270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 236136270Swpaul 236236270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 236336302Swpaul 236448992Swpaul splx(s); 236548992Swpaul 236636302Swpaul return; 236736270Swpaul} 236836270Swpaul 236936270Swpaul/* 237036270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 237136270Swpaul * pointers to the fragment pointers. 237236270Swpaul */ 237336270Swpaulstatic int tl_encap(sc, c, m_head) 237436270Swpaul struct tl_softc *sc; 237536270Swpaul struct tl_chain *c; 237636270Swpaul struct mbuf *m_head; 237736270Swpaul{ 237836270Swpaul int frag = 0; 237936270Swpaul struct tl_frag *f = NULL; 238036270Swpaul int total_len; 238136270Swpaul struct mbuf *m; 238236270Swpaul 238336270Swpaul /* 238436270Swpaul * Start packing the mbufs in this chain into 238536270Swpaul * the fragment pointers. Stop when we run out 238636270Swpaul * of fragments or hit the end of the mbuf chain. 238736270Swpaul */ 238836270Swpaul m = m_head; 238936270Swpaul total_len = 0; 239036270Swpaul 239136270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 239236270Swpaul if (m->m_len != 0) { 239336270Swpaul if (frag == TL_MAXFRAGS) 239436270Swpaul break; 239536270Swpaul total_len+= m->m_len; 239636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 239736270Swpaul vtophys(mtod(m, vm_offset_t)); 239836270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 239936270Swpaul frag++; 240036270Swpaul } 240136270Swpaul } 240236270Swpaul 240336270Swpaul /* 240436270Swpaul * Handle special cases. 240536270Swpaul * Special case #1: we used up all 10 fragments, but 240636270Swpaul * we have more mbufs left in the chain. Copy the 240736270Swpaul * data into an mbuf cluster. Note that we don't 240836270Swpaul * bother clearing the values in the other fragment 240936270Swpaul * pointers/counters; it wouldn't gain us anything, 241036270Swpaul * and would waste cycles. 241136270Swpaul */ 241236270Swpaul if (m != NULL) { 241336270Swpaul struct mbuf *m_new = NULL; 241436270Swpaul 241536270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 241636270Swpaul if (m_new == NULL) { 241736270Swpaul printf("tl%d: no memory for tx list", sc->tl_unit); 241836270Swpaul return(1); 241936270Swpaul } 242036270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 242136270Swpaul MCLGET(m_new, M_DONTWAIT); 242236270Swpaul if (!(m_new->m_flags & M_EXT)) { 242336270Swpaul m_freem(m_new); 242436270Swpaul printf("tl%d: no memory for tx list", 242536270Swpaul sc->tl_unit); 242636270Swpaul return(1); 242736270Swpaul } 242836270Swpaul } 242936270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 243036270Swpaul mtod(m_new, caddr_t)); 243136270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 243236270Swpaul m_freem(m_head); 243336270Swpaul m_head = m_new; 243436270Swpaul f = &c->tl_ptr->tl_frag[0]; 243536270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 243636270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 243736270Swpaul frag = 1; 243836270Swpaul } 243936270Swpaul 244036270Swpaul /* 244136270Swpaul * Special case #2: the frame is smaller than the minimum 244236270Swpaul * frame size. We have to pad it to make the chip happy. 244336270Swpaul */ 244436270Swpaul if (total_len < TL_MIN_FRAMELEN) { 244536270Swpaul if (frag == TL_MAXFRAGS) 244639583Swpaul printf("tl%d: all frags filled but " 244739583Swpaul "frame still to small!\n", sc->tl_unit); 244836270Swpaul f = &c->tl_ptr->tl_frag[frag]; 244936270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 245036270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 245136270Swpaul total_len += f->tlist_dcnt; 245236270Swpaul frag++; 245336270Swpaul } 245436270Swpaul 245536270Swpaul c->tl_mbuf = m_head; 245636270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 245736270Swpaul c->tl_ptr->tlist_frsize = total_len; 245836270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 245936270Swpaul c->tl_ptr->tlist_fptr = 0; 246036270Swpaul 246136270Swpaul return(0); 246236270Swpaul} 246336270Swpaul 246436270Swpaul/* 246536270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 246636270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 246736270Swpaul * copy of the pointers since the transmit list fragment pointers are 246836270Swpaul * physical addresses. 246936270Swpaul */ 247036270Swpaulstatic void tl_start(ifp) 247136270Swpaul struct ifnet *ifp; 247236270Swpaul{ 247336270Swpaul struct tl_softc *sc; 247436270Swpaul struct mbuf *m_head = NULL; 247536270Swpaul u_int32_t cmd; 247636270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 247736270Swpaul 247836270Swpaul sc = ifp->if_softc; 247936270Swpaul 248037626Swpaul if (sc->tl_autoneg) { 248137626Swpaul sc->tl_tx_pend = 1; 248237626Swpaul return; 248337626Swpaul } 248437626Swpaul 248536270Swpaul /* 248636270Swpaul * Check for an available queue slot. If there are none, 248736270Swpaul * punt. 248836270Swpaul */ 248936270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 249036270Swpaul ifp->if_flags |= IFF_OACTIVE; 249136270Swpaul return; 249236270Swpaul } 249336270Swpaul 249436270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 249536270Swpaul 249636270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 249736270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 249836270Swpaul if (m_head == NULL) 249936270Swpaul break; 250036270Swpaul 250136270Swpaul /* Pick a chain member off the free list. */ 250236270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 250336270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 250436270Swpaul 250536270Swpaul cur_tx->tl_next = NULL; 250636270Swpaul 250736270Swpaul /* Pack the data into the list. */ 250836270Swpaul tl_encap(sc, cur_tx, m_head); 250936270Swpaul 251036270Swpaul /* Chain it together */ 251136270Swpaul if (prev != NULL) { 251236270Swpaul prev->tl_next = cur_tx; 251336270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 251436270Swpaul } 251536270Swpaul prev = cur_tx; 251636270Swpaul 251736270Swpaul /* 251836270Swpaul * If there's a BPF listener, bounce a copy of this frame 251936270Swpaul * to him. 252036270Swpaul */ 252148645Sdes#if NBPF > 0 252236270Swpaul if (ifp->if_bpf) 252336270Swpaul bpf_mtap(ifp, cur_tx->tl_mbuf); 252436270Swpaul#endif 252536270Swpaul } 252636270Swpaul 252736270Swpaul /* 252841526Swpaul * If there are no packets queued, bail. 252941526Swpaul */ 253041526Swpaul if (cur_tx == NULL) 253141526Swpaul return; 253241526Swpaul 253341526Swpaul /* 253436270Swpaul * That's all we can stands, we can't stands no more. 253536270Swpaul * If there are no other transfers pending, then issue the 253636270Swpaul * TX GO command to the adapter to start things moving. 253736270Swpaul * Otherwise, just leave the data in the queue and let 253836270Swpaul * the EOF/EOC interrupt handler send. 253936270Swpaul */ 254036270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 254136270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 254236270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 254339583Swpaul 254436270Swpaul if (sc->tl_txeoc) { 254536270Swpaul sc->tl_txeoc = 0; 254639583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(start_tx->tl_ptr)); 254739583Swpaul cmd = CSR_READ_4(sc, TL_HOSTCMD); 254836270Swpaul cmd &= ~TL_CMD_RT; 254936270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 255039583Swpaul CMD_PUT(sc, cmd); 255136270Swpaul } 255236270Swpaul } else { 255336270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 255442146Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 255536270Swpaul } 255636270Swpaul 255736270Swpaul /* 255836270Swpaul * Set a timeout in case the chip goes out to lunch. 255936270Swpaul */ 256036270Swpaul ifp->if_timer = 5; 256136270Swpaul 256236270Swpaul return; 256336270Swpaul} 256436270Swpaul 256536270Swpaulstatic void tl_init(xsc) 256636270Swpaul void *xsc; 256736270Swpaul{ 256836270Swpaul struct tl_softc *sc = xsc; 256936270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 257036270Swpaul int s; 257136270Swpaul u_int16_t phy_sts; 257236270Swpaul 257339583Swpaul if (sc->tl_autoneg) 257439583Swpaul return; 257539583Swpaul 257636270Swpaul s = splimp(); 257736270Swpaul 257836270Swpaul ifp = &sc->arpcom.ac_if; 257936270Swpaul 258036270Swpaul /* 258136270Swpaul * Cancel pending I/O. 258236270Swpaul */ 258336270Swpaul tl_stop(sc); 258436270Swpaul 258536270Swpaul /* 258636270Swpaul * Set 'capture all frames' bit for promiscuous mode. 258736270Swpaul */ 258839583Swpaul if (ifp->if_flags & IFF_PROMISC) 258939583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_CAF); 259039583Swpaul else 259139583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_CAF); 259236270Swpaul 259336270Swpaul /* 259436270Swpaul * Set capture broadcast bit to capture broadcast frames. 259536270Swpaul */ 259639583Swpaul if (ifp->if_flags & IFF_BROADCAST) 259739583Swpaul tl_dio_clrbit(sc, TL_NETCMD, TL_CMD_NOBRX); 259839583Swpaul else 259939583Swpaul tl_dio_setbit(sc, TL_NETCMD, TL_CMD_NOBRX); 260036270Swpaul 260136270Swpaul /* Init our MAC address */ 260241656Swpaul tl_setfilt(sc, (caddr_t)&sc->arpcom.ac_enaddr, 0); 260336270Swpaul 260439583Swpaul /* Init multicast filter, if needed. */ 260539583Swpaul tl_setmulti(sc); 260639583Swpaul 260736270Swpaul /* Init circular RX list. */ 260839583Swpaul if (tl_list_rx_init(sc) == ENOBUFS) { 260939583Swpaul printf("tl%d: initialization failed: no " 261039583Swpaul "memory for rx buffers\n", sc->tl_unit); 261139583Swpaul tl_stop(sc); 261236270Swpaul return; 261336270Swpaul } 261436270Swpaul 261536270Swpaul /* Init TX pointers. */ 261636270Swpaul tl_list_tx_init(sc); 261736270Swpaul 261836270Swpaul /* 261936270Swpaul * Enable PHY interrupts. 262036270Swpaul */ 262136270Swpaul phy_sts = tl_phy_readreg(sc, TL_PHY_CTL); 262236270Swpaul phy_sts |= PHY_CTL_INTEN; 262336270Swpaul tl_phy_writereg(sc, TL_PHY_CTL, phy_sts); 262436270Swpaul 262536270Swpaul /* Enable MII interrupts. */ 262639583Swpaul tl_dio_setbit(sc, TL_NETSIO, TL_SIO_MINTEN); 262736270Swpaul 262839583Swpaul /* Enable PCI interrupts. */ 262939583Swpaul CMD_SET(sc, TL_CMD_INTSON); 263036270Swpaul 263136270Swpaul /* Load the address of the rx list */ 263239583Swpaul CMD_SET(sc, TL_CMD_RT); 263339583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, vtophys(&sc->tl_ldata->tl_rx_list[0])); 263436270Swpaul 263538030Swpaul /* 263639583Swpaul * XXX This is a kludge to handle adapters with the Micro Linear 263739583Swpaul * ML6692 100BaseTX PHY, which only supports 100Mbps modes and 263839583Swpaul * relies on the controller's internal 10Mbps PHY to provide 263939583Swpaul * 10Mbps modes. The ML6692 always shows up with a vendor/device ID 264039583Swpaul * of 0 (it doesn't actually have vendor/device ID registers) 264139583Swpaul * so we use that property to detect it. In theory there ought to 264239583Swpaul * be a better way to 'spot the looney' but I can't find one. 264339583Swpaul */ 264439583Swpaul if (!sc->tl_phy_vid) { 264539583Swpaul u_int8_t addr = 0; 264639583Swpaul u_int16_t bmcr; 264738030Swpaul 264839583Swpaul bmcr = tl_phy_readreg(sc, PHY_BMCR); 264939583Swpaul addr = sc->tl_phy_addr; 265039583Swpaul sc->tl_phy_addr = TL_PHYADDR_MAX; 265139583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 265239583Swpaul if (bmcr & PHY_BMCR_SPEEDSEL) 265339583Swpaul tl_phy_writereg(sc, PHY_BMCR, PHY_BMCR_ISOLATE); 265439583Swpaul else 265539583Swpaul tl_phy_writereg(sc, PHY_BMCR, bmcr); 265639583Swpaul sc->tl_phy_addr = addr; 265739583Swpaul } 265838030Swpaul 265936270Swpaul /* Send the RX go command */ 266039583Swpaul CMD_SET(sc, TL_CMD_GO|TL_CMD_RT); 266136270Swpaul 266236270Swpaul ifp->if_flags |= IFF_RUNNING; 266336270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 266436270Swpaul 266536270Swpaul (void)splx(s); 266636270Swpaul 266736270Swpaul /* Start the stats update counter */ 266836270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 266936270Swpaul 267036270Swpaul return; 267136270Swpaul} 267236270Swpaul 267336270Swpaul/* 267436270Swpaul * Set media options. 267536270Swpaul */ 267636270Swpaulstatic int tl_ifmedia_upd(ifp) 267736270Swpaul struct ifnet *ifp; 267836270Swpaul{ 267936270Swpaul struct tl_softc *sc; 268036270Swpaul struct ifmedia *ifm; 268136270Swpaul 268236270Swpaul sc = ifp->if_softc; 268336270Swpaul ifm = &sc->ifmedia; 268436270Swpaul 268536270Swpaul if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 268636270Swpaul return(EINVAL); 268736270Swpaul 268836270Swpaul if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) 268936270Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 269036270Swpaul else 269136270Swpaul tl_setmode(sc, ifm->ifm_media); 269236270Swpaul 269336270Swpaul return(0); 269436270Swpaul} 269536270Swpaul 269636270Swpaul/* 269736270Swpaul * Report current media status. 269836270Swpaul */ 269936270Swpaulstatic void tl_ifmedia_sts(ifp, ifmr) 270036270Swpaul struct ifnet *ifp; 270136270Swpaul struct ifmediareq *ifmr; 270236270Swpaul{ 270336270Swpaul u_int16_t phy_ctl; 270436270Swpaul u_int16_t phy_sts; 270536270Swpaul struct tl_softc *sc; 270636270Swpaul 270736270Swpaul sc = ifp->if_softc; 270836270Swpaul 270936270Swpaul ifmr->ifm_active = IFM_ETHER; 271036270Swpaul 271145155Swpaul if (sc->tl_bitrate) { 271245155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD1) 271345155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 271445155Swpaul else 271545155Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 271645155Swpaul if (tl_dio_read8(sc, TL_ACOMMIT) & TL_AC_MTXD3) 271745155Swpaul ifmr->ifm_active |= IFM_HDX; 271845155Swpaul else 271945155Swpaul ifmr->ifm_active |= IFM_FDX; 272045155Swpaul return; 272145155Swpaul } 272245155Swpaul 272336270Swpaul phy_ctl = tl_phy_readreg(sc, PHY_BMCR); 272436270Swpaul phy_sts = tl_phy_readreg(sc, TL_PHY_CTL); 272536270Swpaul 272636270Swpaul if (phy_sts & PHY_CTL_AUISEL) 272739583Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_5; 272836270Swpaul 272936270Swpaul if (phy_ctl & PHY_BMCR_LOOPBK) 273039583Swpaul ifmr->ifm_active = IFM_ETHER|IFM_LOOP; 273136270Swpaul 273236270Swpaul if (phy_ctl & PHY_BMCR_SPEEDSEL) 273339583Swpaul ifmr->ifm_active = IFM_ETHER|IFM_100_TX; 273436270Swpaul else 273539583Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 273636270Swpaul 273736270Swpaul if (phy_ctl & PHY_BMCR_DUPLEX) { 273836270Swpaul ifmr->ifm_active |= IFM_FDX; 273936270Swpaul ifmr->ifm_active &= ~IFM_HDX; 274036270Swpaul } else { 274136270Swpaul ifmr->ifm_active &= ~IFM_FDX; 274236270Swpaul ifmr->ifm_active |= IFM_HDX; 274336270Swpaul } 274436270Swpaul 274536270Swpaul return; 274636270Swpaul} 274736270Swpaul 274836270Swpaulstatic int tl_ioctl(ifp, command, data) 274936270Swpaul struct ifnet *ifp; 275036735Sdfr u_long command; 275136270Swpaul caddr_t data; 275236270Swpaul{ 275336270Swpaul struct tl_softc *sc = ifp->if_softc; 275436270Swpaul struct ifreq *ifr = (struct ifreq *) data; 275536270Swpaul int s, error = 0; 275636270Swpaul 275736270Swpaul s = splimp(); 275836270Swpaul 275936270Swpaul switch(command) { 276036270Swpaul case SIOCSIFADDR: 276136270Swpaul case SIOCGIFADDR: 276236270Swpaul case SIOCSIFMTU: 276336270Swpaul error = ether_ioctl(ifp, command, data); 276436270Swpaul break; 276536270Swpaul case SIOCSIFFLAGS: 276636270Swpaul if (ifp->if_flags & IFF_UP) { 276736270Swpaul tl_init(sc); 276836270Swpaul } else { 276936270Swpaul if (ifp->if_flags & IFF_RUNNING) { 277036270Swpaul tl_stop(sc); 277136270Swpaul } 277236270Swpaul } 277336270Swpaul error = 0; 277436270Swpaul break; 277536270Swpaul case SIOCADDMULTI: 277636270Swpaul case SIOCDELMULTI: 277736270Swpaul tl_setmulti(sc); 277836270Swpaul error = 0; 277936270Swpaul break; 278036270Swpaul case SIOCSIFMEDIA: 278136270Swpaul case SIOCGIFMEDIA: 278236270Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 278336270Swpaul break; 278436270Swpaul default: 278536270Swpaul error = EINVAL; 278636270Swpaul break; 278736270Swpaul } 278836270Swpaul 278936270Swpaul (void)splx(s); 279036270Swpaul 279136270Swpaul return(error); 279236270Swpaul} 279336270Swpaul 279436270Swpaulstatic void tl_watchdog(ifp) 279536270Swpaul struct ifnet *ifp; 279636270Swpaul{ 279736270Swpaul struct tl_softc *sc; 279836270Swpaul u_int16_t bmsr; 279936270Swpaul 280036270Swpaul sc = ifp->if_softc; 280136270Swpaul 280236270Swpaul if (sc->tl_autoneg) { 280336270Swpaul tl_autoneg(sc, TL_FLAG_DELAYTIMEO, 1); 280436270Swpaul return; 280536270Swpaul } 280636270Swpaul 280736270Swpaul /* Check that we're still connected. */ 280836270Swpaul tl_phy_readreg(sc, PHY_BMSR); 280936270Swpaul bmsr = tl_phy_readreg(sc, PHY_BMSR); 281036270Swpaul if (!(bmsr & PHY_BMSR_LINKSTAT)) { 281136270Swpaul printf("tl%d: no carrier\n", sc->tl_unit); 281236270Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 281336270Swpaul } else 281436270Swpaul printf("tl%d: device timeout\n", sc->tl_unit); 281536270Swpaul 281636270Swpaul ifp->if_oerrors++; 281736270Swpaul 281836270Swpaul tl_init(sc); 281936270Swpaul 282036270Swpaul return; 282136270Swpaul} 282236270Swpaul 282336270Swpaul/* 282436270Swpaul * Stop the adapter and free any mbufs allocated to the 282536270Swpaul * RX and TX lists. 282636270Swpaul */ 282736270Swpaulstatic void tl_stop(sc) 282836270Swpaul struct tl_softc *sc; 282936270Swpaul{ 283036270Swpaul register int i; 283136270Swpaul struct ifnet *ifp; 283236270Swpaul 283336270Swpaul ifp = &sc->arpcom.ac_if; 283436270Swpaul 283536270Swpaul /* Stop the stats updater. */ 283636270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 283736270Swpaul 283836270Swpaul /* Stop the transmitter */ 283939583Swpaul CMD_CLR(sc, TL_CMD_RT); 284039583Swpaul CMD_SET(sc, TL_CMD_STOP); 284139583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 284236270Swpaul 284336270Swpaul /* Stop the receiver */ 284439583Swpaul CMD_SET(sc, TL_CMD_RT); 284539583Swpaul CMD_SET(sc, TL_CMD_STOP); 284639583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 284736270Swpaul 284836270Swpaul /* 284936270Swpaul * Disable host interrupts. 285036270Swpaul */ 285139583Swpaul CMD_SET(sc, TL_CMD_INTSOFF); 285236270Swpaul 285336270Swpaul /* 285436270Swpaul * Disable MII interrupts. 285536270Swpaul */ 285639583Swpaul tl_dio_clrbit(sc, TL_NETSIO, TL_SIO_MINTEN); 285736270Swpaul 285836270Swpaul /* 285936270Swpaul * Clear list pointer. 286036270Swpaul */ 286139583Swpaul CSR_WRITE_4(sc, TL_CH_PARM, 0); 286236270Swpaul 286336270Swpaul /* 286436270Swpaul * Free the RX lists. 286536270Swpaul */ 286636270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 286736270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 286836270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 286936270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 287036270Swpaul } 287136270Swpaul } 287236270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 287336270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 287436270Swpaul 287536270Swpaul /* 287636270Swpaul * Free the TX list buffers. 287736270Swpaul */ 287836270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 287936270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 288036270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 288136270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 288236270Swpaul } 288336270Swpaul } 288436270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 288536270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 288636270Swpaul 288736270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 288836270Swpaul 288936270Swpaul return; 289036270Swpaul} 289136270Swpaul 289236270Swpaul/* 289336270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 289436270Swpaul * get confused by errant DMAs when rebooting. 289536270Swpaul */ 289648992Swpaulstatic void tl_shutdown(dev) 289748992Swpaul device_t dev; 289836270Swpaul{ 289939583Swpaul struct tl_softc *sc; 290036270Swpaul 290148992Swpaul sc = device_get_softc(dev); 290236270Swpaul 290339583Swpaul tl_stop(sc); 290436270Swpaul 290536270Swpaul return; 290636270Swpaul} 2907