if_tl.c revision 36317
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 * 3236317Swpaul * $Id: if_tl.c,v 1.4 1998/05/22 15:32:22 wpaul Exp $ 3336270Swpaul */ 3436270Swpaul 3536270Swpaul/* 3636270Swpaul * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. 3736270Swpaul * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, 3836270Swpaul * the National Semiconductor DP83840A physical interface and the 3936270Swpaul * Microchip Technology 24Cxx series serial EEPROM. 4036270Swpaul * 4136270Swpaul * Written using the following three 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) 4636270Swpaul * 4736270Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4836270Swpaul * Electrical Engineering Department 4936270Swpaul * Columbia University, New York City 5036270Swpaul */ 5136270Swpaul 5236270Swpaul/* 5336270Swpaul * Some notes about the ThunderLAN: 5436270Swpaul * 5536270Swpaul * The ThunderLAN controller is a single chip containing PCI controller 5636270Swpaul * logic, approximately 3K of on-board SRAM, a LAN controller, and media 5736270Swpaul * independent interface (MII). The MII allows the ThunderLAN chip to 5836270Swpaul * control up to 32 different physical interfaces (PHYs). The ThunderLAN 5936270Swpaul * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller 6036270Swpaul * to act as a complete ethernet interface. 6136270Swpaul * 6236270Swpaul * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards 6336270Swpaul * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec 6436270Swpaul * in full or half duplex. Some of the Compaq Deskpro machines use a 6536270Swpaul * Level 1 LXT970 PHY with the same capabilities. A serial EEPROM is also 6636270Swpaul * attached to the ThunderLAN chip to provide power-up default register 6736270Swpaul * settings and for storing the adapter's stattion address. Although not 6836270Swpaul * supported by this driver, the ThunderLAN chip can also be connected 6936270Swpaul * to token ring PHYs. 7036270Swpaul * 7136270Swpaul * It is important to note that while it is possible to have multiple 7236270Swpaul * PHYs attached to the ThunderLAN's MII, only one PHY may be active at 7336270Swpaul * any time. (This makes me wonder exactly how the dual port Compaq 7436270Swpaul * adapter is supposed to work.) This driver attempts to compensate for 7536270Swpaul * this in the following way: 7636270Swpaul * 7736270Swpaul * When the ThunderLAN chip is probed, the probe routine attempts to 7836270Swpaul * locate all attached PHYs by checking all 32 possible PHY addresses 7936270Swpaul * (0x00 to 0x1F). Each PHY is attached as a separate logical interface. 8036270Swpaul * The driver allows any one interface to be brought up at any given 8136270Swpaul * time: if an attempt is made to bring up a second PHY while another 8236270Swpaul * PHY is already enabled, the driver will return an error. 8336270Swpaul * 8436270Swpaul * The ThunderLAN has a set of registers which can be used to issue 8536270Swpaul * command, acknowledge interrupts, and to manipulate other internal 8636270Swpaul * registers on its DIO bus. The primary registers can be accessed 8736270Swpaul * using either programmed I/O (inb/outb) or via PCI memory mapping, 8836270Swpaul * depending on how the card is configured during the PCI probing 8936270Swpaul * phase. It is even possible to have both PIO and memory mapped 9036270Swpaul * access turned on at the same time. 9136270Swpaul * 9236270Swpaul * Frame reception and transmission with the ThunderLAN chip is done 9336270Swpaul * using frame 'lists.' A list structure looks more or less like this: 9436270Swpaul * 9536270Swpaul * struct tl_frag { 9636270Swpaul * u_int32_t fragment_address; 9736270Swpaul * u_int32_t fragment_size; 9836270Swpaul * }; 9936270Swpaul * struct tl_list { 10036270Swpaul * u_int32_t forward_pointer; 10136270Swpaul * u_int16_t cstat; 10236270Swpaul * u_int16_t frame_size; 10336270Swpaul * struct tl_frag fragments[10]; 10436270Swpaul * }; 10536270Swpaul * 10636270Swpaul * The forward pointer in the list header can be either a 0 or the address 10736270Swpaul * of another list, which allows several lists to be linked together. Each 10836270Swpaul * list contains up to 10 fragment descriptors. This means the chip allows 10936270Swpaul * ethernet frames to be broken up into up to 10 chunks for transfer to 11036270Swpaul * and from the SRAM. Note that the forward pointer and fragment buffer 11136270Swpaul * addresses are physical memory addresses, not virtual. Note also that 11236270Swpaul * a single ethernet frame can not span lists: if the host wants to 11336270Swpaul * transmit a frame and the frame data is split up over more than 10 11436270Swpaul * buffers, the frame has to collapsed before it can be transmitted. 11536270Swpaul * 11636270Swpaul * To receive frames, the driver sets up a number of lists and populates 11736270Swpaul * the fragment descriptors, then it sends an RX GO command to the chip. 11836270Swpaul * When a frame is received, the chip will DMA it into the memory regions 11936270Swpaul * specified by the fragment descriptors and then trigger an RX 'end of 12036270Swpaul * frame interrupt' when done. The driver may choose to use only one 12136270Swpaul * fragment per list; this may result is slighltly less efficient use 12236270Swpaul * of memory in exchange for improving performance. 12336270Swpaul * 12436270Swpaul * To transmit frames, the driver again sets up lists and fragment 12536270Swpaul * descriptors, only this time the buffers contain frame data that 12636270Swpaul * is to be DMA'ed into the chip instead of out of it. Once the chip 12736270Swpaul * has transfered the data into its on-board SRAM, it will trigger a 12836270Swpaul * TX 'end of frame' interrupt. It will also generate an 'end of channel' 12936270Swpaul * interrupt when it reaches the end of the list. 13036270Swpaul */ 13136270Swpaul 13236270Swpaul/* 13336270Swpaul * Some notes about this driver: 13436270Swpaul * 13536270Swpaul * The ThunderLAN chip provides a couple of different ways to organize 13636270Swpaul * reception, transmission and interrupt handling. The simplest approach 13736270Swpaul * is to use one list each for transmission and reception. In this mode, 13836270Swpaul * the ThunderLAN will generate two interrupts for every received frame 13936270Swpaul * (one RX EOF and one RX EOC) and two for each transmitted frame (one 14036270Swpaul * TX EOF and one TX EOC). This may make the driver simpler but it hurts 14136270Swpaul * performance to have to handle so many interrupts. 14236270Swpaul * 14336270Swpaul * Initially I wanted to create a circular list of receive buffers so 14436270Swpaul * that the ThunderLAN chip would think there was an infinitely long 14536270Swpaul * receive channel and never deliver an RXEOC interrupt. However this 14636270Swpaul * doesn't work correctly under heavy load: while the manual says the 14736270Swpaul * chip will trigger an RXEOF interrupt each time a frame is copied into 14836270Swpaul * memory, you can't count on the chip waiting around for you to acknowledge 14936270Swpaul * the interrupt before it starts trying to DMA the next frame. The result 15036270Swpaul * is that the chip might traverse the entire circular list and then wrap 15136270Swpaul * around before you have a chance to do anything about it. Consequently, 15236270Swpaul * the receive list is terminated (with a 0 in the forward pointer in the 15336270Swpaul * last element). Each time an RXEOF interrupt arrives, the used list 15436270Swpaul * is shifted to the end of the list. This gives the appearance of an 15536270Swpaul * infinitely large RX chain so long as the driver doesn't fall behind 15636270Swpaul * the chip and allow all of the lists to be filled up. 15736270Swpaul * 15836270Swpaul * If all the lists are filled, the adapter will deliver an RX 'end of 15936270Swpaul * channel' interrupt when it hits the 0 forward pointer at the end of 16036270Swpaul * the chain. The RXEOC handler then cleans out the RX chain and resets 16136270Swpaul * the list head pointer in the ch_parm register and restarts the receiver. 16236270Swpaul * 16336270Swpaul * For frame transmission, it is possible to program the ThunderLAN's 16436270Swpaul * transmit interrupt threshold so that the chip can acknowledge multiple 16536270Swpaul * lists with only a single TX EOF interrupt. This allows the driver to 16636270Swpaul * queue several frames in one shot, and only have to handle a total 16736270Swpaul * two interrupts (one TX EOF and one TX EOC) no matter how many frames 16836270Swpaul * are transmitted. Frame transmission is done directly out of the 16936270Swpaul * mbufs passed to the tl_start() routine via the interface send queue. 17036270Swpaul * The driver simply sets up the fragment descriptors in the transmit 17136270Swpaul * lists to point to the mbuf data regions and sends a TX GO command. 17236270Swpaul * 17336270Swpaul * Note that since the RX and TX lists themselves are always used 17436270Swpaul * only by the driver, the are malloc()ed once at driver initialization 17536270Swpaul * time and never free()ed. 17636270Swpaul * 17736270Swpaul * Also, in order to remain as platform independent as possible, this 17836270Swpaul * driver uses memory mapped register access to manipulate the card 17936270Swpaul * as opposed to programmed I/O. This avoids the use of the inb/outb 18036270Swpaul * (and related) instructions which are specific to the i386 platform. 18136270Swpaul * 18236270Swpaul * Using these techniques, this driver achieves very high performance 18336270Swpaul * by minimizing the amount of interrupts generated during large 18436270Swpaul * transfers and by completely avoiding buffer copies. Frame transfer 18536270Swpaul * to and from the ThunderLAN chip is performed entirely by the chip 18636270Swpaul * itself thereby reducing the load on the host CPU. 18736270Swpaul */ 18836270Swpaul 18936270Swpaul#include "bpfilter.h" 19036270Swpaul 19136270Swpaul#include <sys/param.h> 19236270Swpaul#include <sys/systm.h> 19336270Swpaul#include <sys/sockio.h> 19436270Swpaul#include <sys/mbuf.h> 19536270Swpaul#include <sys/malloc.h> 19636270Swpaul#include <sys/kernel.h> 19736270Swpaul#include <sys/socket.h> 19836270Swpaul#include <sys/syslog.h> 19936270Swpaul 20036270Swpaul#include <net/if.h> 20136270Swpaul#include <net/if_arp.h> 20236270Swpaul#include <net/ethernet.h> 20336270Swpaul#include <net/if_dl.h> 20436270Swpaul#include <net/if_mib.h> 20536270Swpaul#include <net/if_media.h> 20636270Swpaul#include <net/if_types.h> 20736270Swpaul 20836270Swpaul#ifdef INET 20936270Swpaul#include <netinet/in.h> 21036270Swpaul#include <netinet/in_systm.h> 21136270Swpaul#include <netinet/in_var.h> 21236270Swpaul#include <netinet/ip.h> 21336270Swpaul#include <netinet/if_ether.h> 21436270Swpaul#endif 21536270Swpaul 21636270Swpaul#ifdef IPX 21736270Swpaul#include <netipx/ipx.h> 21836270Swpaul#include <netipx/ipx_if.h> 21936270Swpaul#endif 22036270Swpaul 22136270Swpaul#ifdef NS 22236270Swpaul#include <netns/ns.h> 22336270Swpaul#include <netns/ns_if.h> 22436270Swpaul#endif 22536270Swpaul 22636270Swpaul#if NBPFILTER > 0 22736270Swpaul#include <net/bpf.h> 22836270Swpaul#include <net/bpfdesc.h> 22936270Swpaul#endif 23036270Swpaul 23136270Swpaul#include <vm/vm.h> /* for vtophys */ 23236270Swpaul#include <vm/vm_param.h> /* for vtophys */ 23336270Swpaul#include <vm/pmap.h> /* for vtophys */ 23436270Swpaul#include <machine/clock.h> /* for DELAY */ 23536270Swpaul 23636270Swpaul#include <pci/pcireg.h> 23736270Swpaul#include <pci/pcivar.h> 23836270Swpaul 23936270Swpaul#include <pci/if_tlreg.h> 24036270Swpaul 24136270Swpaul#ifndef lint 24236270Swpaulstatic char rcsid[] = 24336317Swpaul "$Id: if_tl.c,v 1.4 1998/05/22 15:32:22 wpaul Exp $"; 24436270Swpaul#endif 24536270Swpaul 24636270Swpaul/* 24736270Swpaul * Various supported device vendors/types and their names. 24836270Swpaul */ 24936270Swpaul 25036270Swpaulstatic struct tl_type tl_devs[] = { 25136270Swpaul { TI_VENDORID, TI_DEVICEID_THUNDERLAN, 25236270Swpaul "Texas Instruments ThunderLAN" }, 25336270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, 25436270Swpaul "Compaq Netelligent 10" }, 25536270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, 25636270Swpaul "Compaq Netelligent 10/100" }, 25736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, 25836270Swpaul "Compaq Netelligent 10/100 Proliant" }, 25936270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, 26036270Swpaul "Compaq Netelligent 10/100 Dual Port" }, 26136270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, 26236270Swpaul "Compaq NetFlex-3/P Integrated" }, 26336270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, 26436270Swpaul "Compaq NetFlex-3/P" }, 26536270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, 26636270Swpaul "Compaq NetFlex 3/P w/ BNC" }, 26736270Swpaul { COMPAQ_VENDORID, COMPAQ_DEVICEID_DESKPRO_4000_5233MMX, 26836270Swpaul "Compaq Deskpro 4000 5233MMX" }, 26936270Swpaul { 0, 0, NULL } 27036270Swpaul}; 27136270Swpaul 27236270Swpaul/* 27336270Swpaul * Various supported PHY vendors/types and their names. Note that 27436270Swpaul * this driver will work with pretty much any MII-compliant PHY, 27536270Swpaul * so failure to positively identify the chip is not a fatal error. 27636270Swpaul */ 27736270Swpaul 27836270Swpaulstatic struct tl_type tl_phys[] = { 27936270Swpaul { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, 28036270Swpaul { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, 28136270Swpaul { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, 28236270Swpaul { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, 28336270Swpaul { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, 28436270Swpaul { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, 28536270Swpaul { 0, 0, "<MII-compliant physical interface>" } 28636270Swpaul}; 28736270Swpaul 28836270Swpaulstatic struct tl_iflist *tl_iflist = NULL; 28936270Swpaulstatic unsigned long tl_count; 29036270Swpaul 29136270Swpaulstatic char *tl_probe __P((pcici_t, pcidi_t)); 29236270Swpaulstatic void tl_attach_ctlr __P((pcici_t, int)); 29336270Swpaulstatic int tl_attach_phy __P((struct tl_csr *, int, char *, 29436270Swpaul int, struct tl_iflist *)); 29536270Swpaulstatic int tl_intvec_invalid __P((void *, u_int32_t)); 29636270Swpaulstatic int tl_intvec_dummy __P((void *, u_int32_t)); 29736270Swpaulstatic int tl_intvec_rxeoc __P((void *, u_int32_t)); 29836270Swpaulstatic int tl_intvec_txeoc __P((void *, u_int32_t)); 29936270Swpaulstatic int tl_intvec_txeof __P((void *, u_int32_t)); 30036270Swpaulstatic int tl_intvec_rxeof __P((void *, u_int32_t)); 30136270Swpaulstatic int tl_intvec_adchk __P((void *, u_int32_t)); 30236270Swpaulstatic int tl_intvec_netsts __P((void *, u_int32_t)); 30336270Swpaulstatic int tl_intvec_statoflow __P((void *, u_int32_t)); 30436270Swpaul 30536270Swpaulstatic int tl_newbuf __P((struct tl_softc *, struct tl_chain *)); 30636270Swpaulstatic void tl_stats_update __P((void *)); 30736270Swpaulstatic int tl_encap __P((struct tl_softc *, struct tl_chain *, 30836270Swpaul struct mbuf *)); 30936270Swpaul 31036270Swpaulstatic void tl_intr __P((void *)); 31136270Swpaulstatic void tl_start __P((struct ifnet *)); 31236270Swpaulstatic int tl_ioctl __P((struct ifnet *, int, caddr_t)); 31336270Swpaulstatic void tl_init __P((void *)); 31436270Swpaulstatic void tl_stop __P((struct tl_softc *)); 31536270Swpaulstatic void tl_watchdog __P((struct ifnet *)); 31636270Swpaulstatic void tl_shutdown __P((int, void *)); 31736270Swpaulstatic int tl_ifmedia_upd __P((struct ifnet *)); 31836270Swpaulstatic void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 31936270Swpaul 32036270Swpaulstatic u_int8_t tl_eeprom_putbyte __P((struct tl_csr *, u_int8_t)); 32136270Swpaulstatic u_int8_t tl_eeprom_getbyte __P((struct tl_csr *, u_int8_t , 32236270Swpaul u_int8_t * )); 32336270Swpaulstatic int tl_read_eeprom __P((struct tl_csr *, caddr_t, int, int)); 32436270Swpaul 32536270Swpaulstatic void tl_mii_sync __P((struct tl_csr *)); 32636270Swpaulstatic void tl_mii_send __P((struct tl_csr *, u_int32_t, int)); 32736270Swpaulstatic int tl_mii_readreg __P((struct tl_csr *, struct tl_mii_frame *)); 32836270Swpaulstatic int tl_mii_writereg __P((struct tl_csr *, struct tl_mii_frame *)); 32936270Swpaulstatic u_int16_t tl_phy_readreg __P((struct tl_softc *, int)); 33036270Swpaulstatic void tl_phy_writereg __P((struct tl_softc *, u_int16_t, u_int16_t)); 33136270Swpaul 33236270Swpaulstatic void tl_autoneg __P((struct tl_softc *, int, int)); 33336270Swpaulstatic void tl_setmode __P((struct tl_softc *, int)); 33436270Swpaulstatic int tl_calchash __P((char *)); 33536270Swpaulstatic void tl_setmulti __P((struct tl_softc *)); 33636270Swpaulstatic void tl_softreset __P((struct tl_csr *, int)); 33736270Swpaulstatic int tl_list_rx_init __P((struct tl_softc *)); 33836270Swpaulstatic int tl_list_tx_init __P((struct tl_softc *)); 33936270Swpaul 34036270Swpaul/* 34136270Swpaul * ThunderLAN adapters typically have a serial EEPROM containing 34236270Swpaul * configuration information. The main reason we're interested in 34336270Swpaul * it is because it also contains the adapters's station address. 34436270Swpaul * 34536270Swpaul * Access to the EEPROM is a bit goofy since it is a serial device: 34636270Swpaul * you have to do reads and writes one bit at a time. The state of 34736270Swpaul * the DATA bit can only change while the CLOCK line is held low. 34836270Swpaul * Transactions work basically like this: 34936270Swpaul * 35036270Swpaul * 1) Send the EEPROM_START sequence to prepare the EEPROM for 35136270Swpaul * accepting commands. This pulls the clock high, sets 35236270Swpaul * the data bit to 0, enables transmission to the EEPROM, 35336270Swpaul * pulls the data bit up to 1, then pulls the clock low. 35436270Swpaul * The idea is to do a 0 to 1 transition of the data bit 35536270Swpaul * while the clock pin is held high. 35636270Swpaul * 35736270Swpaul * 2) To write a bit to the EEPROM, set the TXENABLE bit, then 35836270Swpaul * set the EDATA bit to send a 1 or clear it to send a 0. 35936270Swpaul * Finally, set and then clear ECLOK. Strobing the clock 36036270Swpaul * transmits the bit. After 8 bits have been written, the 36136270Swpaul * EEPROM should respond with an ACK, which should be read. 36236270Swpaul * 36336270Swpaul * 3) To read a bit from the EEPROM, clear the TXENABLE bit, 36436270Swpaul * then set ECLOK. The bit can then be read by reading EDATA. 36536270Swpaul * ECLOCK should then be cleared again. This can be repeated 36636270Swpaul * 8 times to read a whole byte, after which the 36736270Swpaul * 36836270Swpaul * 4) We need to send the address byte to the EEPROM. For this 36936270Swpaul * we have to send the write control byte to the EEPROM to 37036270Swpaul * tell it to accept data. The byte is 0xA0. The EEPROM should 37136270Swpaul * ack this. The address byte can be send after that. 37236270Swpaul * 37336270Swpaul * 5) Now we have to tell the EEPROM to send us data. For that we 37436270Swpaul * have to transmit the read control byte, which is 0xA1. This 37536270Swpaul * byte should also be acked. We can then read the data bits 37636270Swpaul * from the EEPROM. 37736270Swpaul * 37836270Swpaul * 6) When we're all finished, send the EEPROM_STOP sequence. 37936270Swpaul * 38036270Swpaul * Note that we use the ThunderLAN's NetSio register to access the 38136270Swpaul * EEPROM, however there is an alternate method. There is a PCI NVRAM 38236270Swpaul * register at PCI offset 0xB4 which can also be used with minor changes. 38336270Swpaul * The difference is that access to PCI registers via pci_conf_read() 38436270Swpaul * and pci_conf_write() is done using programmed I/O, which we want to 38536270Swpaul * avoid. 38636270Swpaul */ 38736270Swpaul 38836270Swpaul/* 38936270Swpaul * Note that EEPROM_START leaves transmission enabled. 39036270Swpaul */ 39136270Swpaul#define EEPROM_START \ 39236270Swpaul DIO_SEL(TL_NETSIO); \ 39336270Swpaul DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock pin high */ \ 39436270Swpaul DIO_BYTE1_SET(TL_SIO_EDATA); /* Set DATA bit to 1 */ \ 39536270Swpaul DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit to write bit */ \ 39636270Swpaul DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA bit to 0 again */ \ 39736270Swpaul DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */ 39836270Swpaul 39936270Swpaul/* 40036270Swpaul * EEPROM_STOP ends access to the EEPROM and clears the ETXEN bit so 40136270Swpaul * that no further data can be written to the EEPROM I/O pin. 40236270Swpaul */ 40336270Swpaul#define EEPROM_STOP \ 40436270Swpaul DIO_SEL(TL_NETSIO); \ 40536270Swpaul DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit */ \ 40636270Swpaul DIO_BYTE1_CLR(TL_SIO_EDATA); /* Pull DATA to 0 */ \ 40736270Swpaul DIO_BYTE1_SET(TL_SIO_ECLOK); /* Pull clock high */ \ 40836270Swpaul DIO_BYTE1_SET(TL_SIO_ETXEN); /* Enable xmit */ \ 40936270Swpaul DIO_BYTE1_SET(TL_SIO_EDATA); /* Toggle DATA to 1 */ \ 41036270Swpaul DIO_BYTE1_CLR(TL_SIO_ETXEN); /* Disable xmit. */ \ 41136270Swpaul DIO_BYTE1_CLR(TL_SIO_ECLOK); /* Pull clock low again */ 41236270Swpaul 41336270Swpaul/* 41436270Swpaul * Send an instruction or address to the EEPROM, check for ACK. 41536270Swpaul */ 41636270Swpaulstatic u_int8_t tl_eeprom_putbyte(csr, byte) 41736270Swpaul struct tl_csr *csr; 41836270Swpaul u_int8_t byte; 41936270Swpaul{ 42036270Swpaul register int i, ack = 0; 42136270Swpaul 42236270Swpaul /* 42336270Swpaul * Make sure we're in TX mode. 42436270Swpaul */ 42536270Swpaul DIO_SEL(TL_NETSIO); 42636270Swpaul DIO_BYTE1_SET(TL_SIO_ETXEN); 42736270Swpaul 42836270Swpaul /* 42936270Swpaul * Feed in each bit and stobe the clock. 43036270Swpaul */ 43136270Swpaul for (i = 0x80; i; i >>= 1) { 43236270Swpaul DIO_SEL(TL_NETSIO); 43336270Swpaul if (byte & i) { 43436270Swpaul DIO_BYTE1_SET(TL_SIO_EDATA); 43536270Swpaul } else { 43636270Swpaul DIO_BYTE1_CLR(TL_SIO_EDATA); 43736270Swpaul } 43836270Swpaul DIO_BYTE1_SET(TL_SIO_ECLOK); 43936270Swpaul DIO_BYTE1_CLR(TL_SIO_ECLOK); 44036270Swpaul } 44136270Swpaul 44236270Swpaul /* 44336270Swpaul * Turn off TX mode. 44436270Swpaul */ 44536270Swpaul DIO_BYTE1_CLR(TL_SIO_ETXEN); 44636270Swpaul 44736270Swpaul /* 44836270Swpaul * Check for ack. 44936270Swpaul */ 45036270Swpaul DIO_BYTE1_SET(TL_SIO_ECLOK); 45136270Swpaul ack = DIO_BYTE1_GET(TL_SIO_EDATA); 45236270Swpaul DIO_BYTE1_CLR(TL_SIO_ECLOK); 45336270Swpaul 45436270Swpaul return(ack); 45536270Swpaul} 45636270Swpaul 45736270Swpaul/* 45836270Swpaul * Read a byte of data stored in the EEPROM at address 'addr.' 45936270Swpaul */ 46036270Swpaulstatic u_int8_t tl_eeprom_getbyte(csr, addr, dest) 46136270Swpaul struct tl_csr *csr; 46236270Swpaul u_int8_t addr; 46336270Swpaul u_int8_t *dest; 46436270Swpaul{ 46536270Swpaul register int i; 46636270Swpaul u_int8_t byte = 0; 46736270Swpaul 46836270Swpaul EEPROM_START; 46936270Swpaul /* 47036270Swpaul * Send write control code to EEPROM. 47136270Swpaul */ 47236270Swpaul if (tl_eeprom_putbyte(csr, EEPROM_CTL_WRITE)) 47336270Swpaul return(1); 47436270Swpaul 47536270Swpaul /* 47636270Swpaul * Send address of byte we want to read. 47736270Swpaul */ 47836270Swpaul if (tl_eeprom_putbyte(csr, addr)) 47936270Swpaul return(1); 48036270Swpaul 48136270Swpaul EEPROM_STOP; 48236270Swpaul EEPROM_START; 48336270Swpaul /* 48436270Swpaul * Send read control code to EEPROM. 48536270Swpaul */ 48636270Swpaul if (tl_eeprom_putbyte(csr, EEPROM_CTL_READ)) 48736270Swpaul return(1); 48836270Swpaul 48936270Swpaul /* 49036270Swpaul * Start reading bits from EEPROM. 49136270Swpaul */ 49236270Swpaul DIO_SEL(TL_NETSIO); 49336270Swpaul DIO_BYTE1_CLR(TL_SIO_ETXEN); 49436270Swpaul for (i = 0x80; i; i >>= 1) { 49536270Swpaul DIO_SEL(TL_NETSIO); 49636270Swpaul DIO_BYTE1_SET(TL_SIO_ECLOK); 49736270Swpaul if (DIO_BYTE1_GET(TL_SIO_EDATA)) 49836270Swpaul byte |= i; 49936270Swpaul DIO_BYTE1_CLR(TL_SIO_ECLOK); 50036270Swpaul } 50136270Swpaul 50236270Swpaul EEPROM_STOP; 50336270Swpaul 50436270Swpaul /* 50536270Swpaul * No ACK generated for read, so just return byte. 50636270Swpaul */ 50736270Swpaul 50836270Swpaul *dest = byte; 50936270Swpaul 51036270Swpaul return(0); 51136270Swpaul} 51236270Swpaul 51336270Swpaulstatic void tl_mii_sync(csr) 51436270Swpaul struct tl_csr *csr; 51536270Swpaul{ 51636270Swpaul register int i; 51736270Swpaul 51836270Swpaul DIO_SEL(TL_NETSIO); 51936270Swpaul DIO_BYTE1_CLR(TL_SIO_MTXEN); 52036270Swpaul 52136270Swpaul for (i = 0; i < 32; i++) { 52236270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 52336270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 52436270Swpaul } 52536270Swpaul 52636270Swpaul return; 52736270Swpaul} 52836270Swpaul 52936270Swpaulstatic void tl_mii_send(csr, bits, cnt) 53036270Swpaul struct tl_csr *csr; 53136270Swpaul u_int32_t bits; 53236270Swpaul int cnt; 53336270Swpaul{ 53436270Swpaul int i; 53536270Swpaul 53636270Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 53736270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 53836270Swpaul if (bits & i) { 53936270Swpaul DIO_BYTE1_SET(TL_SIO_MDATA); 54036270Swpaul } else { 54136270Swpaul DIO_BYTE1_CLR(TL_SIO_MDATA); 54236270Swpaul } 54336270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 54436270Swpaul } 54536270Swpaul} 54636270Swpaul 54736270Swpaulstatic int tl_mii_readreg(csr, frame) 54836270Swpaul struct tl_csr *csr; 54936270Swpaul struct tl_mii_frame *frame; 55036270Swpaul 55136270Swpaul{ 55236270Swpaul int i, ack, s; 55336270Swpaul int minten = 0; 55436270Swpaul 55536270Swpaul s = splimp(); 55636270Swpaul 55736270Swpaul tl_mii_sync(csr); 55836270Swpaul 55936270Swpaul /* 56036270Swpaul * Set up frame for RX. 56136270Swpaul */ 56236270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 56336270Swpaul frame->mii_opcode = TL_MII_READOP; 56436270Swpaul frame->mii_turnaround = 0; 56536270Swpaul frame->mii_data = 0; 56636270Swpaul 56736270Swpaul /* 56836270Swpaul * Select the NETSIO register. We will be using it 56936270Swpaul * to communicate indirectly with the MII. 57036270Swpaul */ 57136270Swpaul 57236270Swpaul DIO_SEL(TL_NETSIO); 57336270Swpaul 57436270Swpaul /* 57536270Swpaul * Turn off MII interrupt by forcing MINTEN low. 57636270Swpaul */ 57736270Swpaul minten = DIO_BYTE1_GET(TL_SIO_MINTEN); 57836270Swpaul if (minten) { 57936270Swpaul DIO_BYTE1_CLR(TL_SIO_MINTEN); 58036270Swpaul } 58136270Swpaul 58236270Swpaul /* 58336270Swpaul * Turn on data xmit. 58436270Swpaul */ 58536270Swpaul DIO_BYTE1_SET(TL_SIO_MTXEN); 58636270Swpaul 58736270Swpaul /* 58836270Swpaul * Send command/address info. 58936270Swpaul */ 59036270Swpaul tl_mii_send(csr, frame->mii_stdelim, 2); 59136270Swpaul tl_mii_send(csr, frame->mii_opcode, 2); 59236270Swpaul tl_mii_send(csr, frame->mii_phyaddr, 5); 59336270Swpaul tl_mii_send(csr, frame->mii_regaddr, 5); 59436270Swpaul 59536270Swpaul /* 59636270Swpaul * Turn off xmit. 59736270Swpaul */ 59836270Swpaul DIO_BYTE1_CLR(TL_SIO_MTXEN); 59936270Swpaul 60036270Swpaul /* Idle bit */ 60136270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 60236270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 60336270Swpaul 60436270Swpaul /* Check for ack */ 60536270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 60636270Swpaul ack = DIO_BYTE1_GET(TL_SIO_MDATA); 60736270Swpaul 60836270Swpaul /* Complete the cycle */ 60936270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 61036270Swpaul 61136270Swpaul /* 61236270Swpaul * Now try reading data bits. If the ack failed, we still 61336270Swpaul * need to clock through 16 cycles to keep the PHYs in sync. 61436270Swpaul */ 61536270Swpaul if (ack) { 61636270Swpaul for(i = 0; i < 16; i++) { 61736270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 61836270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 61936270Swpaul } 62036270Swpaul goto fail; 62136270Swpaul } 62236270Swpaul 62336270Swpaul for (i = 0x8000; i; i >>= 1) { 62436270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 62536270Swpaul if (!ack) { 62636270Swpaul if (DIO_BYTE1_GET(TL_SIO_MDATA)) 62736270Swpaul frame->mii_data |= i; 62836270Swpaul } 62936270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 63036270Swpaul } 63136270Swpaul 63236270Swpaulfail: 63336270Swpaul 63436270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 63536270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 63636270Swpaul 63736270Swpaul /* Reenable interrupts */ 63836270Swpaul if (minten) { 63936270Swpaul DIO_BYTE1_SET(TL_SIO_MINTEN); 64036270Swpaul } 64136270Swpaul 64236270Swpaul splx(s); 64336270Swpaul 64436270Swpaul if (ack) 64536270Swpaul return(1); 64636270Swpaul return(0); 64736270Swpaul} 64836270Swpaul 64936270Swpaulstatic int tl_mii_writereg(csr, frame) 65036270Swpaul struct tl_csr *csr; 65136270Swpaul struct tl_mii_frame *frame; 65236270Swpaul 65336270Swpaul{ 65436270Swpaul int s; 65536270Swpaul int minten; 65636270Swpaul 65736270Swpaul tl_mii_sync(csr); 65836270Swpaul 65936270Swpaul s = splimp(); 66036270Swpaul /* 66136270Swpaul * Set up frame for TX. 66236270Swpaul */ 66336270Swpaul 66436270Swpaul frame->mii_stdelim = TL_MII_STARTDELIM; 66536270Swpaul frame->mii_opcode = TL_MII_WRITEOP; 66636270Swpaul frame->mii_turnaround = TL_MII_TURNAROUND; 66736270Swpaul 66836270Swpaul /* 66936270Swpaul * Select the NETSIO register. We will be using it 67036270Swpaul * to communicate indirectly with the MII. 67136270Swpaul */ 67236270Swpaul 67336270Swpaul DIO_SEL(TL_NETSIO); 67436270Swpaul 67536270Swpaul /* 67636270Swpaul * Turn off MII interrupt by forcing MINTEN low. 67736270Swpaul */ 67836270Swpaul minten = DIO_BYTE1_GET(TL_SIO_MINTEN); 67936270Swpaul if (minten) { 68036270Swpaul DIO_BYTE1_CLR(TL_SIO_MINTEN); 68136270Swpaul } 68236270Swpaul 68336270Swpaul /* 68436270Swpaul * Turn on data output. 68536270Swpaul */ 68636270Swpaul DIO_BYTE1_SET(TL_SIO_MTXEN); 68736270Swpaul 68836270Swpaul tl_mii_send(csr, frame->mii_stdelim, 2); 68936270Swpaul tl_mii_send(csr, frame->mii_opcode, 2); 69036270Swpaul tl_mii_send(csr, frame->mii_phyaddr, 5); 69136270Swpaul tl_mii_send(csr, frame->mii_regaddr, 5); 69236270Swpaul tl_mii_send(csr, frame->mii_turnaround, 2); 69336270Swpaul tl_mii_send(csr, frame->mii_data, 16); 69436270Swpaul 69536270Swpaul DIO_BYTE1_SET(TL_SIO_MCLK); 69636270Swpaul DIO_BYTE1_CLR(TL_SIO_MCLK); 69736270Swpaul 69836270Swpaul /* 69936270Swpaul * Turn off xmit. 70036270Swpaul */ 70136270Swpaul DIO_BYTE1_CLR(TL_SIO_MTXEN); 70236270Swpaul 70336270Swpaul /* Reenable interrupts */ 70436270Swpaul if (minten) 70536270Swpaul DIO_BYTE1_SET(TL_SIO_MINTEN); 70636270Swpaul 70736270Swpaul splx(s); 70836270Swpaul 70936270Swpaul return(0); 71036270Swpaul} 71136270Swpaul 71236270Swpaulstatic u_int16_t tl_phy_readreg(sc, reg) 71336270Swpaul struct tl_softc *sc; 71436270Swpaul int reg; 71536270Swpaul{ 71636270Swpaul struct tl_mii_frame frame; 71736270Swpaul struct tl_csr *csr; 71836270Swpaul 71936270Swpaul bzero((char *)&frame, sizeof(frame)); 72036270Swpaul 72136270Swpaul csr = sc->csr; 72236270Swpaul 72336270Swpaul frame.mii_phyaddr = sc->tl_phy_addr; 72436270Swpaul frame.mii_regaddr = reg; 72536270Swpaul tl_mii_readreg(sc->csr, &frame); 72636270Swpaul 72736270Swpaul /* Reenable MII interrupts, just in case. */ 72836270Swpaul DIO_SEL(TL_NETSIO); 72936270Swpaul DIO_BYTE1_SET(TL_SIO_MINTEN); 73036270Swpaul 73136270Swpaul return(frame.mii_data); 73236270Swpaul} 73336270Swpaul 73436270Swpaulstatic void tl_phy_writereg(sc, reg, data) 73536270Swpaul struct tl_softc *sc; 73636270Swpaul u_int16_t reg; 73736270Swpaul u_int16_t data; 73836270Swpaul{ 73936270Swpaul struct tl_mii_frame frame; 74036270Swpaul struct tl_csr *csr; 74136270Swpaul 74236270Swpaul bzero((char *)&frame, sizeof(frame)); 74336270Swpaul 74436270Swpaul csr = sc->csr; 74536270Swpaul frame.mii_phyaddr = sc->tl_phy_addr; 74636270Swpaul frame.mii_regaddr = reg; 74736270Swpaul frame.mii_data = data; 74836270Swpaul 74936270Swpaul tl_mii_writereg(sc->csr, &frame); 75036270Swpaul 75136270Swpaul /* Reenable MII interrupts, just in case. */ 75236270Swpaul DIO_SEL(TL_NETSIO); 75336270Swpaul DIO_BYTE1_SET(TL_SIO_MINTEN); 75436270Swpaul 75536270Swpaul return; 75636270Swpaul} 75736270Swpaul 75836270Swpaul/* 75936270Swpaul * Read a sequence of bytes from the EEPROM. 76036270Swpaul */ 76136270Swpaulstatic int tl_read_eeprom(csr, dest, off, cnt) 76236270Swpaul struct tl_csr *csr; 76336270Swpaul caddr_t dest; 76436270Swpaul int off; 76536270Swpaul int cnt; 76636270Swpaul{ 76736270Swpaul int err = 0, i; 76836270Swpaul u_int8_t byte = 0; 76936270Swpaul 77036270Swpaul for (i = 0; i < cnt; i++) { 77136270Swpaul err = tl_eeprom_getbyte(csr, off + i, &byte); 77236270Swpaul if (err) 77336270Swpaul break; 77436270Swpaul *(dest + i) = byte; 77536270Swpaul } 77636270Swpaul 77736270Swpaul return(err ? 1 : 0); 77836270Swpaul} 77936270Swpaul 78036270Swpaul/* 78136270Swpaul * Initiate autonegotiation with a link partner. 78236270Swpaul * 78336270Swpaul * Note that the Texas Instruments ThunderLAN programmer's guide 78436270Swpaul * fails to mention one very important point about autonegotiation. 78536270Swpaul * Autonegotiation is done largely by the PHY, independent of the 78636270Swpaul * ThunderLAN chip itself: the PHY sets the flags in the BMCR 78736270Swpaul * register to indicate what modes were selected and if link status 78836270Swpaul * is good. In fact, the PHY does pretty much all of the work itself, 78936270Swpaul * except for one small detail. 79036270Swpaul * 79136270Swpaul * The PHY may negotiate a full-duplex of half-duplex link, and set 79236270Swpaul * the PHY_BMCR_DUPLEX bit accordingly, but the ThunderLAN's 'NetCommand' 79336270Swpaul * register _also_ has a half-duplex/full-duplex bit, and you MUST ALSO 79436270Swpaul * SET THIS BIT MANUALLY TO CORRESPOND TO THE MODE SELECTED FOR THE PHY! 79536270Swpaul * In other words, both the ThunderLAN chip and the PHY have to be 79636270Swpaul * programmed for full-duplex mode in order for full-duplex to actually 79736270Swpaul * work. So in order for autonegotiation to really work right, we have 79836270Swpaul * to wait for the link to come up, check the BMCR register, then set 79936270Swpaul * the ThunderLAN for full or half-duplex as needed. 80036270Swpaul * 80136270Swpaul * I struggled for two days to figure this out, so I'm making a point 80236270Swpaul * of drawing attention to this fact. I think it's very strange that 80336270Swpaul * the ThunderLAN doesn't automagically track the duplex state of the 80436270Swpaul * PHY, but there you have it. 80536270Swpaul * 80636270Swpaul * Also when, using a National Semiconductor DP83840A PHY, we have to 80736270Swpaul * allow a full three seconds for autonegotiation to complete. So what 80836270Swpaul * we do is flip the autonegotiation restart bit, then set a timeout 80936270Swpaul * to wake us up in three seconds to check the link state. 81036270Swpaul */ 81136270Swpaulstatic void tl_autoneg(sc, flag, verbose) 81236270Swpaul struct tl_softc *sc; 81336270Swpaul int flag; 81436270Swpaul int verbose; 81536270Swpaul{ 81636270Swpaul u_int16_t phy_sts = 0, media = 0; 81736270Swpaul struct ifnet *ifp; 81836270Swpaul struct ifmedia *ifm; 81936270Swpaul struct tl_csr *csr; 82036270Swpaul 82136270Swpaul ifm = &sc->ifmedia; 82236270Swpaul ifp = &sc->arpcom.ac_if; 82336270Swpaul csr = sc->csr; 82436270Swpaul 82536270Swpaul /* 82636270Swpaul * First, see if autoneg is supported. If not, there's 82736270Swpaul * no point in continuing. 82836270Swpaul */ 82936270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 83036270Swpaul if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { 83136270Swpaul if (verbose) 83236270Swpaul printf("tl%d: autonegotiation not supported\n", 83336270Swpaul sc->tl_unit); 83436270Swpaul return; 83536270Swpaul } 83636270Swpaul 83736270Swpaul switch (flag) { 83836270Swpaul case TL_FLAG_FORCEDELAY: 83936270Swpaul /* 84036270Swpaul * XXX Never use this option anywhere but in the probe 84136270Swpaul * routine: making the kernel stop dead in its tracks 84236270Swpaul * for three whole seconds after we've gone multi-user 84336270Swpaul * is really bad manners. 84436270Swpaul */ 84536270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMCR); 84636270Swpaul phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; 84736270Swpaul tl_phy_writereg(sc, PHY_BMCR, phy_sts); 84836270Swpaul DELAY(3000000); 84936270Swpaul break; 85036270Swpaul case TL_FLAG_SCHEDDELAY: 85136270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMCR); 85236270Swpaul phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; 85336270Swpaul tl_phy_writereg(sc, PHY_BMCR, phy_sts); 85436270Swpaul ifp->if_timer = 3; 85536270Swpaul sc->tl_autoneg = 1; 85636270Swpaul return; 85736270Swpaul case TL_FLAG_DELAYTIMEO: 85836270Swpaul ifp->if_timer = 0; 85936270Swpaul sc->tl_autoneg = 0; 86036270Swpaul break; 86136270Swpaul default: 86236270Swpaul printf("tl%d: invalid autoneg flag: %d\n", flag, sc->tl_unit); 86336270Swpaul return; 86436270Swpaul } 86536270Swpaul 86636270Swpaul /* 86736270Swpaul * Read the BMSR register twice: the LINKSTAT bit is a 86836270Swpaul * latching bit. 86936270Swpaul */ 87036270Swpaul tl_phy_readreg(sc, PHY_BMSR); 87136270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 87236270Swpaul if (phy_sts & PHY_BMSR_AUTONEGCOMP) { 87336270Swpaul if (verbose) 87436270Swpaul printf("tl%d: autoneg complete, ", sc->tl_unit); 87536270Swpaul phy_sts = tl_phy_readreg(sc, PHY_BMSR); 87636270Swpaul } else { 87736270Swpaul if (verbose) 87836270Swpaul printf("tl%d: autoneg not complete, ", sc->tl_unit); 87936270Swpaul } 88036270Swpaul 88136270Swpaul /* Link is good. Report modes and set duplex mode. */ 88236270Swpaul if (phy_sts & PHY_BMSR_LINKSTAT) { 88336270Swpaul if (verbose) 88436270Swpaul printf("link status good "); 88536270Swpaul media = tl_phy_readreg(sc, PHY_BMCR); 88636270Swpaul 88736270Swpaul /* Set the DUPLEX bit in the NetCmd register accordingly. */ 88836270Swpaul if (media & PHY_BMCR_DUPLEX) { 88936270Swpaul if (verbose) 89036270Swpaul printf("(full-duplex, "); 89136270Swpaul ifm->ifm_media |= IFM_FDX; 89236270Swpaul ifm->ifm_media &= ~IFM_HDX; 89336270Swpaul DIO_SEL(TL_NETCMD); 89436270Swpaul DIO_BYTE0_SET(TL_CMD_DUPLEX); 89536270Swpaul } else { 89636270Swpaul if (verbose) 89736270Swpaul printf("(half-duplex, "); 89836270Swpaul ifm->ifm_media &= ~IFM_FDX; 89936270Swpaul ifm->ifm_media |= IFM_HDX; 90036270Swpaul DIO_SEL(TL_NETCMD); 90136270Swpaul DIO_BYTE0_CLR(TL_CMD_DUPLEX); 90236270Swpaul } 90336270Swpaul 90436270Swpaul if (media & PHY_BMCR_SPEEDSEL) { 90536270Swpaul if (verbose) 90636270Swpaul printf("100Mb/s)\n"); 90736270Swpaul ifm->ifm_media |= IFM_100_TX; 90836270Swpaul ifm->ifm_media &= ~IFM_10_T; 90936270Swpaul } else { 91036270Swpaul if (verbose) 91136270Swpaul printf("10Mb/s)\n"); 91236270Swpaul ifm->ifm_media &= ~IFM_100_TX; 91336270Swpaul ifm->ifm_media |= IFM_10_T; 91436270Swpaul } 91536270Swpaul 91636270Swpaul /* Turn off autoneg */ 91736270Swpaul media &= ~PHY_BMCR_AUTONEGENBL; 91836270Swpaul tl_phy_writereg(sc, PHY_BMCR, media); 91936270Swpaul } else { 92036270Swpaul if (verbose) 92136270Swpaul printf("no carrier\n"); 92236270Swpaul } 92336270Swpaul 92436270Swpaul return; 92536270Swpaul} 92636270Swpaul 92736270Swpaul/* 92836270Swpaul * Set speed and duplex mode. Also program autoneg advertisements 92936270Swpaul * accordingly. 93036270Swpaul */ 93136270Swpaulstatic void tl_setmode(sc, media) 93236270Swpaul struct tl_softc *sc; 93336270Swpaul int media; 93436270Swpaul{ 93536270Swpaul u_int16_t bmcr, anar, ctl; 93636270Swpaul struct tl_csr *csr; 93736270Swpaul 93836270Swpaul csr = sc->csr; 93936270Swpaul bmcr = tl_phy_readreg(sc, PHY_BMCR); 94036270Swpaul anar = tl_phy_readreg(sc, PHY_ANAR); 94136270Swpaul ctl = tl_phy_readreg(sc, TL_PHY_CTL); 94236270Swpaul DIO_SEL(TL_NETCMD); 94336270Swpaul 94436270Swpaul bmcr &= ~(PHY_BMCR_SPEEDSEL|PHY_BMCR_DUPLEX|PHY_BMCR_AUTONEGENBL| 94536270Swpaul PHY_BMCR_LOOPBK); 94636270Swpaul anar &= ~(PHY_ANAR_100BT4|PHY_ANAR_100BTXFULL|PHY_ANAR_100BTXHALF| 94736270Swpaul PHY_ANAR_10BTFULL|PHY_ANAR_10BTHALF); 94836270Swpaul 94936270Swpaul ctl &= ~PHY_CTL_AUISEL; 95036270Swpaul 95136270Swpaul if (IFM_SUBTYPE(media) == IFM_LOOP) 95236270Swpaul bmcr |= PHY_BMCR_LOOPBK; 95336270Swpaul 95436270Swpaul if (IFM_SUBTYPE(media) == IFM_AUTO) 95536270Swpaul bmcr |= PHY_BMCR_AUTONEGENBL; 95636270Swpaul 95736270Swpaul if (IFM_SUBTYPE(media) == IFM_10_5) 95836270Swpaul ctl |= PHY_CTL_AUISEL; 95936270Swpaul 96036270Swpaul if (IFM_SUBTYPE(media) == IFM_100_TX) { 96136270Swpaul bmcr |= PHY_BMCR_SPEEDSEL; 96236270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 96336270Swpaul bmcr |= PHY_BMCR_DUPLEX; 96436270Swpaul anar |= PHY_ANAR_100BTXFULL; 96536270Swpaul DIO_BYTE0_SET(TL_CMD_DUPLEX); 96636270Swpaul } else if ((media & IFM_GMASK) == IFM_HDX) { 96736270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 96836270Swpaul anar |= PHY_ANAR_100BTXHALF; 96936270Swpaul DIO_BYTE0_CLR(TL_CMD_DUPLEX); 97036270Swpaul } else { 97136270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 97236270Swpaul anar |= PHY_ANAR_100BTXHALF; 97336270Swpaul DIO_BYTE0_CLR(TL_CMD_DUPLEX); 97436270Swpaul } 97536270Swpaul } 97636270Swpaul 97736270Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 97836270Swpaul bmcr &= ~PHY_BMCR_SPEEDSEL; 97936270Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 98036270Swpaul bmcr |= PHY_BMCR_DUPLEX; 98136270Swpaul anar |= PHY_ANAR_10BTFULL; 98236270Swpaul DIO_BYTE0_SET(TL_CMD_DUPLEX); 98336270Swpaul } else if ((media & IFM_GMASK) == IFM_HDX) { 98436270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 98536270Swpaul anar |= PHY_ANAR_10BTHALF; 98636270Swpaul DIO_BYTE0_CLR(TL_CMD_DUPLEX); 98736270Swpaul } else { 98836270Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 98936270Swpaul anar |= PHY_ANAR_10BTHALF; 99036270Swpaul DIO_BYTE0_CLR(TL_CMD_DUPLEX); 99136270Swpaul } 99236270Swpaul } 99336270Swpaul 99436270Swpaul tl_phy_writereg(sc, PHY_BMCR, bmcr); 99536270Swpaul tl_phy_writereg(sc, PHY_ANAR, anar); 99636270Swpaul tl_phy_writereg(sc, TL_PHY_CTL, ctl); 99736270Swpaul 99836270Swpaul return; 99936270Swpaul} 100036270Swpaul 100136270Swpaul#define XOR(a, b) ((a && !b) || (!a && b)) 100236270Swpaul#define DA(addr, offset) (addr[offset / 8] & (1 << (offset % 8))) 100336270Swpaul 100436270Swpaulstatic int tl_calchash(addr) 100536270Swpaul char *addr; 100636270Swpaul{ 100736270Swpaul int h; 100836270Swpaul 100936270Swpaul h = XOR(DA(addr, 0), XOR(DA(addr, 6), XOR(DA(addr, 12), 101036270Swpaul XOR(DA(addr, 18), XOR(DA(addr, 24), XOR(DA(addr, 30), 101136270Swpaul XOR(DA(addr, 36), DA(addr, 42)))))))); 101236270Swpaul 101336270Swpaul h |= XOR(DA(addr, 1), XOR(DA(addr, 7), XOR(DA(addr, 13), 101436270Swpaul XOR(DA(addr, 19), XOR(DA(addr, 25), XOR(DA(addr, 31), 101536270Swpaul XOR(DA(addr, 37), DA(addr, 43)))))))) << 1; 101636270Swpaul 101736270Swpaul h |= XOR(DA(addr, 2), XOR(DA(addr, 8), XOR(DA(addr, 14), 101836270Swpaul XOR(DA(addr, 20), XOR(DA(addr, 26), XOR(DA(addr, 32), 101936270Swpaul XOR(DA(addr, 38), DA(addr, 44)))))))) << 2; 102036270Swpaul 102136270Swpaul h |= XOR(DA(addr, 3), XOR(DA(addr, 9), XOR(DA(addr, 15), 102236270Swpaul XOR(DA(addr, 21), XOR(DA(addr, 27), XOR(DA(addr, 33), 102336270Swpaul XOR(DA(addr, 39), DA(addr, 45)))))))) << 3; 102436270Swpaul 102536270Swpaul h |= XOR(DA(addr, 4), XOR(DA(addr, 10), XOR(DA(addr, 16), 102636270Swpaul XOR(DA(addr, 22), XOR(DA(addr, 28), XOR(DA(addr, 34), 102736270Swpaul XOR(DA(addr, 40), DA(addr, 46)))))))) << 4; 102836270Swpaul 102936270Swpaul h |= XOR(DA(addr, 5), XOR(DA(addr, 11), XOR(DA(addr, 17), 103036270Swpaul XOR(DA(addr, 23), XOR(DA(addr, 29), XOR(DA(addr, 35), 103136270Swpaul XOR(DA(addr, 41), DA(addr, 47)))))))) << 5; 103236270Swpaul 103336270Swpaul return(h); 103436270Swpaul} 103536270Swpaul 103636270Swpaulstatic void tl_setmulti(sc) 103736270Swpaul struct tl_softc *sc; 103836270Swpaul{ 103936270Swpaul struct ifnet *ifp; 104036270Swpaul struct tl_csr *csr; 104136270Swpaul u_int32_t hashes[2] = { 0, 0 }; 104236270Swpaul int h; 104336270Swpaul struct ifmultiaddr *ifma; 104436270Swpaul 104536270Swpaul csr = sc->csr; 104636270Swpaul ifp = &sc->arpcom.ac_if; 104736270Swpaul 104836270Swpaul if (sc->arpcom.ac_multicnt > 64 || ifp->if_flags & IFF_ALLMULTI) { 104936270Swpaul hashes[0] = 0xFFFFFFFF; 105036270Swpaul hashes[1] = 0xFFFFFFFF; 105136270Swpaul } else { 105236270Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 105336270Swpaul ifma = ifma->ifma_link.le_next) { 105436270Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 105536270Swpaul continue; 105636270Swpaul h = tl_calchash( 105736270Swpaul LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 105836270Swpaul if (h < 32) 105936270Swpaul hashes[0] |= (1 << h); 106036270Swpaul else 106136317Swpaul hashes[1] |= (1 << (h - 32)); 106236270Swpaul } 106336270Swpaul } 106436270Swpaul 106536270Swpaul DIO_SEL(TL_HASH1); 106636270Swpaul DIO_LONG_PUT(hashes[0]); 106736270Swpaul DIO_SEL(TL_HASH2); 106836270Swpaul DIO_LONG_PUT(hashes[1]); 106936270Swpaul 107036270Swpaul return; 107136270Swpaul} 107236270Swpaul 107336270Swpaulstatic void tl_softreset(csr, internal) 107436270Swpaul struct tl_csr *csr; 107536270Swpaul int internal; 107636270Swpaul{ 107736270Swpaul u_int32_t cmd, dummy; 107836270Swpaul 107936270Swpaul /* Assert the adapter reset bit. */ 108036270Swpaul csr->tl_host_cmd |= TL_CMD_ADRST; 108136270Swpaul /* Turn off interrupts */ 108236270Swpaul csr->tl_host_cmd |= TL_CMD_INTSOFF; 108336270Swpaul 108436270Swpaul /* First, clear the stats registers. */ 108536270Swpaul DIO_SEL(TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 108636270Swpaul DIO_LONG_GET(dummy); 108736270Swpaul DIO_LONG_GET(dummy); 108836270Swpaul DIO_LONG_GET(dummy); 108936270Swpaul DIO_LONG_GET(dummy); 109036270Swpaul DIO_LONG_GET(dummy); 109136270Swpaul 109236270Swpaul /* Clear Areg and Hash registers */ 109336270Swpaul DIO_SEL(TL_AREG0_B5|TL_DIO_ADDR_INC); 109436270Swpaul DIO_LONG_PUT(0x00000000); 109536270Swpaul DIO_LONG_PUT(0x00000000); 109636270Swpaul DIO_LONG_PUT(0x00000000); 109736270Swpaul DIO_LONG_PUT(0x00000000); 109836270Swpaul DIO_LONG_PUT(0x00000000); 109936270Swpaul DIO_LONG_PUT(0x00000000); 110036270Swpaul DIO_LONG_PUT(0x00000000); 110136270Swpaul DIO_LONG_PUT(0x00000000); 110236270Swpaul 110336270Swpaul /* 110436270Swpaul * Set up Netconfig register. Enable one channel and 110536270Swpaul * one fragment mode. 110636270Swpaul */ 110736270Swpaul DIO_SEL(TL_NETCONFIG); 110836270Swpaul DIO_WORD0_SET(TL_CFG_ONECHAN|TL_CFG_ONEFRAG); 110936270Swpaul if (internal) { 111036270Swpaul DIO_SEL(TL_NETCONFIG); 111136270Swpaul DIO_WORD0_SET(TL_CFG_PHYEN); 111236270Swpaul } else { 111336270Swpaul DIO_SEL(TL_NETCONFIG); 111436270Swpaul DIO_WORD0_CLR(TL_CFG_PHYEN); 111536270Swpaul } 111636270Swpaul 111736270Swpaul /* Set PCI burst size */ 111836270Swpaul DIO_SEL(TL_BSIZEREG); 111936270Swpaul DIO_BYTE1_SET(0x33); 112036270Swpaul 112136270Swpaul /* 112236270Swpaul * Load adapter irq pacing timer and tx threshold. 112336270Swpaul * We make the transmit threshold 1 initially but we may 112436270Swpaul * change that later. 112536270Swpaul */ 112636270Swpaul cmd = csr->tl_host_cmd; 112736270Swpaul cmd |= TL_CMD_NES; 112836270Swpaul cmd &= ~(TL_CMD_RT|TL_CMD_EOC|TL_CMD_ACK_MASK|TL_CMD_CHSEL_MASK); 112936270Swpaul csr->tl_host_cmd = cmd | (TL_CMD_LDTHR | TX_THR); 113036270Swpaul csr->tl_host_cmd = cmd | (TL_CMD_LDTMR | 0x00000003); 113136270Swpaul 113236270Swpaul /* Unreset the MII */ 113336270Swpaul DIO_SEL(TL_NETSIO); 113436270Swpaul DIO_BYTE1_SET(TL_SIO_NMRST); 113536270Swpaul 113636270Swpaul /* Clear status register */ 113736270Swpaul DIO_SEL(TL_NETSTS); 113836270Swpaul DIO_BYTE2_SET(TL_STS_MIRQ); 113936270Swpaul DIO_BYTE2_SET(TL_STS_HBEAT); 114036270Swpaul DIO_BYTE2_SET(TL_STS_TXSTOP); 114136270Swpaul DIO_BYTE2_SET(TL_STS_RXSTOP); 114236270Swpaul 114336270Swpaul /* Enable network status interrupts for everything. */ 114436270Swpaul DIO_SEL(TL_NETMASK); 114536270Swpaul DIO_BYTE3_SET(TL_MASK_MASK7|TL_MASK_MASK6| 114636270Swpaul TL_MASK_MASK5|TL_MASK_MASK4); 114736270Swpaul 114836270Swpaul /* Take the adapter out of reset */ 114936270Swpaul DIO_SEL(TL_NETCMD); 115036270Swpaul DIO_BYTE0_SET(TL_CMD_NRESET|TL_CMD_NWRAP); 115136270Swpaul 115236270Swpaul /* Wait for things to settle down a little. */ 115336270Swpaul DELAY(500); 115436270Swpaul 115536270Swpaul return; 115636270Swpaul} 115736270Swpaul 115836270Swpaul/* 115936270Swpaul * Probe for a ThunderLAN chip. Check the PCI vendor and device IDs 116036270Swpaul * against our list and return its name if we find a match. Note that 116136270Swpaul * we also save a pointer to the tl_type struct for this card since we 116236270Swpaul * will need it for the softc struct and attach routine later. 116336270Swpaul */ 116436270Swpaulstatic char * 116536270Swpaultl_probe(config_id, device_id) 116636270Swpaul pcici_t config_id; 116736270Swpaul pcidi_t device_id; 116836270Swpaul{ 116936270Swpaul struct tl_type *t; 117036270Swpaul struct tl_iflist *new; 117136270Swpaul 117236270Swpaul t = tl_devs; 117336270Swpaul 117436270Swpaul while(t->tl_name != NULL) { 117536270Swpaul if ((device_id & 0xFFFF) == t->tl_vid && 117636270Swpaul ((device_id >> 16) & 0xFFFF) == t->tl_did) { 117736270Swpaul new = malloc(sizeof(struct tl_iflist), 117836270Swpaul M_DEVBUF, M_NOWAIT); 117936270Swpaul if (new == NULL) { 118036270Swpaul printf("no memory for controller struct!\n"); 118136270Swpaul break; 118236270Swpaul } 118336270Swpaul bzero(new, sizeof(struct tl_iflist)); 118436270Swpaul new->tl_config_id = config_id; 118536270Swpaul new->tl_dinfo = t; 118636270Swpaul new->tl_next = tl_iflist; 118736270Swpaul tl_iflist = new; 118836270Swpaul return(t->tl_name); 118936270Swpaul } 119036270Swpaul t++; 119136270Swpaul } 119236270Swpaul 119336270Swpaul return(NULL); 119436270Swpaul} 119536270Swpaul 119636270Swpaul/* 119736270Swpaul * The ThunderLAN controller can support multiple PHYs. Logically, 119836270Swpaul * this means we have to be able to deal with each PHY as a separate 119936270Swpaul * interface. We therefore consider ThunderLAN devices as follows: 120036270Swpaul * 120136270Swpaul * o Each ThunderLAN controller device is assigned the name tlcX where 120236270Swpaul * X is the controller's unit number. Each ThunderLAN device found 120336270Swpaul * is assigned a different number. 120436270Swpaul * 120536270Swpaul * o Each PHY on each controller is assigned the name tlX. X starts at 120636270Swpaul * 0 and is incremented each time an additional PHY is found. 120736270Swpaul * 120836270Swpaul * So, if you had two dual-channel ThunderLAN cards, you'd have 120936270Swpaul * tlc0 and tlc1 (the controllers) and tl0, tl1, tl2, tl3 (the logical 121036270Swpaul * interfaces). I think. I'm still not sure how dual chanel controllers 121136270Swpaul * work as I've yet to see one. 121236270Swpaul */ 121336270Swpaul 121436270Swpaul/* 121536270Swpaul * Do the interface setup and attach for a PHY on a particular 121636270Swpaul * ThunderLAN chip. Also also set up interrupt vectors. 121736270Swpaul */ 121836270Swpaulstatic int tl_attach_phy(csr, tl_unit, eaddr, tl_phy, ilist) 121936270Swpaul struct tl_csr *csr; 122036270Swpaul int tl_unit; 122136270Swpaul char *eaddr; 122236270Swpaul int tl_phy; 122336270Swpaul struct tl_iflist *ilist; 122436270Swpaul{ 122536270Swpaul struct tl_softc *sc; 122636270Swpaul struct ifnet *ifp; 122736270Swpaul int phy_ctl; 122836270Swpaul struct tl_type *p = tl_phys; 122936270Swpaul struct tl_mii_frame frame; 123036270Swpaul int i, media = IFM_ETHER|IFM_100_TX|IFM_FDX; 123136270Swpaul unsigned int round; 123236270Swpaul caddr_t roundptr; 123336270Swpaul 123436270Swpaul if (tl_phy != TL_PHYADDR_MAX) 123536270Swpaul tl_softreset(csr, 0); 123636270Swpaul 123736270Swpaul /* Reset the PHY again, just in case. */ 123836270Swpaul bzero((char *)&frame, sizeof(frame)); 123936270Swpaul frame.mii_phyaddr = tl_phy; 124036270Swpaul frame.mii_regaddr = TL_PHY_GENCTL; 124136270Swpaul frame.mii_data = PHY_BMCR_RESET; 124236270Swpaul tl_mii_writereg(csr, &frame); 124336270Swpaul DELAY(500); 124436270Swpaul frame.mii_data = 0; 124536270Swpaul 124636270Swpaul /* First, allocate memory for the softc struct. */ 124736270Swpaul sc = malloc(sizeof(struct tl_softc), M_DEVBUF, M_NOWAIT); 124836270Swpaul if (sc == NULL) { 124936270Swpaul printf("tlc%d: no memory for softc struct!\n", ilist->tlc_unit); 125036270Swpaul return(1); 125136270Swpaul } 125236270Swpaul 125336270Swpaul bzero(sc, sizeof(struct tl_softc)); 125436270Swpaul 125536270Swpaul /* 125636270Swpaul * Now allocate memory for the TX and RX lists. Note that 125736270Swpaul * we actually allocate 8 bytes more than we really need: 125836270Swpaul * this is because we need to adjust the final address to 125936270Swpaul * be aligned on a quadword (64-bit) boundary in order to 126036270Swpaul * make the chip happy. If the list structures aren't properly 126136270Swpaul * aligned, DMA fails and the chip generates an adapter check 126236270Swpaul * interrupt and has to be reset. If you set up the softc struct 126336270Swpaul * just right you can sort of obtain proper alignment 'by chance.' 126436270Swpaul * But I don't want to depend on this, so instead the alignment 126536270Swpaul * is forced here. 126636270Swpaul */ 126736270Swpaul sc->tl_ldata_ptr = malloc(sizeof(struct tl_list_data) + 8, 126836270Swpaul M_DEVBUF, M_NOWAIT); 126936270Swpaul 127036270Swpaul if (sc->tl_ldata_ptr == NULL) { 127136270Swpaul free(sc, M_DEVBUF); 127236270Swpaul printf("tlc%d: no memory for list buffers!\n", ilist->tlc_unit); 127336270Swpaul return(1); 127436270Swpaul } 127536270Swpaul 127636270Swpaul /* 127736270Swpaul * Convoluted but satisfies my ANSI sensibilities. GCC lets 127836270Swpaul * you do casts on the LHS of an assignment, but ANSI doesn't 127936270Swpaul * allow that. 128036270Swpaul */ 128136270Swpaul sc->tl_ldata = (struct tl_list_data *)sc->tl_ldata_ptr; 128236270Swpaul round = (unsigned int)sc->tl_ldata_ptr & 0xF; 128336270Swpaul roundptr = sc->tl_ldata_ptr; 128436270Swpaul for (i = 0; i < 8; i++) { 128536270Swpaul if (round % 8) { 128636270Swpaul round++; 128736270Swpaul roundptr++; 128836270Swpaul } else 128936270Swpaul break; 129036270Swpaul } 129136270Swpaul sc->tl_ldata = (struct tl_list_data *)roundptr; 129236270Swpaul 129336270Swpaul bzero(sc->tl_ldata, sizeof(struct tl_list_data)); 129436270Swpaul 129536270Swpaul sc->csr = csr; 129636270Swpaul sc->tl_dinfo = ilist->tl_dinfo; 129736270Swpaul sc->tl_ctlr = ilist->tlc_unit; 129836270Swpaul sc->tl_unit = tl_unit; 129936270Swpaul sc->tl_phy_addr = tl_phy; 130036270Swpaul sc->tl_iflist = ilist; 130136270Swpaul callout_handle_init(&sc->tl_stat_ch); 130236270Swpaul 130336270Swpaul frame.mii_regaddr = TL_PHY_VENID; 130436270Swpaul tl_mii_readreg(csr, &frame); 130536270Swpaul sc->tl_phy_vid = frame.mii_data; 130636270Swpaul 130736270Swpaul frame.mii_regaddr = TL_PHY_DEVID; 130836270Swpaul tl_mii_readreg(csr, &frame); 130936270Swpaul sc->tl_phy_did = frame.mii_data; 131036270Swpaul 131136270Swpaul frame.mii_regaddr = TL_PHY_GENSTS; 131236270Swpaul tl_mii_readreg(csr, &frame); 131336270Swpaul sc->tl_phy_sts = frame.mii_data; 131436270Swpaul 131536270Swpaul frame.mii_regaddr = TL_PHY_GENCTL; 131636270Swpaul tl_mii_readreg(csr, &frame); 131736270Swpaul phy_ctl = frame.mii_data; 131836270Swpaul 131936270Swpaul /* 132036270Swpaul * PHY revision numbers tend to vary a bit. Our algorithm here 132136270Swpaul * is to check everything but the 8 least significant bits. 132236270Swpaul */ 132336270Swpaul while(p->tl_vid) { 132436270Swpaul if (sc->tl_phy_vid == p->tl_vid && 132536270Swpaul (sc->tl_phy_did | 0x000F) == p->tl_did) { 132636270Swpaul sc->tl_pinfo = p; 132736270Swpaul break; 132836270Swpaul } 132936270Swpaul p++; 133036270Swpaul } 133136270Swpaul if (sc->tl_pinfo == NULL) { 133236270Swpaul sc->tl_pinfo = &tl_phys[PHY_UNKNOWN]; 133336270Swpaul } 133436270Swpaul 133536270Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 133636270Swpaul ifp = &sc->arpcom.ac_if; 133736270Swpaul ifp->if_softc = sc; 133836270Swpaul ifp->if_unit = tl_unit; 133936270Swpaul ifp->if_name = "tl"; 134036270Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 134136270Swpaul ifp->if_ioctl = tl_ioctl; 134236270Swpaul ifp->if_output = ether_output; 134336270Swpaul ifp->if_start = tl_start; 134436270Swpaul ifp->if_watchdog = tl_watchdog; 134536270Swpaul ifp->if_init = tl_init; 134636270Swpaul 134736270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4 || 134836270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXFULL || 134936270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 135036270Swpaul ifp->if_baudrate = 100000000; 135136270Swpaul else 135236270Swpaul ifp->if_baudrate = 10000000; 135336270Swpaul 135436270Swpaul ilist->tl_sc[tl_phy] = sc; 135536270Swpaul 135636270Swpaul printf("tl%d at tlc%d physical interface %d\n", ifp->if_unit, 135736270Swpaul sc->tl_ctlr, 135836270Swpaul sc->tl_phy_addr); 135936270Swpaul 136036270Swpaul printf("tl%d: %s ", ifp->if_unit, sc->tl_pinfo->tl_name); 136136270Swpaul 136236270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4 || 136336270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXHALF || 136436270Swpaul sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 136536270Swpaul printf("10/100Mbps "); 136636270Swpaul else { 136736270Swpaul media &= ~IFM_100_TX; 136836270Swpaul media |= IFM_10_T; 136936270Swpaul printf("10Mbps "); 137036270Swpaul } 137136270Swpaul 137236270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL || 137336270Swpaul sc->tl_phy_sts & PHY_BMSR_10BTFULL) 137436270Swpaul printf("full duplex "); 137536270Swpaul else { 137636270Swpaul printf("half duplex "); 137736270Swpaul media &= ~IFM_FDX; 137836270Swpaul } 137936270Swpaul 138036270Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) { 138136270Swpaul media = IFM_ETHER|IFM_AUTO; 138236270Swpaul printf("autonegotiating\n"); 138336270Swpaul } else 138436270Swpaul printf("\n"); 138536270Swpaul 138636270Swpaul /* If this isn't a known PHY, print the PHY indentifier info. */ 138736270Swpaul if (sc->tl_pinfo->tl_vid == 0) 138836270Swpaul printf("tl%d: vendor id: %04x product id: %04x\n", 138936270Swpaul sc->tl_unit, sc->tl_phy_vid, sc->tl_phy_did); 139036270Swpaul 139136270Swpaul /* Set up ifmedia data and callbacks. */ 139236270Swpaul ifmedia_init(&sc->ifmedia, 0, tl_ifmedia_upd, tl_ifmedia_sts); 139336270Swpaul 139436270Swpaul /* 139536270Swpaul * All ThunderLANs support at least 10baseT half duplex. 139636270Swpaul * They also support AUI selection if used in 10Mb/s modes. 139736270Swpaul * They all also support a loopback mode. 139836270Swpaul */ 139936270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 140036270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 140136270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 140236270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_LOOP, 0, NULL); 140336270Swpaul 140436270Swpaul /* Some ThunderLAN PHYs support autonegotiation. */ 140536270Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) 140636270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 140736270Swpaul 140836270Swpaul /* Some support 10baseT full duplex. */ 140936270Swpaul if (sc->tl_phy_sts & PHY_BMSR_10BTFULL) 141036270Swpaul ifmedia_add(&sc->ifmedia, 141136270Swpaul IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 141236270Swpaul 141336270Swpaul /* Some support 100BaseTX half duplex. */ 141436270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 141536270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); 141636270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXHALF) 141736270Swpaul ifmedia_add(&sc->ifmedia, 141836270Swpaul IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); 141936270Swpaul 142036270Swpaul /* Some support 100BaseTX full duplex. */ 142136270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BTXFULL) 142236270Swpaul ifmedia_add(&sc->ifmedia, 142336270Swpaul IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); 142436270Swpaul 142536270Swpaul /* Some also support 100BaseT4. */ 142636270Swpaul if (sc->tl_phy_sts & PHY_BMSR_100BT4) 142736270Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); 142836270Swpaul 142936270Swpaul /* Set default media. */ 143036270Swpaul ifmedia_set(&sc->ifmedia, media); 143136270Swpaul 143236270Swpaul /* 143336270Swpaul * Kick off an autonegotiation session if this PHY supports it. 143436270Swpaul * This is necessary to make sure the chip's duplex mode matches 143536270Swpaul * the PHY's duplex mode. It may not: once enabled, the PHY may 143636270Swpaul * autonegotiate full-duplex mode with its link partner, but the 143736270Swpaul * ThunderLAN chip defaults to half-duplex and stays there unless 143836270Swpaul * told otherwise. 143936270Swpaul */ 144036270Swpaul if (sc->tl_phy_sts & PHY_BMSR_CANAUTONEG) 144136270Swpaul tl_autoneg(sc, TL_FLAG_FORCEDELAY, 0); 144236270Swpaul 144336270Swpaul /* 144436270Swpaul * Call MI attach routines. 144536270Swpaul */ 144636270Swpaul if_attach(ifp); 144736270Swpaul ether_ifattach(ifp); 144836270Swpaul 144936270Swpaul#if NBPFILTER > 0 145036270Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 145136270Swpaul#endif 145236270Swpaul 145336270Swpaul return(0); 145436270Swpaul} 145536270Swpaul 145636270Swpaulstatic void 145736270Swpaultl_attach_ctlr(config_id, unit) 145836270Swpaul pcici_t config_id; 145936270Swpaul int unit; 146036270Swpaul{ 146136270Swpaul int s, i, phys = 0; 146236270Swpaul vm_offset_t pbase, vbase; 146336270Swpaul struct tl_csr *csr; 146436270Swpaul char eaddr[ETHER_ADDR_LEN]; 146536270Swpaul struct tl_mii_frame frame; 146636270Swpaul u_int32_t command; 146736270Swpaul struct tl_iflist *ilist; 146836270Swpaul 146936270Swpaul s = splimp(); 147036270Swpaul 147136270Swpaul for (ilist = tl_iflist; ilist != NULL; ilist = ilist->tl_next) 147236270Swpaul if (ilist->tl_config_id == config_id) 147336270Swpaul break; 147436270Swpaul 147536270Swpaul if (ilist == NULL) { 147636270Swpaul printf("couldn't match config id with controller struct\n"); 147736270Swpaul goto fail; 147836270Swpaul } 147936270Swpaul 148036270Swpaul /* 148136270Swpaul * Map control/status registers. 148236270Swpaul */ 148336270Swpaul pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, 148436270Swpaul PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 148536270Swpaul 148636270Swpaul command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); 148736270Swpaul 148836270Swpaul if (!(command & PCIM_CMD_MEMEN)) { 148936270Swpaul printf("tlc%d: failed to enable memory mapping!\n", unit); 149036270Swpaul goto fail; 149136270Swpaul } 149236270Swpaul 149336270Swpaul if (!pci_map_mem(config_id, TL_PCI_LOMEM, &vbase, &pbase)) { 149436270Swpaul printf ("tlc%d: couldn't map memory\n", unit); 149536270Swpaul goto fail; 149636270Swpaul } 149736270Swpaul 149836270Swpaul csr = (struct tl_csr *)vbase; 149936270Swpaul 150036270Swpaul ilist->csr = csr; 150136270Swpaul ilist->tl_active_phy = TL_PHYS_IDLE; 150236270Swpaul ilist->tlc_unit = unit; 150336270Swpaul 150436270Swpaul /* Allocate interrupt */ 150536270Swpaul if (!pci_map_int(config_id, tl_intr, ilist, &net_imask)) { 150636270Swpaul printf("tlc%d: couldn't map interrupt\n", unit); 150736270Swpaul goto fail; 150836270Swpaul } 150936270Swpaul 151036270Swpaul /* Reset the adapter. */ 151136270Swpaul tl_softreset(csr, 1); 151236270Swpaul 151336270Swpaul /* 151436270Swpaul * Get station address from the EEPROM. 151536270Swpaul */ 151636270Swpaul if (tl_read_eeprom(csr, (caddr_t)&eaddr, 151736270Swpaul TL_EEPROM_EADDR, ETHER_ADDR_LEN)) { 151836270Swpaul printf("tlc%d: failed to read station address\n", unit); 151936270Swpaul goto fail; 152036270Swpaul } 152136270Swpaul 152236270Swpaul /* 152336270Swpaul * A ThunderLAN chip was detected. Inform the world. 152436270Swpaul */ 152536270Swpaul printf("tlc%d: Ethernet address: %6D\n", unit, eaddr, ":"); 152636270Swpaul 152736270Swpaul /* 152836270Swpaul * Now attach the ThunderLAN's PHYs. There will always 152936270Swpaul * be at least one PHY; if the PHY address is 0x1F, then 153036270Swpaul * it's the internal one. If we encounter a lower numbered 153136270Swpaul * PHY, we ignore the internal once since enabling the 153236270Swpaul * internal PHY disables the external one. 153336270Swpaul */ 153436270Swpaul 153536270Swpaul bzero((char *)&frame, sizeof(frame)); 153636270Swpaul 153736270Swpaul for (i = TL_PHYADDR_MIN; i < TL_PHYADDR_MAX + 1; i++) { 153836270Swpaul frame.mii_phyaddr = i; 153936270Swpaul frame.mii_regaddr = TL_PHY_GENCTL; 154036270Swpaul frame.mii_data = PHY_BMCR_RESET; 154136270Swpaul tl_mii_writereg(csr, &frame); 154236270Swpaul DELAY(500); 154336270Swpaul while(frame.mii_data & PHY_BMCR_RESET) 154436270Swpaul tl_mii_readreg(csr, &frame); 154536270Swpaul frame.mii_regaddr = TL_PHY_VENID; 154636270Swpaul frame.mii_data = 0; 154736270Swpaul tl_mii_readreg(csr, &frame); 154836270Swpaul if (!frame.mii_data) 154936270Swpaul continue; 155036270Swpaul if (tl_attach_phy(csr, phys, eaddr, i, ilist)) { 155136270Swpaul printf("tlc%d: failed to attach interface %d\n", 155236270Swpaul unit, i); 155336270Swpaul goto fail; 155436270Swpaul } 155536270Swpaul phys++; 155636270Swpaul if (phys && i != TL_PHYADDR_MAX) 155736270Swpaul break; 155836270Swpaul } 155936270Swpaul 156036270Swpaul if (!phys) { 156136270Swpaul printf("tlc%d: no physical interfaces attached!\n", unit); 156236270Swpaul goto fail; 156336270Swpaul } 156436270Swpaul 156536270Swpaul at_shutdown(tl_shutdown, ilist, SHUTDOWN_POST_SYNC); 156636270Swpaul 156736270Swpaulfail: 156836270Swpaul splx(s); 156936270Swpaul return; 157036270Swpaul} 157136270Swpaul 157236270Swpaul/* 157336270Swpaul * Initialize the transmit lists. 157436270Swpaul */ 157536270Swpaulstatic int tl_list_tx_init(sc) 157636270Swpaul struct tl_softc *sc; 157736270Swpaul{ 157836270Swpaul struct tl_chain_data *cd; 157936270Swpaul struct tl_list_data *ld; 158036270Swpaul int i; 158136270Swpaul 158236270Swpaul cd = &sc->tl_cdata; 158336270Swpaul ld = sc->tl_ldata; 158436270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 158536270Swpaul cd->tl_tx_chain[i].tl_ptr = &ld->tl_tx_list[i]; 158636270Swpaul if (i == (TL_TX_LIST_CNT - 1)) 158736270Swpaul cd->tl_tx_chain[i].tl_next = NULL; 158836270Swpaul else 158936270Swpaul cd->tl_tx_chain[i].tl_next = &cd->tl_tx_chain[i + 1]; 159036270Swpaul } 159136270Swpaul 159236270Swpaul cd->tl_tx_free = &cd->tl_tx_chain[0]; 159336270Swpaul cd->tl_tx_tail = cd->tl_tx_head = NULL; 159436270Swpaul sc->tl_txeoc = 1; 159536270Swpaul 159636270Swpaul return(0); 159736270Swpaul} 159836270Swpaul 159936270Swpaul/* 160036270Swpaul * Initialize the RX lists and allocate mbufs for them. 160136270Swpaul */ 160236270Swpaulstatic int tl_list_rx_init(sc) 160336270Swpaul struct tl_softc *sc; 160436270Swpaul{ 160536270Swpaul struct tl_chain_data *cd; 160636270Swpaul struct tl_list_data *ld; 160736270Swpaul int i; 160836270Swpaul 160936270Swpaul cd = &sc->tl_cdata; 161036270Swpaul ld = sc->tl_ldata; 161136270Swpaul 161236270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 161336270Swpaul cd->tl_rx_chain[i].tl_ptr = 161436270Swpaul (struct tl_list *)&ld->tl_rx_list[i]; 161536270Swpaul tl_newbuf(sc, &cd->tl_rx_chain[i]); 161636270Swpaul if (i == (TL_TX_LIST_CNT - 1)) { 161736270Swpaul cd->tl_rx_chain[i].tl_next = NULL; 161836270Swpaul ld->tl_rx_list[i].tlist_fptr = 0; 161936270Swpaul } else { 162036270Swpaul cd->tl_rx_chain[i].tl_next = &cd->tl_rx_chain[i + 1]; 162136270Swpaul ld->tl_rx_list[i].tlist_fptr = 162236270Swpaul vtophys(&ld->tl_rx_list[i + 1]); 162336270Swpaul } 162436270Swpaul } 162536270Swpaul 162636270Swpaul cd->tl_rx_head = &cd->tl_rx_chain[0]; 162736270Swpaul cd->tl_rx_tail = &cd->tl_rx_chain[TL_RX_LIST_CNT - 1]; 162836270Swpaul 162936270Swpaul return(0); 163036270Swpaul} 163136270Swpaul 163236270Swpaulstatic int tl_newbuf(sc, c) 163336270Swpaul struct tl_softc *sc; 163436270Swpaul struct tl_chain *c; 163536270Swpaul{ 163636270Swpaul struct mbuf *m_new = NULL; 163736270Swpaul 163836270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 163936270Swpaul if (m_new == NULL) { 164036270Swpaul printf("tl%d: no memory for rx list", 164136270Swpaul sc->tl_unit); 164236270Swpaul return(ENOBUFS); 164336270Swpaul } 164436270Swpaul 164536270Swpaul MCLGET(m_new, M_DONTWAIT); 164636270Swpaul if (!(m_new->m_flags & M_EXT)) { 164736270Swpaul printf("tl%d: no memory for rx list", sc->tl_unit); 164836270Swpaul m_freem(m_new); 164936270Swpaul return(ENOBUFS); 165036270Swpaul } 165136270Swpaul 165236270Swpaul c->tl_mbuf = m_new; 165336270Swpaul c->tl_next = NULL; 165436270Swpaul c->tl_ptr->tlist_frsize = MCLBYTES; 165536270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 165636270Swpaul c->tl_ptr->tlist_fptr = 0; 165736270Swpaul c->tl_ptr->tl_frag[0].tlist_dadr = vtophys(mtod(m_new, caddr_t)); 165836270Swpaul c->tl_ptr->tl_frag[0].tlist_dcnt = MCLBYTES; 165936270Swpaul 166036270Swpaul return(0); 166136270Swpaul} 166236270Swpaul/* 166336270Swpaul * Interrupt handler for RX 'end of frame' condition (EOF). This 166436270Swpaul * tells us that a full ethernet frame has been captured and we need 166536270Swpaul * to handle it. 166636270Swpaul * 166736270Swpaul * Reception is done using 'lists' which consist of a header and a 166836270Swpaul * series of 10 data count/data address pairs that point to buffers. 166936270Swpaul * Initially you're supposed to create a list, populate it with pointers 167036270Swpaul * to buffers, then load the physical address of the list into the 167136270Swpaul * ch_parm register. The adapter is then supposed to DMA the received 167236270Swpaul * frame into the buffers for you. 167336270Swpaul * 167436270Swpaul * To make things as fast as possible, we have the chip DMA directly 167536270Swpaul * into mbufs. This saves us from having to do a buffer copy: we can 167636270Swpaul * just hand the mbufs directly to ether_input(). Once the frame has 167736270Swpaul * been sent on its way, the 'list' structure is assigned a new buffer 167836270Swpaul * and moved to the end of the RX chain. As long we we stay ahead of 167936270Swpaul * the chip, it will always think it has an endless receive channel. 168036270Swpaul * 168136270Swpaul * If we happen to fall behind and the chip manages to fill up all of 168236270Swpaul * the buffers, it will generate an end of channel interrupt and wait 168336270Swpaul * for us to empty the chain and restart the receiver. 168436270Swpaul */ 168536270Swpaulstatic int tl_intvec_rxeof(xsc, type) 168636270Swpaul void *xsc; 168736270Swpaul u_int32_t type; 168836270Swpaul{ 168936270Swpaul struct tl_softc *sc; 169036270Swpaul int r = 0, total_len = 0; 169136270Swpaul struct ether_header *eh; 169236270Swpaul struct mbuf *m; 169336270Swpaul struct ifnet *ifp; 169436270Swpaul struct tl_chain *cur_rx; 169536270Swpaul 169636270Swpaul sc = xsc; 169736270Swpaul ifp = &sc->arpcom.ac_if; 169836270Swpaul 169936270Swpaul while(sc->tl_cdata.tl_rx_head->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP){ 170036270Swpaul r++; 170136270Swpaul cur_rx = sc->tl_cdata.tl_rx_head; 170236270Swpaul sc->tl_cdata.tl_rx_head = cur_rx->tl_next; 170336270Swpaul m = cur_rx->tl_mbuf; 170436270Swpaul total_len = cur_rx->tl_ptr->tlist_frsize; 170536270Swpaul 170636270Swpaul tl_newbuf(sc, cur_rx); 170736270Swpaul 170836270Swpaul sc->tl_cdata.tl_rx_tail->tl_ptr->tlist_fptr = 170936270Swpaul vtophys(cur_rx->tl_ptr); 171036270Swpaul sc->tl_cdata.tl_rx_tail->tl_next = cur_rx; 171136270Swpaul sc->tl_cdata.tl_rx_tail = cur_rx; 171236270Swpaul 171336270Swpaul eh = mtod(m, struct ether_header *); 171436270Swpaul m->m_pkthdr.rcvif = ifp; 171536270Swpaul 171636270Swpaul#if NBPFILTER > 0 171736270Swpaul /* 171836270Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 171936270Swpaul * don't pass it up to the ether_input() layer unless it's 172036270Swpaul * a broadcast packet, multicast packet, matches our ethernet 172136270Swpaul * address or the interface is in promiscuous mode. If we don't 172236270Swpaul * want the packet, just forget it. We leave the mbuf in place 172336270Swpaul * since it can be used again later. 172436270Swpaul */ 172536270Swpaul if (ifp->if_bpf) { 172636270Swpaul m->m_pkthdr.len = m->m_len = total_len; 172736270Swpaul bpf_mtap(ifp, m); 172836270Swpaul if (ifp->if_flags & IFF_PROMISC && 172936270Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 173036270Swpaul ETHER_ADDR_LEN) && 173136270Swpaul (eh->ether_dhost[0] & 1) == 0)) { 173236270Swpaul m_freem(m); 173336270Swpaul continue; 173436270Swpaul } 173536270Swpaul } 173636270Swpaul#endif 173736270Swpaul /* Remove header from mbuf and pass it on. */ 173836270Swpaul m->m_pkthdr.len = m->m_len = 173936270Swpaul total_len - sizeof(struct ether_header); 174036270Swpaul m->m_data += sizeof(struct ether_header); 174136270Swpaul ether_input(ifp, eh, m); 174236270Swpaul } 174336270Swpaul 174436270Swpaul return(r); 174536270Swpaul} 174636270Swpaul 174736270Swpaul/* 174836270Swpaul * The RX-EOC condition hits when the ch_parm address hasn't been 174936270Swpaul * initialized or the adapter reached a list with a forward pointer 175036270Swpaul * of 0 (which indicates the end of the chain). In our case, this means 175136270Swpaul * the card has hit the end of the receive buffer chain and we need to 175236270Swpaul * empty out the buffers and shift the pointer back to the beginning again. 175336270Swpaul */ 175436270Swpaulstatic int tl_intvec_rxeoc(xsc, type) 175536270Swpaul void *xsc; 175636270Swpaul u_int32_t type; 175736270Swpaul{ 175836270Swpaul struct tl_softc *sc; 175936270Swpaul int r; 176036270Swpaul 176136270Swpaul sc = xsc; 176236270Swpaul 176336270Swpaul /* Flush out the receive queue and ack RXEOF interrupts. */ 176436270Swpaul r = tl_intvec_rxeof(xsc, type); 176536270Swpaul sc->csr->tl_host_cmd = TL_CMD_ACK | r | (type & ~(0x00100000)); 176636270Swpaul r = 1; 176736270Swpaul sc->csr->tl_ch_parm = vtophys(sc->tl_cdata.tl_rx_head->tl_ptr); 176836270Swpaul r |= (TL_CMD_GO|TL_CMD_RT); 176936270Swpaul return(r); 177036270Swpaul} 177136270Swpaul 177236270Swpaul/* 177336270Swpaul * Invalid interrupt handler. The manual says invalid interrupts 177436270Swpaul * are caused by a hardware error in other hardware and that they 177536270Swpaul * should just be ignored. 177636270Swpaul */ 177736270Swpaulstatic int tl_intvec_invalid(xsc, type) 177836270Swpaul void *xsc; 177936270Swpaul u_int32_t type; 178036270Swpaul{ 178136270Swpaul struct tl_softc *sc; 178236270Swpaul 178336270Swpaul sc = xsc; 178436270Swpaul 178536270Swpaul#ifdef DIAGNOSTIC 178636270Swpaul printf("tl%d: got an invalid interrupt!\n", sc->tl_unit); 178736270Swpaul#endif 178836270Swpaul /* Re-enable interrupts but don't ack this one. */ 178936270Swpaul sc->csr->tl_host_cmd |= type; 179036270Swpaul 179136270Swpaul return(0); 179236270Swpaul} 179336270Swpaul 179436270Swpaul/* 179536270Swpaul * Dummy interrupt handler. Dummy interrupts are generated by setting 179636270Swpaul * the ReqInt bit in the host command register. They should only occur 179736270Swpaul * if we ask for them, and we never do, so if one magically appears, 179836270Swpaul * we should make some noise about it. 179936270Swpaul */ 180036270Swpaulstatic int tl_intvec_dummy(xsc, type) 180136270Swpaul void *xsc; 180236270Swpaul u_int32_t type; 180336270Swpaul{ 180436270Swpaul struct tl_softc *sc; 180536270Swpaul 180636270Swpaul sc = xsc; 180736270Swpaul printf("tl%d: got a dummy interrupt\n", sc->tl_unit); 180836270Swpaul 180936270Swpaul return(1); 181036270Swpaul} 181136270Swpaul 181236270Swpaul/* 181336270Swpaul * Stats counter overflow interrupt. The chip delivers one of these 181436270Swpaul * if we don't poll the stats counters often enough. 181536270Swpaul */ 181636270Swpaulstatic int tl_intvec_statoflow(xsc, type) 181736270Swpaul void *xsc; 181836270Swpaul u_int32_t type; 181936270Swpaul{ 182036270Swpaul struct tl_softc *sc; 182136270Swpaul 182236270Swpaul sc = xsc; 182336270Swpaul 182436270Swpaul tl_stats_update(sc); 182536270Swpaul 182636270Swpaul return(1); 182736270Swpaul} 182836270Swpaul 182936270Swpaulstatic int tl_intvec_txeof(xsc, type) 183036270Swpaul void *xsc; 183136270Swpaul u_int32_t type; 183236270Swpaul{ 183336270Swpaul struct tl_softc *sc; 183436270Swpaul int r = 0; 183536270Swpaul struct tl_chain *cur_tx; 183636270Swpaul 183736270Swpaul sc = xsc; 183836270Swpaul 183936270Swpaul /* 184036270Swpaul * Go through our tx list and free mbufs for those 184136270Swpaul * frames that have been sent. 184236270Swpaul */ 184336270Swpaul while (sc->tl_cdata.tl_tx_head != NULL) { 184436270Swpaul cur_tx = sc->tl_cdata.tl_tx_head; 184536270Swpaul if (!(cur_tx->tl_ptr->tlist_cstat & TL_CSTAT_FRAMECMP)) 184636270Swpaul break; 184736270Swpaul sc->tl_cdata.tl_tx_head = cur_tx->tl_next; 184836270Swpaul 184936270Swpaul r++; 185036270Swpaul m_freem(cur_tx->tl_mbuf); 185136270Swpaul cur_tx->tl_mbuf = NULL; 185236270Swpaul 185336270Swpaul cur_tx->tl_next = sc->tl_cdata.tl_tx_free; 185436270Swpaul sc->tl_cdata.tl_tx_free = cur_tx; 185536270Swpaul } 185636270Swpaul 185736270Swpaul return(r); 185836270Swpaul} 185936270Swpaul 186036270Swpaul/* 186136270Swpaul * The transmit end of channel interrupt. The adapter triggers this 186236270Swpaul * interrupt to tell us it hit the end of the current transmit list. 186336270Swpaul * 186436270Swpaul * A note about this: it's possible for a condition to arise where 186536270Swpaul * tl_start() may try to send frames between TXEOF and TXEOC interrupts. 186636270Swpaul * You have to avoid this since the chip expects things to go in a 186736270Swpaul * particular order: transmit, acknowledge TXEOF, acknowledge TXEOC. 186836270Swpaul * When the TXEOF handler is called, it will free all of the transmitted 186936270Swpaul * frames and reset the tx_head pointer to NULL. However, a TXEOC 187036270Swpaul * interrupt should be received and acknowledged before any more frames 187136270Swpaul * are queued for transmission. If tl_statrt() is called after TXEOF 187236270Swpaul * resets the tx_head pointer but _before_ the TXEOC interrupt arrives, 187336270Swpaul * it could attempt to issue a transmit command prematurely. 187436270Swpaul * 187536270Swpaul * To guard against this, tl_start() will only issue transmit commands 187636270Swpaul * if the tl_txeoc flag is set, and only the TXEOC interrupt handler 187736270Swpaul * can set this flag once tl_start() has cleared it. 187836270Swpaul */ 187936270Swpaulstatic int tl_intvec_txeoc(xsc, type) 188036270Swpaul void *xsc; 188136270Swpaul u_int32_t type; 188236270Swpaul{ 188336270Swpaul struct tl_softc *sc; 188436270Swpaul struct ifnet *ifp; 188536270Swpaul u_int32_t cmd; 188636270Swpaul 188736270Swpaul sc = xsc; 188836270Swpaul ifp = &sc->arpcom.ac_if; 188936270Swpaul 189036270Swpaul /* Clear the timeout timer. */ 189136270Swpaul ifp->if_timer = 0; 189236270Swpaul 189336270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 189436270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 189536270Swpaul sc->tl_cdata.tl_tx_tail = NULL; 189636270Swpaul sc->tl_txeoc = 1; 189736270Swpaul } else { 189836270Swpaul sc->tl_txeoc = 0; 189936270Swpaul /* First we have to ack the EOC interrupt. */ 190036270Swpaul sc->csr->tl_host_cmd = TL_CMD_ACK | 0x00000001 | type; 190136270Swpaul /* Then load the address of the next TX list. */ 190236270Swpaul sc->csr->tl_ch_parm = vtophys(sc->tl_cdata.tl_tx_head->tl_ptr); 190336270Swpaul /* Restart TX channel. */ 190436270Swpaul cmd = sc->csr->tl_host_cmd; 190536270Swpaul cmd &= ~TL_CMD_RT; 190636270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 190736270Swpaul sc->csr->tl_host_cmd = cmd; 190836270Swpaul return(0); 190936270Swpaul } 191036270Swpaul 191136270Swpaul return(1); 191236270Swpaul} 191336270Swpaul 191436270Swpaulstatic int tl_intvec_adchk(xsc, type) 191536270Swpaul void *xsc; 191636270Swpaul u_int32_t type; 191736270Swpaul{ 191836270Swpaul struct tl_softc *sc; 191936270Swpaul 192036270Swpaul sc = xsc; 192136270Swpaul 192236270Swpaul printf("tl%d: adapter check: %x\n", sc->tl_unit, sc->csr->tl_ch_parm); 192336270Swpaul 192436270Swpaul tl_softreset(sc->csr, sc->tl_phy_addr == TL_PHYADDR_MAX ? 1 : 0); 192536270Swpaul tl_init(sc); 192636270Swpaul sc->csr->tl_host_cmd |= TL_CMD_INTSON; 192736270Swpaul 192836270Swpaul return(0); 192936270Swpaul} 193036270Swpaul 193136270Swpaulstatic int tl_intvec_netsts(xsc, type) 193236270Swpaul void *xsc; 193336270Swpaul u_int32_t type; 193436270Swpaul{ 193536270Swpaul struct tl_softc *sc; 193636270Swpaul u_int16_t netsts; 193736270Swpaul struct tl_csr *csr; 193836270Swpaul 193936270Swpaul sc = xsc; 194036270Swpaul csr = sc->csr; 194136270Swpaul 194236270Swpaul DIO_SEL(TL_NETSTS); 194336270Swpaul netsts = DIO_BYTE2_GET(0xFF); 194436270Swpaul DIO_BYTE2_SET(netsts); 194536270Swpaul 194636270Swpaul printf("tl%d: network status: %x\n", sc->tl_unit, netsts); 194736270Swpaul 194836270Swpaul return(1); 194936270Swpaul} 195036270Swpaul 195136270Swpaulstatic void tl_intr(xilist) 195236270Swpaul void *xilist; 195336270Swpaul{ 195436270Swpaul struct tl_iflist *ilist; 195536270Swpaul struct tl_softc *sc; 195636270Swpaul struct tl_csr *csr; 195736270Swpaul struct ifnet *ifp; 195836270Swpaul int r = 0; 195936270Swpaul u_int32_t type = 0; 196036270Swpaul u_int16_t ints = 0; 196136270Swpaul u_int8_t ivec = 0; 196236270Swpaul 196336270Swpaul ilist = xilist; 196436270Swpaul csr = ilist->csr; 196536270Swpaul 196636270Swpaul /* Disable interrupts */ 196736270Swpaul ints = csr->tl_host_int; 196836270Swpaul csr->tl_host_int = ints; 196936270Swpaul type = (ints << 16) & 0xFFFF0000; 197036270Swpaul ivec = (ints & TL_VEC_MASK) >> 5; 197136270Swpaul ints = (ints & TL_INT_MASK) >> 2; 197236270Swpaul /* 197336270Swpaul * An interrupt has been posted by the ThunderLAN, but we 197436270Swpaul * have to figure out which PHY generated it before we can 197536270Swpaul * do anything with it. If we receive an interrupt when we 197636270Swpaul * know none of the PHYs are turned on, then either there's 197736270Swpaul * a bug in the driver or we we handed an interrupt that 197836270Swpaul * doesn't actually belong to us. 197936270Swpaul */ 198036270Swpaul if (ilist->tl_active_phy == TL_PHYS_IDLE) { 198136270Swpaul printf("tlc%d: interrupt type %x with all phys idle\n", 198236270Swpaul ilist->tlc_unit, ints); 198336270Swpaul return; 198436270Swpaul } 198536270Swpaul 198636270Swpaul sc = ilist->tl_sc[ilist->tl_active_phy]; 198736270Swpaul csr = sc->csr; 198836270Swpaul ifp = &sc->arpcom.ac_if; 198936270Swpaul 199036270Swpaul switch(ints) { 199136270Swpaul case (TL_INTR_INVALID): 199236270Swpaul r = tl_intvec_invalid((void *)sc, type); 199336270Swpaul break; 199436270Swpaul case (TL_INTR_TXEOF): 199536270Swpaul r = tl_intvec_txeof((void *)sc, type); 199636270Swpaul break; 199736270Swpaul case (TL_INTR_TXEOC): 199836270Swpaul r = tl_intvec_txeoc((void *)sc, type); 199936270Swpaul break; 200036270Swpaul case (TL_INTR_STATOFLOW): 200136270Swpaul r = tl_intvec_statoflow((void *)sc, type); 200236270Swpaul break; 200336270Swpaul case (TL_INTR_RXEOF): 200436270Swpaul r = tl_intvec_rxeof((void *)sc, type); 200536270Swpaul break; 200636270Swpaul case (TL_INTR_DUMMY): 200736270Swpaul r = tl_intvec_dummy((void *)sc, type); 200836270Swpaul break; 200936270Swpaul case (TL_INTR_ADCHK): 201036270Swpaul if (ivec) 201136270Swpaul r = tl_intvec_adchk((void *)sc, type); 201236270Swpaul else 201336270Swpaul r = tl_intvec_netsts((void *)sc, type); 201436270Swpaul break; 201536270Swpaul case (TL_INTR_RXEOC): 201636270Swpaul r = tl_intvec_rxeoc((void *)sc, type); 201736270Swpaul break; 201836270Swpaul default: 201936270Swpaul printf("tl%d: bogus interrupt type\n", ifp->if_unit); 202036270Swpaul break; 202136270Swpaul } 202236270Swpaul 202336270Swpaul /* Re-enable interrupts */ 202436270Swpaul if (r) 202536270Swpaul csr->tl_host_cmd = TL_CMD_ACK | r | type; 202636270Swpaul 202736270Swpaul return; 202836270Swpaul} 202936270Swpaul 203036270Swpaulstatic void tl_stats_update(xsc) 203136270Swpaul void *xsc; 203236270Swpaul{ 203336270Swpaul struct tl_softc *sc; 203436270Swpaul struct ifnet *ifp; 203536270Swpaul struct tl_csr *csr; 203636270Swpaul struct tl_stats tl_stats; 203736270Swpaul u_int32_t *p; 203836270Swpaul 203936270Swpaul bzero((char *)&tl_stats, sizeof(struct tl_stats)); 204036270Swpaul 204136270Swpaul sc = xsc; 204236270Swpaul csr = sc->csr; 204336270Swpaul ifp = &sc->arpcom.ac_if; 204436270Swpaul 204536270Swpaul p = (u_int32_t *)&tl_stats; 204636270Swpaul 204736270Swpaul DIO_SEL(TL_TXGOODFRAMES|TL_DIO_ADDR_INC); 204836270Swpaul DIO_LONG_GET(*p++); 204936270Swpaul DIO_LONG_GET(*p++); 205036270Swpaul DIO_LONG_GET(*p++); 205136270Swpaul DIO_LONG_GET(*p++); 205236270Swpaul DIO_LONG_GET(*p++); 205336270Swpaul 205436270Swpaul ifp->if_opackets += tl_tx_goodframes(tl_stats); 205536270Swpaul ifp->if_collisions += tl_stats.tl_tx_single_collision + 205636270Swpaul tl_stats.tl_tx_multi_collision; 205736270Swpaul ifp->if_ipackets += tl_rx_goodframes(tl_stats); 205836270Swpaul ifp->if_ierrors += tl_stats.tl_crc_errors + tl_stats.tl_code_errors + 205936270Swpaul tl_rx_overrun(tl_stats); 206036270Swpaul ifp->if_oerrors += tl_tx_underrun(tl_stats); 206136270Swpaul 206236270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 206336302Swpaul 206436302Swpaul return; 206536270Swpaul} 206636270Swpaul 206736270Swpaul/* 206836270Swpaul * Encapsulate an mbuf chain in a list by coupling the mbuf data 206936270Swpaul * pointers to the fragment pointers. 207036270Swpaul */ 207136270Swpaulstatic int tl_encap(sc, c, m_head) 207236270Swpaul struct tl_softc *sc; 207336270Swpaul struct tl_chain *c; 207436270Swpaul struct mbuf *m_head; 207536270Swpaul{ 207636270Swpaul int frag = 0; 207736270Swpaul struct tl_frag *f = NULL; 207836270Swpaul int total_len; 207936270Swpaul struct mbuf *m; 208036270Swpaul 208136270Swpaul /* 208236270Swpaul * Start packing the mbufs in this chain into 208336270Swpaul * the fragment pointers. Stop when we run out 208436270Swpaul * of fragments or hit the end of the mbuf chain. 208536270Swpaul */ 208636270Swpaul m = m_head; 208736270Swpaul total_len = 0; 208836270Swpaul 208936270Swpaul for (m = m_head, frag = 0; m != NULL; m = m->m_next) { 209036270Swpaul if (m->m_len != 0) { 209136270Swpaul if (frag == TL_MAXFRAGS) 209236270Swpaul break; 209336270Swpaul total_len+= m->m_len; 209436270Swpaul c->tl_ptr->tl_frag[frag].tlist_dadr = 209536270Swpaul vtophys(mtod(m, vm_offset_t)); 209636270Swpaul c->tl_ptr->tl_frag[frag].tlist_dcnt = m->m_len; 209736270Swpaul frag++; 209836270Swpaul } 209936270Swpaul } 210036270Swpaul 210136270Swpaul /* 210236270Swpaul * Handle special cases. 210336270Swpaul * Special case #1: we used up all 10 fragments, but 210436270Swpaul * we have more mbufs left in the chain. Copy the 210536270Swpaul * data into an mbuf cluster. Note that we don't 210636270Swpaul * bother clearing the values in the other fragment 210736270Swpaul * pointers/counters; it wouldn't gain us anything, 210836270Swpaul * and would waste cycles. 210936270Swpaul */ 211036270Swpaul if (m != NULL) { 211136270Swpaul struct mbuf *m_new = NULL; 211236270Swpaul 211336270Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 211436270Swpaul if (m_new == NULL) { 211536270Swpaul printf("tl%d: no memory for tx list", sc->tl_unit); 211636270Swpaul return(1); 211736270Swpaul } 211836270Swpaul if (m_head->m_pkthdr.len > MHLEN) { 211936270Swpaul MCLGET(m_new, M_DONTWAIT); 212036270Swpaul if (!(m_new->m_flags & M_EXT)) { 212136270Swpaul m_freem(m_new); 212236270Swpaul printf("tl%d: no memory for tx list", 212336270Swpaul sc->tl_unit); 212436270Swpaul return(1); 212536270Swpaul } 212636270Swpaul } 212736270Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 212836270Swpaul mtod(m_new, caddr_t)); 212936270Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 213036270Swpaul m_freem(m_head); 213136270Swpaul m_head = m_new; 213236270Swpaul f = &c->tl_ptr->tl_frag[0]; 213336270Swpaul f->tlist_dadr = vtophys(mtod(m_new, caddr_t)); 213436270Swpaul f->tlist_dcnt = total_len = m_new->m_len; 213536270Swpaul frag = 1; 213636270Swpaul } 213736270Swpaul 213836270Swpaul /* 213936270Swpaul * Special case #2: the frame is smaller than the minimum 214036270Swpaul * frame size. We have to pad it to make the chip happy. 214136270Swpaul */ 214236270Swpaul if (total_len < TL_MIN_FRAMELEN) { 214336270Swpaul if (frag == TL_MAXFRAGS) 214436270Swpaul printf("all frags filled but frame still to small!\n"); 214536270Swpaul f = &c->tl_ptr->tl_frag[frag]; 214636270Swpaul f->tlist_dcnt = TL_MIN_FRAMELEN - total_len; 214736270Swpaul f->tlist_dadr = vtophys(&sc->tl_ldata->tl_pad); 214836270Swpaul total_len += f->tlist_dcnt; 214936270Swpaul frag++; 215036270Swpaul } 215136270Swpaul 215236270Swpaul c->tl_mbuf = m_head; 215336270Swpaul c->tl_ptr->tl_frag[frag - 1].tlist_dcnt |= TL_LAST_FRAG; 215436270Swpaul c->tl_ptr->tlist_frsize = total_len; 215536270Swpaul c->tl_ptr->tlist_cstat = TL_CSTAT_READY; 215636270Swpaul c->tl_ptr->tlist_fptr = 0; 215736270Swpaul 215836270Swpaul return(0); 215936270Swpaul} 216036270Swpaul 216136270Swpaul/* 216236270Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 216336270Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 216436270Swpaul * copy of the pointers since the transmit list fragment pointers are 216536270Swpaul * physical addresses. 216636270Swpaul */ 216736270Swpaulstatic void tl_start(ifp) 216836270Swpaul struct ifnet *ifp; 216936270Swpaul{ 217036270Swpaul struct tl_softc *sc; 217136270Swpaul struct tl_csr *csr; 217236270Swpaul struct mbuf *m_head = NULL; 217336270Swpaul u_int32_t cmd; 217436270Swpaul struct tl_chain *prev = NULL, *cur_tx = NULL, *start_tx; 217536270Swpaul 217636270Swpaul sc = ifp->if_softc; 217736270Swpaul csr = sc->csr; 217836270Swpaul 217936270Swpaul /* 218036270Swpaul * Check for an available queue slot. If there are none, 218136270Swpaul * punt. 218236270Swpaul */ 218336270Swpaul if (sc->tl_cdata.tl_tx_free == NULL) { 218436270Swpaul ifp->if_flags |= IFF_OACTIVE; 218536270Swpaul return; 218636270Swpaul } 218736270Swpaul 218836270Swpaul start_tx = sc->tl_cdata.tl_tx_free; 218936270Swpaul 219036270Swpaul while(sc->tl_cdata.tl_tx_free != NULL) { 219136270Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 219236270Swpaul if (m_head == NULL) 219336270Swpaul break; 219436270Swpaul 219536270Swpaul /* Pick a chain member off the free list. */ 219636270Swpaul cur_tx = sc->tl_cdata.tl_tx_free; 219736270Swpaul sc->tl_cdata.tl_tx_free = cur_tx->tl_next; 219836270Swpaul 219936270Swpaul cur_tx->tl_next = NULL; 220036270Swpaul 220136270Swpaul /* Pack the data into the list. */ 220236270Swpaul tl_encap(sc, cur_tx, m_head); 220336270Swpaul 220436270Swpaul /* Chain it together */ 220536270Swpaul if (prev != NULL) { 220636270Swpaul prev->tl_next = cur_tx; 220736270Swpaul prev->tl_ptr->tlist_fptr = vtophys(cur_tx->tl_ptr); 220836270Swpaul } 220936270Swpaul prev = cur_tx; 221036270Swpaul 221136270Swpaul /* 221236270Swpaul * If there's a BPF listener, bounce a copy of this frame 221336270Swpaul * to him. 221436270Swpaul */ 221536270Swpaul#if NBPFILTER > 0 221636270Swpaul if (ifp->if_bpf) 221736270Swpaul bpf_mtap(ifp, cur_tx->tl_mbuf); 221836270Swpaul#endif 221936270Swpaul } 222036270Swpaul 222136270Swpaul /* 222236270Swpaul * That's all we can stands, we can't stands no more. 222336270Swpaul * If there are no other transfers pending, then issue the 222436270Swpaul * TX GO command to the adapter to start things moving. 222536270Swpaul * Otherwise, just leave the data in the queue and let 222636270Swpaul * the EOF/EOC interrupt handler send. 222736270Swpaul */ 222836270Swpaul if (sc->tl_cdata.tl_tx_head == NULL) { 222936270Swpaul sc->tl_cdata.tl_tx_head = start_tx; 223036270Swpaul sc->tl_cdata.tl_tx_tail = cur_tx; 223136270Swpaul if (sc->tl_txeoc) { 223236270Swpaul sc->tl_txeoc = 0; 223336270Swpaul sc->csr->tl_ch_parm = vtophys(start_tx->tl_ptr); 223436270Swpaul cmd = sc->csr->tl_host_cmd; 223536270Swpaul cmd &= ~TL_CMD_RT; 223636270Swpaul cmd |= TL_CMD_GO|TL_CMD_INTSON; 223736270Swpaul sc->csr->tl_host_cmd = cmd; 223836270Swpaul } 223936270Swpaul } else { 224036270Swpaul sc->tl_cdata.tl_tx_tail->tl_next = start_tx; 224136270Swpaul sc->tl_cdata.tl_tx_tail->tl_ptr->tlist_fptr = 224236270Swpaul vtophys(start_tx->tl_ptr); 224336270Swpaul sc->tl_cdata.tl_tx_tail = start_tx; 224436270Swpaul } 224536270Swpaul 224636270Swpaul /* 224736270Swpaul * Set a timeout in case the chip goes out to lunch. 224836270Swpaul */ 224936270Swpaul ifp->if_timer = 5; 225036270Swpaul 225136270Swpaul return; 225236270Swpaul} 225336270Swpaul 225436270Swpaulstatic void tl_init(xsc) 225536270Swpaul void *xsc; 225636270Swpaul{ 225736270Swpaul struct tl_softc *sc = xsc; 225836270Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 225936270Swpaul struct tl_csr *csr = sc->csr; 226036270Swpaul int s; 226136270Swpaul u_int16_t phy_sts; 226236270Swpaul 226336270Swpaul s = splimp(); 226436270Swpaul 226536270Swpaul ifp = &sc->arpcom.ac_if; 226636270Swpaul 226736270Swpaul /* 226836270Swpaul * Cancel pending I/O. 226936270Swpaul */ 227036270Swpaul tl_stop(sc); 227136270Swpaul 227236270Swpaul /* 227336270Swpaul * Set 'capture all frames' bit for promiscuous mode. 227436270Swpaul */ 227536270Swpaul if (ifp->if_flags & IFF_PROMISC) { 227636270Swpaul DIO_SEL(TL_NETCMD); 227736270Swpaul DIO_BYTE0_SET(TL_CMD_CAF); 227836270Swpaul } else { 227936270Swpaul DIO_SEL(TL_NETCMD); 228036270Swpaul DIO_BYTE0_CLR(TL_CMD_CAF); 228136270Swpaul } 228236270Swpaul 228336270Swpaul /* 228436270Swpaul * Set capture broadcast bit to capture broadcast frames. 228536270Swpaul */ 228636270Swpaul if (ifp->if_flags & IFF_BROADCAST) { 228736270Swpaul DIO_SEL(TL_NETCMD); 228836270Swpaul DIO_BYTE0_CLR(TL_CMD_NOBRX); 228936270Swpaul } else { 229036270Swpaul DIO_SEL(TL_NETCMD); 229136270Swpaul DIO_BYTE0_SET(TL_CMD_NOBRX); 229236270Swpaul } 229336270Swpaul 229436270Swpaul /* Init our MAC address */ 229536270Swpaul DIO_SEL(TL_AREG0_B5); 229636270Swpaul csr->u.tl_dio_bytes.byte0 = sc->arpcom.ac_enaddr[0]; 229736270Swpaul csr->u.tl_dio_bytes.byte1 = sc->arpcom.ac_enaddr[1]; 229836270Swpaul csr->u.tl_dio_bytes.byte2 = sc->arpcom.ac_enaddr[2]; 229936270Swpaul csr->u.tl_dio_bytes.byte3 = sc->arpcom.ac_enaddr[3]; 230036270Swpaul DIO_SEL(TL_AREG0_B1); 230136270Swpaul csr->u.tl_dio_bytes.byte0 = sc->arpcom.ac_enaddr[4]; 230236270Swpaul csr->u.tl_dio_bytes.byte1 = sc->arpcom.ac_enaddr[5]; 230336270Swpaul 230436270Swpaul /* Init circular RX list. */ 230536270Swpaul if (tl_list_rx_init(sc)) { 230636270Swpaul printf("tl%d: failed to set up rx lists\n", sc->tl_unit); 230736270Swpaul return; 230836270Swpaul } 230936270Swpaul 231036270Swpaul /* Init TX pointers. */ 231136270Swpaul tl_list_tx_init(sc); 231236270Swpaul 231336270Swpaul /* 231436270Swpaul * Enable PHY interrupts. 231536270Swpaul */ 231636270Swpaul phy_sts = tl_phy_readreg(sc, TL_PHY_CTL); 231736270Swpaul phy_sts |= PHY_CTL_INTEN; 231836270Swpaul tl_phy_writereg(sc, TL_PHY_CTL, phy_sts); 231936270Swpaul 232036270Swpaul /* Enable MII interrupts. */ 232136270Swpaul DIO_SEL(TL_NETSIO); 232236270Swpaul DIO_BYTE1_SET(TL_SIO_MINTEN); 232336270Swpaul 232436270Swpaul /* Enable PCI interrupts. */ 232536270Swpaul csr->tl_host_cmd |= TL_CMD_INTSON; 232636270Swpaul 232736270Swpaul /* Load the address of the rx list */ 232836270Swpaul sc->csr->tl_host_cmd |= TL_CMD_RT; 232936270Swpaul sc->csr->tl_ch_parm = vtophys(&sc->tl_ldata->tl_rx_list[0]); 233036270Swpaul 233136270Swpaul /* Send the RX go command */ 233236270Swpaul sc->csr->tl_host_cmd |= (TL_CMD_GO|TL_CMD_RT); 233336270Swpaul sc->tl_iflist->tl_active_phy = sc->tl_phy_addr; 233436270Swpaul 233536270Swpaul ifp->if_flags |= IFF_RUNNING; 233636270Swpaul ifp->if_flags &= ~IFF_OACTIVE; 233736270Swpaul 233836270Swpaul (void)splx(s); 233936270Swpaul 234036270Swpaul /* Start the stats update counter */ 234136270Swpaul sc->tl_stat_ch = timeout(tl_stats_update, sc, hz); 234236270Swpaul 234336270Swpaul return; 234436270Swpaul} 234536270Swpaul 234636270Swpaul/* 234736270Swpaul * Set media options. 234836270Swpaul */ 234936270Swpaulstatic int tl_ifmedia_upd(ifp) 235036270Swpaul struct ifnet *ifp; 235136270Swpaul{ 235236270Swpaul struct tl_softc *sc; 235336270Swpaul struct tl_csr *csr; 235436270Swpaul struct ifmedia *ifm; 235536270Swpaul 235636270Swpaul sc = ifp->if_softc; 235736270Swpaul csr = sc->csr; 235836270Swpaul ifm = &sc->ifmedia; 235936270Swpaul 236036270Swpaul if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 236136270Swpaul return(EINVAL); 236236270Swpaul 236336270Swpaul if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) 236436270Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 236536270Swpaul else 236636270Swpaul tl_setmode(sc, ifm->ifm_media); 236736270Swpaul 236836270Swpaul return(0); 236936270Swpaul} 237036270Swpaul 237136270Swpaul/* 237236270Swpaul * Report current media status. 237336270Swpaul */ 237436270Swpaulstatic void tl_ifmedia_sts(ifp, ifmr) 237536270Swpaul struct ifnet *ifp; 237636270Swpaul struct ifmediareq *ifmr; 237736270Swpaul{ 237836270Swpaul u_int16_t phy_ctl; 237936270Swpaul u_int16_t phy_sts; 238036270Swpaul struct tl_softc *sc; 238136270Swpaul struct tl_csr *csr; 238236270Swpaul 238336270Swpaul sc = ifp->if_softc; 238436270Swpaul csr = sc->csr; 238536270Swpaul 238636270Swpaul ifmr->ifm_active = IFM_ETHER; 238736270Swpaul 238836270Swpaul phy_ctl = tl_phy_readreg(sc, PHY_BMCR); 238936270Swpaul phy_sts = tl_phy_readreg(sc, TL_PHY_CTL); 239036270Swpaul 239136270Swpaul if (phy_sts & PHY_CTL_AUISEL) 239236270Swpaul ifmr->ifm_active |= IFM_10_5; 239336270Swpaul 239436270Swpaul if (phy_ctl & PHY_BMCR_LOOPBK) 239536270Swpaul ifmr->ifm_active |= IFM_LOOP; 239636270Swpaul 239736270Swpaul if (phy_ctl & PHY_BMCR_SPEEDSEL) 239836270Swpaul ifmr->ifm_active |= IFM_100_TX; 239936270Swpaul else 240036270Swpaul ifmr->ifm_active |= IFM_10_T; 240136270Swpaul 240236270Swpaul if (phy_ctl & PHY_BMCR_DUPLEX) { 240336270Swpaul ifmr->ifm_active |= IFM_FDX; 240436270Swpaul ifmr->ifm_active &= ~IFM_HDX; 240536270Swpaul } else { 240636270Swpaul ifmr->ifm_active &= ~IFM_FDX; 240736270Swpaul ifmr->ifm_active |= IFM_HDX; 240836270Swpaul } 240936270Swpaul 241036270Swpaul if (phy_ctl & PHY_BMCR_AUTONEGENBL) 241136270Swpaul ifmr->ifm_active |= IFM_AUTO; 241236270Swpaul 241336270Swpaul return; 241436270Swpaul} 241536270Swpaul 241636270Swpaulstatic int tl_ioctl(ifp, command, data) 241736270Swpaul struct ifnet *ifp; 241836270Swpaul int command; 241936270Swpaul caddr_t data; 242036270Swpaul{ 242136270Swpaul struct tl_softc *sc = ifp->if_softc; 242236270Swpaul struct ifreq *ifr = (struct ifreq *) data; 242336270Swpaul int s, error = 0; 242436270Swpaul 242536270Swpaul s = splimp(); 242636270Swpaul 242736270Swpaul switch(command) { 242836270Swpaul case SIOCSIFADDR: 242936270Swpaul case SIOCGIFADDR: 243036270Swpaul case SIOCSIFMTU: 243136270Swpaul error = ether_ioctl(ifp, command, data); 243236270Swpaul break; 243336270Swpaul case SIOCSIFFLAGS: 243436270Swpaul /* 243536270Swpaul * Make sure no more than one PHY is active 243636270Swpaul * at any one time. 243736270Swpaul */ 243836270Swpaul if (ifp->if_flags & IFF_UP) { 243936270Swpaul if (sc->tl_iflist->tl_active_phy != TL_PHYS_IDLE && 244036270Swpaul sc->tl_iflist->tl_active_phy != sc->tl_phy_addr) { 244136270Swpaul error = EINVAL; 244236270Swpaul break; 244336270Swpaul } 244436270Swpaul sc->tl_iflist->tl_active_phy = sc->tl_phy_addr; 244536270Swpaul tl_init(sc); 244636270Swpaul } else { 244736270Swpaul if (ifp->if_flags & IFF_RUNNING) { 244836270Swpaul sc->tl_iflist->tl_active_phy = TL_PHYS_IDLE; 244936270Swpaul tl_stop(sc); 245036270Swpaul } 245136270Swpaul } 245236270Swpaul error = 0; 245336270Swpaul break; 245436270Swpaul case SIOCADDMULTI: 245536270Swpaul case SIOCDELMULTI: 245636270Swpaul tl_setmulti(sc); 245736270Swpaul error = 0; 245836270Swpaul break; 245936270Swpaul case SIOCSIFMEDIA: 246036270Swpaul case SIOCGIFMEDIA: 246136270Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 246236270Swpaul break; 246336270Swpaul default: 246436270Swpaul error = EINVAL; 246536270Swpaul break; 246636270Swpaul } 246736270Swpaul 246836270Swpaul (void)splx(s); 246936270Swpaul 247036270Swpaul return(error); 247136270Swpaul} 247236270Swpaul 247336270Swpaulstatic void tl_watchdog(ifp) 247436270Swpaul struct ifnet *ifp; 247536270Swpaul{ 247636270Swpaul struct tl_softc *sc; 247736270Swpaul u_int16_t bmsr; 247836270Swpaul 247936270Swpaul sc = ifp->if_softc; 248036270Swpaul 248136270Swpaul if (sc->tl_autoneg) { 248236270Swpaul tl_autoneg(sc, TL_FLAG_DELAYTIMEO, 1); 248336270Swpaul return; 248436270Swpaul } 248536270Swpaul 248636270Swpaul /* Check that we're still connected. */ 248736270Swpaul tl_phy_readreg(sc, PHY_BMSR); 248836270Swpaul bmsr = tl_phy_readreg(sc, PHY_BMSR); 248936270Swpaul if (!(bmsr & PHY_BMSR_LINKSTAT)) { 249036270Swpaul printf("tl%d: no carrier\n", sc->tl_unit); 249136270Swpaul tl_autoneg(sc, TL_FLAG_SCHEDDELAY, 1); 249236270Swpaul } else 249336270Swpaul printf("tl%d: device timeout\n", sc->tl_unit); 249436270Swpaul 249536270Swpaul ifp->if_oerrors++; 249636270Swpaul 249736270Swpaul tl_init(sc); 249836270Swpaul 249936270Swpaul return; 250036270Swpaul} 250136270Swpaul 250236270Swpaul/* 250336270Swpaul * Stop the adapter and free any mbufs allocated to the 250436270Swpaul * RX and TX lists. 250536270Swpaul */ 250636270Swpaulstatic void tl_stop(sc) 250736270Swpaul struct tl_softc *sc; 250836270Swpaul{ 250936270Swpaul register int i; 251036270Swpaul struct ifnet *ifp; 251136270Swpaul struct tl_csr *csr; 251236270Swpaul struct tl_mii_frame frame; 251336270Swpaul 251436270Swpaul csr = sc->csr; 251536270Swpaul ifp = &sc->arpcom.ac_if; 251636270Swpaul 251736270Swpaul /* Stop the stats updater. */ 251836270Swpaul untimeout(tl_stats_update, sc, sc->tl_stat_ch); 251936270Swpaul 252036270Swpaul /* Stop the transmitter */ 252136270Swpaul sc->csr->tl_host_cmd &= TL_CMD_RT; 252236270Swpaul sc->csr->tl_host_cmd |= TL_CMD_STOP; 252336270Swpaul 252436270Swpaul /* Stop the receiver */ 252536270Swpaul sc->csr->tl_host_cmd |= TL_CMD_RT; 252636270Swpaul sc->csr->tl_host_cmd |= TL_CMD_STOP; 252736270Swpaul 252836270Swpaul /* 252936270Swpaul * Disable host interrupts. 253036270Swpaul */ 253136270Swpaul sc->csr->tl_host_cmd |= TL_CMD_INTSOFF; 253236270Swpaul 253336270Swpaul /* 253436270Swpaul * Disable PHY interrupts. 253536270Swpaul */ 253636270Swpaul bzero((char *)&frame, sizeof(frame)); 253736270Swpaul 253836270Swpaul frame.mii_phyaddr = sc->tl_phy_addr; 253936270Swpaul frame.mii_regaddr = TL_PHY_CTL; 254036270Swpaul tl_mii_readreg(csr, &frame); 254136270Swpaul frame.mii_data |= PHY_CTL_INTEN; 254236270Swpaul tl_mii_writereg(csr, &frame); 254336270Swpaul 254436270Swpaul /* 254536270Swpaul * Disable MII interrupts. 254636270Swpaul */ 254736270Swpaul DIO_SEL(TL_NETSIO); 254836270Swpaul DIO_BYTE1_CLR(TL_SIO_MINTEN); 254936270Swpaul 255036270Swpaul /* 255136270Swpaul * Clear list pointer. 255236270Swpaul */ 255336270Swpaul sc->csr->tl_ch_parm = 0; 255436270Swpaul 255536270Swpaul /* 255636270Swpaul * Free the RX lists. 255736270Swpaul */ 255836270Swpaul for (i = 0; i < TL_RX_LIST_CNT; i++) { 255936270Swpaul if (sc->tl_cdata.tl_rx_chain[i].tl_mbuf != NULL) { 256036270Swpaul m_freem(sc->tl_cdata.tl_rx_chain[i].tl_mbuf); 256136270Swpaul sc->tl_cdata.tl_rx_chain[i].tl_mbuf = NULL; 256236270Swpaul } 256336270Swpaul } 256436270Swpaul bzero((char *)&sc->tl_ldata->tl_rx_list, 256536270Swpaul sizeof(sc->tl_ldata->tl_rx_list)); 256636270Swpaul 256736270Swpaul /* 256836270Swpaul * Free the TX list buffers. 256936270Swpaul */ 257036270Swpaul for (i = 0; i < TL_TX_LIST_CNT; i++) { 257136270Swpaul if (sc->tl_cdata.tl_tx_chain[i].tl_mbuf != NULL) { 257236270Swpaul m_freem(sc->tl_cdata.tl_tx_chain[i].tl_mbuf); 257336270Swpaul sc->tl_cdata.tl_tx_chain[i].tl_mbuf = NULL; 257436270Swpaul } 257536270Swpaul } 257636270Swpaul bzero((char *)&sc->tl_ldata->tl_tx_list, 257736270Swpaul sizeof(sc->tl_ldata->tl_tx_list)); 257836270Swpaul 257936270Swpaul sc->tl_iflist->tl_active_phy = TL_PHYS_IDLE; 258036270Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 258136270Swpaul 258236270Swpaul return; 258336270Swpaul} 258436270Swpaul 258536270Swpaul/* 258636270Swpaul * Stop all chip I/O so that the kernel's probe routines don't 258736270Swpaul * get confused by errant DMAs when rebooting. 258836270Swpaul */ 258936270Swpaulstatic void tl_shutdown(howto, xilist) 259036270Swpaul int howto; 259136270Swpaul void *xilist; 259236270Swpaul{ 259336270Swpaul struct tl_iflist *ilist = (struct tl_iflist *)xilist; 259436270Swpaul struct tl_csr *csr = ilist->csr; 259536270Swpaul struct tl_mii_frame frame; 259636270Swpaul int i; 259736270Swpaul 259836270Swpaul /* Stop the transmitter */ 259936270Swpaul csr->tl_host_cmd &= TL_CMD_RT; 260036270Swpaul csr->tl_host_cmd |= TL_CMD_STOP; 260136270Swpaul 260236270Swpaul /* Stop the receiver */ 260336270Swpaul csr->tl_host_cmd |= TL_CMD_RT; 260436270Swpaul csr->tl_host_cmd |= TL_CMD_STOP; 260536270Swpaul 260636270Swpaul /* 260736270Swpaul * Disable host interrupts. 260836270Swpaul */ 260936270Swpaul csr->tl_host_cmd |= TL_CMD_INTSOFF; 261036270Swpaul 261136270Swpaul /* 261236270Swpaul * Disable PHY interrupts. 261336270Swpaul */ 261436270Swpaul bzero((char *)&frame, sizeof(frame)); 261536270Swpaul 261636270Swpaul for (i = TL_PHYADDR_MIN; i < TL_PHYADDR_MAX + 1; i++) { 261736270Swpaul frame.mii_phyaddr = i; 261836270Swpaul frame.mii_regaddr = TL_PHY_CTL; 261936270Swpaul tl_mii_readreg(csr, &frame); 262036270Swpaul frame.mii_data |= PHY_CTL_INTEN; 262136270Swpaul tl_mii_writereg(csr, &frame); 262236270Swpaul }; 262336270Swpaul 262436270Swpaul /* 262536270Swpaul * Disable MII interrupts. 262636270Swpaul */ 262736270Swpaul DIO_SEL(TL_NETSIO); 262836270Swpaul DIO_BYTE1_CLR(TL_SIO_MINTEN); 262936270Swpaul 263036270Swpaul return; 263136270Swpaul} 263236270Swpaul 263336270Swpaul 263436270Swpaulstatic struct pci_device tlc_device = { 263536270Swpaul "tlc", 263636270Swpaul tl_probe, 263736270Swpaul tl_attach_ctlr, 263836270Swpaul &tl_count, 263936270Swpaul NULL 264036270Swpaul}; 264136270SwpaulDATA_SET(pcidevice_set, tlc_device); 2642