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