xref: /freebsd/sys/dev/xl/if_xl.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
183825b71SWarner Losh /*-
2df57947fSPedro F. Giffuni  * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni  *
483825b71SWarner Losh  * Copyright (c) 1997, 1998, 1999
583825b71SWarner Losh  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
683825b71SWarner Losh  *
783825b71SWarner Losh  * Redistribution and use in source and binary forms, with or without
883825b71SWarner Losh  * modification, are permitted provided that the following conditions
983825b71SWarner Losh  * are met:
1083825b71SWarner Losh  * 1. Redistributions of source code must retain the above copyright
1183825b71SWarner Losh  *    notice, this list of conditions and the following disclaimer.
1283825b71SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
1383825b71SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
1483825b71SWarner Losh  *    documentation and/or other materials provided with the distribution.
1583825b71SWarner Losh  * 3. All advertising materials mentioning features or use of this software
1683825b71SWarner Losh  *    must display the following acknowledgement:
1783825b71SWarner Losh  *	This product includes software developed by Bill Paul.
1883825b71SWarner Losh  * 4. Neither the name of the author nor the names of any co-contributors
1983825b71SWarner Losh  *    may be used to endorse or promote products derived from this software
2083825b71SWarner Losh  *    without specific prior written permission.
2183825b71SWarner Losh  *
2283825b71SWarner Losh  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2383825b71SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2483825b71SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2583825b71SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2683825b71SWarner Losh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2783825b71SWarner Losh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2883825b71SWarner Losh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2983825b71SWarner Losh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3083825b71SWarner Losh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3183825b71SWarner Losh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3283825b71SWarner Losh  * THE POSSIBILITY OF SUCH DAMAGE.
3383825b71SWarner Losh  */
3483825b71SWarner Losh 
3583825b71SWarner Losh #include <sys/cdefs.h>
3683825b71SWarner Losh /*
3783825b71SWarner Losh  * 3Com 3c90x Etherlink XL PCI NIC driver
3883825b71SWarner Losh  *
3983825b71SWarner Losh  * Supports the 3Com "boomerang", "cyclone" and "hurricane" PCI
4083825b71SWarner Losh  * bus-master chips (3c90x cards and embedded controllers) including
4183825b71SWarner Losh  * the following:
4283825b71SWarner Losh  *
4383825b71SWarner Losh  * 3Com 3c900-TPO	10Mbps/RJ-45
4483825b71SWarner Losh  * 3Com 3c900-COMBO	10Mbps/RJ-45,AUI,BNC
4583825b71SWarner Losh  * 3Com 3c905-TX	10/100Mbps/RJ-45
4683825b71SWarner Losh  * 3Com 3c905-T4	10/100Mbps/RJ-45
4783825b71SWarner Losh  * 3Com 3c900B-TPO	10Mbps/RJ-45
4883825b71SWarner Losh  * 3Com 3c900B-COMBO	10Mbps/RJ-45,AUI,BNC
4983825b71SWarner Losh  * 3Com 3c900B-TPC	10Mbps/RJ-45,BNC
5083825b71SWarner Losh  * 3Com 3c900B-FL	10Mbps/Fiber-optic
5183825b71SWarner Losh  * 3Com 3c905B-COMBO	10/100Mbps/RJ-45,AUI,BNC
5283825b71SWarner Losh  * 3Com 3c905B-TX	10/100Mbps/RJ-45
5383825b71SWarner Losh  * 3Com 3c905B-FL/FX	10/100Mbps/Fiber-optic
5483825b71SWarner Losh  * 3Com 3c905C-TX	10/100Mbps/RJ-45 (Tornado ASIC)
5583825b71SWarner Losh  * 3Com 3c980-TX	10/100Mbps server adapter (Hurricane ASIC)
5683825b71SWarner Losh  * 3Com 3c980C-TX	10/100Mbps server adapter (Tornado ASIC)
5783825b71SWarner Losh  * 3Com 3cSOHO100-TX	10/100Mbps/RJ-45 (Hurricane ASIC)
5883825b71SWarner Losh  * 3Com 3c450-TX	10/100Mbps/RJ-45 (Tornado ASIC)
5983825b71SWarner Losh  * 3Com 3c555		10/100Mbps/RJ-45 (MiniPCI, Laptop Hurricane)
6083825b71SWarner Losh  * 3Com 3c556		10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC)
6183825b71SWarner Losh  * 3Com 3c556B		10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC)
6283825b71SWarner Losh  * 3Com 3c575TX		10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
6383825b71SWarner Losh  * 3Com 3c575B		10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
6483825b71SWarner Losh  * 3Com 3c575C		10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
6583825b71SWarner Losh  * 3Com 3cxfem656	10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
6683825b71SWarner Losh  * 3Com 3cxfem656b	10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
6783825b71SWarner Losh  * 3Com 3cxfem656c	10/100Mbps/RJ-45 (Cardbus, Tornado ASIC)
6883825b71SWarner Losh  * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45
6983825b71SWarner Losh  * Dell on-board 3c920 10/100Mbps/RJ-45
7083825b71SWarner Losh  * Dell Precision on-board 3c905B 10/100Mbps/RJ-45
7183825b71SWarner Losh  * Dell Latitude laptop docking station embedded 3c905-TX
7283825b71SWarner Losh  *
7383825b71SWarner Losh  * Written by Bill Paul <wpaul@ctr.columbia.edu>
7483825b71SWarner Losh  * Electrical Engineering Department
7583825b71SWarner Losh  * Columbia University, New York City
7683825b71SWarner Losh  */
7783825b71SWarner Losh /*
78453130d9SPedro F. Giffuni  * The 3c90x series chips use a bus-master DMA interface for transferring
7983825b71SWarner Losh  * packets to and from the controller chip. Some of the "vortex" cards
8083825b71SWarner Losh  * (3c59x) also supported a bus master mode, however for those chips
8183825b71SWarner Losh  * you could only DMA packets to/from a contiguous memory buffer. For
8283825b71SWarner Losh  * transmission this would mean copying the contents of the queued mbuf
8383825b71SWarner Losh  * chain into an mbuf cluster and then DMAing the cluster. This extra
8483825b71SWarner Losh  * copy would sort of defeat the purpose of the bus master support for
8583825b71SWarner Losh  * any packet that doesn't fit into a single mbuf.
8683825b71SWarner Losh  *
8783825b71SWarner Losh  * By contrast, the 3c90x cards support a fragment-based bus master
8883825b71SWarner Losh  * mode where mbuf chains can be encapsulated using TX descriptors.
8983825b71SWarner Losh  * This is similar to other PCI chips such as the Texas Instruments
9083825b71SWarner Losh  * ThunderLAN and the Intel 82557/82558.
9183825b71SWarner Losh  *
9283825b71SWarner Losh  * The "vortex" driver (if_vx.c) happens to work for the "boomerang"
9383825b71SWarner Losh  * bus master chips because they maintain the old PIO interface for
9483825b71SWarner Losh  * backwards compatibility, but starting with the 3c905B and the
9583825b71SWarner Losh  * "cyclone" chips, the compatibility interface has been dropped.
9683825b71SWarner Losh  * Since using bus master DMA is a big win, we use this driver to
9783825b71SWarner Losh  * support the PCI "boomerang" chips even though they work with the
9883825b71SWarner Losh  * "vortex" driver in order to obtain better performance.
9983825b71SWarner Losh  */
10083825b71SWarner Losh 
10183825b71SWarner Losh #ifdef HAVE_KERNEL_OPTION_HEADERS
10283825b71SWarner Losh #include "opt_device_polling.h"
10383825b71SWarner Losh #endif
10483825b71SWarner Losh 
10583825b71SWarner Losh #include <sys/param.h>
10683825b71SWarner Losh #include <sys/systm.h>
10783825b71SWarner Losh #include <sys/sockio.h>
10883825b71SWarner Losh #include <sys/endian.h>
10983825b71SWarner Losh #include <sys/kernel.h>
1108ec07310SGleb Smirnoff #include <sys/malloc.h>
1118ec07310SGleb Smirnoff #include <sys/mbuf.h>
11283825b71SWarner Losh #include <sys/module.h>
11383825b71SWarner Losh #include <sys/socket.h>
11483825b71SWarner Losh #include <sys/taskqueue.h>
11583825b71SWarner Losh 
11683825b71SWarner Losh #include <net/if.h>
11776039bc8SGleb Smirnoff #include <net/if_var.h>
11883825b71SWarner Losh #include <net/if_arp.h>
11983825b71SWarner Losh #include <net/ethernet.h>
12083825b71SWarner Losh #include <net/if_dl.h>
12183825b71SWarner Losh #include <net/if_media.h>
12283825b71SWarner Losh #include <net/if_types.h>
12383825b71SWarner Losh 
12483825b71SWarner Losh #include <net/bpf.h>
12583825b71SWarner Losh 
12683825b71SWarner Losh #include <machine/bus.h>
12783825b71SWarner Losh #include <machine/resource.h>
12883825b71SWarner Losh #include <sys/bus.h>
12983825b71SWarner Losh #include <sys/rman.h>
13083825b71SWarner Losh 
13183825b71SWarner Losh #include <dev/mii/mii.h>
1328c1093fcSMarius Strobl #include <dev/mii/mii_bitbang.h>
13383825b71SWarner Losh #include <dev/mii/miivar.h>
13483825b71SWarner Losh 
13583825b71SWarner Losh #include <dev/pci/pcireg.h>
13683825b71SWarner Losh #include <dev/pci/pcivar.h>
13783825b71SWarner Losh 
13883825b71SWarner Losh MODULE_DEPEND(xl, pci, 1, 1, 1);
13983825b71SWarner Losh MODULE_DEPEND(xl, ether, 1, 1, 1);
14083825b71SWarner Losh MODULE_DEPEND(xl, miibus, 1, 1, 1);
14183825b71SWarner Losh 
14283825b71SWarner Losh /* "device miibus" required.  See GENERIC if you get errors here. */
14383825b71SWarner Losh #include "miibus_if.h"
14483825b71SWarner Losh 
14583825b71SWarner Losh #include <dev/xl/if_xlreg.h>
14683825b71SWarner Losh 
14783825b71SWarner Losh /*
14883825b71SWarner Losh  * TX Checksumming is disabled by default for two reasons:
14983825b71SWarner Losh  * - TX Checksumming will occasionally produce corrupt packets
15083825b71SWarner Losh  * - TX Checksumming seems to reduce performance
15183825b71SWarner Losh  *
15283825b71SWarner Losh  * Only 905B/C cards were reported to have this problem, it is possible
15383825b71SWarner Losh  * that later chips _may_ be immune.
15483825b71SWarner Losh  */
15583825b71SWarner Losh #define	XL905B_TXCSUM_BROKEN	1
15683825b71SWarner Losh 
15783825b71SWarner Losh #ifdef XL905B_TXCSUM_BROKEN
15883825b71SWarner Losh #define XL905B_CSUM_FEATURES	0
15983825b71SWarner Losh #else
16083825b71SWarner Losh #define XL905B_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
16183825b71SWarner Losh #endif
16283825b71SWarner Losh 
16383825b71SWarner Losh /*
16483825b71SWarner Losh  * Various supported device vendors/types and their names.
16583825b71SWarner Losh  */
16629658c96SDimitry Andric static const struct xl_type xl_devs[] = {
16783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT,
16883825b71SWarner Losh 		"3Com 3c900-TPO Etherlink XL" },
16983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO,
17083825b71SWarner Losh 		"3Com 3c900-COMBO Etherlink XL" },
17183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT,
17283825b71SWarner Losh 		"3Com 3c905-TX Fast Etherlink XL" },
17383825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4,
17483825b71SWarner Losh 		"3Com 3c905-T4 Fast Etherlink XL" },
17583825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT,
17683825b71SWarner Losh 		"3Com 3c900B-TPO Etherlink XL" },
17783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO,
17883825b71SWarner Losh 		"3Com 3c900B-COMBO Etherlink XL" },
17983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC,
18083825b71SWarner Losh 		"3Com 3c900B-TPC Etherlink XL" },
18183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10FL,
18283825b71SWarner Losh 		"3Com 3c900B-FL Etherlink XL" },
18383825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT,
18483825b71SWarner Losh 		"3Com 3c905B-TX Fast Etherlink XL" },
18583825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4,
18683825b71SWarner Losh 		"3Com 3c905B-T4 Fast Etherlink XL" },
18783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX,
18883825b71SWarner Losh 		"3Com 3c905B-FX/SC Fast Etherlink XL" },
18983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO,
19083825b71SWarner Losh 		"3Com 3c905B-COMBO Fast Etherlink XL" },
19183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT,
19283825b71SWarner Losh 		"3Com 3c905C-TX Fast Etherlink XL" },
19383825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B,
19483825b71SWarner Losh 		"3Com 3c920B-EMB Integrated Fast Etherlink XL" },
19583825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B_WNM,
19683825b71SWarner Losh 		"3Com 3c920B-EMB-WNM Integrated Fast Etherlink XL" },
19783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV,
19883825b71SWarner Losh 		"3Com 3c980 Fast Etherlink XL" },
19983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV,
20083825b71SWarner Losh 		"3Com 3c980C Fast Etherlink XL" },
20183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX,
20283825b71SWarner Losh 		"3Com 3cSOHO100-TX OfficeConnect" },
20383825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT,
20483825b71SWarner Losh 		"3Com 3c450-TX HomeConnect" },
20583825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_555,
20683825b71SWarner Losh 		"3Com 3c555 Fast Etherlink XL" },
20783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_556,
20883825b71SWarner Losh 		"3Com 3c556 Fast Etherlink XL" },
20983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_556B,
21083825b71SWarner Losh 		"3Com 3c556B Fast Etherlink XL" },
21183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575A,
21283825b71SWarner Losh 		"3Com 3c575TX Fast Etherlink XL" },
21383825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575B,
21483825b71SWarner Losh 		"3Com 3c575B Fast Etherlink XL" },
21583825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575C,
21683825b71SWarner Losh 		"3Com 3c575C Fast Etherlink XL" },
21783825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_656,
21883825b71SWarner Losh 		"3Com 3c656 Fast Etherlink XL" },
21983825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_656B,
22083825b71SWarner Losh 		"3Com 3c656B Fast Etherlink XL" },
22183825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_656C,
22283825b71SWarner Losh 		"3Com 3c656C Fast Etherlink XL" },
22383825b71SWarner Losh 	{ 0, 0, NULL }
22483825b71SWarner Losh };
22583825b71SWarner Losh 
22683825b71SWarner Losh static int xl_probe(device_t);
22783825b71SWarner Losh static int xl_attach(device_t);
22883825b71SWarner Losh static int xl_detach(device_t);
22983825b71SWarner Losh 
23083825b71SWarner Losh static int xl_newbuf(struct xl_softc *, struct xl_chain_onefrag *);
23148dcbc33SPyun YongHyeon static void xl_tick(void *);
23248dcbc33SPyun YongHyeon static void xl_stats_update(struct xl_softc *);
23383825b71SWarner Losh static int xl_encap(struct xl_softc *, struct xl_chain *, struct mbuf **);
2341abcdbd1SAttilio Rao static int xl_rxeof(struct xl_softc *);
23583825b71SWarner Losh static void xl_rxeof_task(void *, int);
23683825b71SWarner Losh static int xl_rx_resync(struct xl_softc *);
23783825b71SWarner Losh static void xl_txeof(struct xl_softc *);
23883825b71SWarner Losh static void xl_txeof_90xB(struct xl_softc *);
23983825b71SWarner Losh static void xl_txeoc(struct xl_softc *);
24083825b71SWarner Losh static void xl_intr(void *);
241*759ad4ddSJustin Hibbits static void xl_start(if_t);
242*759ad4ddSJustin Hibbits static void xl_start_locked(if_t);
243*759ad4ddSJustin Hibbits static void xl_start_90xB_locked(if_t);
244*759ad4ddSJustin Hibbits static int xl_ioctl(if_t, u_long, caddr_t);
24583825b71SWarner Losh static void xl_init(void *);
24683825b71SWarner Losh static void xl_init_locked(struct xl_softc *);
24783825b71SWarner Losh static void xl_stop(struct xl_softc *);
24883825b71SWarner Losh static int xl_watchdog(struct xl_softc *);
24983825b71SWarner Losh static int xl_shutdown(device_t);
25083825b71SWarner Losh static int xl_suspend(device_t);
25183825b71SWarner Losh static int xl_resume(device_t);
2529ae11bbaSPyun YongHyeon static void xl_setwol(struct xl_softc *);
25383825b71SWarner Losh 
25483825b71SWarner Losh #ifdef DEVICE_POLLING
255*759ad4ddSJustin Hibbits static int xl_poll(if_t ifp, enum poll_cmd cmd, int count);
256*759ad4ddSJustin Hibbits static int xl_poll_locked(if_t ifp, enum poll_cmd cmd, int count);
25783825b71SWarner Losh #endif
25883825b71SWarner Losh 
259*759ad4ddSJustin Hibbits static int xl_ifmedia_upd(if_t);
260*759ad4ddSJustin Hibbits static void xl_ifmedia_sts(if_t, struct ifmediareq *);
26183825b71SWarner Losh 
26283825b71SWarner Losh static int xl_eeprom_wait(struct xl_softc *);
26383825b71SWarner Losh static int xl_read_eeprom(struct xl_softc *, caddr_t, int, int, int);
26483825b71SWarner Losh 
265dd0a7688SPyun YongHyeon static void xl_rxfilter(struct xl_softc *);
266dd0a7688SPyun YongHyeon static void xl_rxfilter_90x(struct xl_softc *);
267dd0a7688SPyun YongHyeon static void xl_rxfilter_90xB(struct xl_softc *);
26883825b71SWarner Losh static void xl_setcfg(struct xl_softc *);
26983825b71SWarner Losh static void xl_setmode(struct xl_softc *, int);
27083825b71SWarner Losh static void xl_reset(struct xl_softc *);
27183825b71SWarner Losh static int xl_list_rx_init(struct xl_softc *);
27283825b71SWarner Losh static int xl_list_tx_init(struct xl_softc *);
27383825b71SWarner Losh static int xl_list_tx_init_90xB(struct xl_softc *);
27483825b71SWarner Losh static void xl_wait(struct xl_softc *);
27583825b71SWarner Losh static void xl_mediacheck(struct xl_softc *);
27683825b71SWarner Losh static void xl_choose_media(struct xl_softc *sc, int *media);
27783825b71SWarner Losh static void xl_choose_xcvr(struct xl_softc *, int);
27883825b71SWarner Losh static void xl_dma_map_addr(void *, bus_dma_segment_t *, int, int);
27983825b71SWarner Losh #ifdef notdef
28083825b71SWarner Losh static void xl_testpacket(struct xl_softc *);
28183825b71SWarner Losh #endif
28283825b71SWarner Losh 
28383825b71SWarner Losh static int xl_miibus_readreg(device_t, int, int);
28483825b71SWarner Losh static int xl_miibus_writereg(device_t, int, int, int);
28583825b71SWarner Losh static void xl_miibus_statchg(device_t);
28683825b71SWarner Losh static void xl_miibus_mediainit(device_t);
28783825b71SWarner Losh 
2888c1093fcSMarius Strobl /*
2898c1093fcSMarius Strobl  * MII bit-bang glue
2908c1093fcSMarius Strobl  */
2918c1093fcSMarius Strobl static uint32_t xl_mii_bitbang_read(device_t);
2928c1093fcSMarius Strobl static void xl_mii_bitbang_write(device_t, uint32_t);
2938c1093fcSMarius Strobl 
2948c1093fcSMarius Strobl static const struct mii_bitbang_ops xl_mii_bitbang_ops = {
2958c1093fcSMarius Strobl 	xl_mii_bitbang_read,
2968c1093fcSMarius Strobl 	xl_mii_bitbang_write,
2978c1093fcSMarius Strobl 	{
2988c1093fcSMarius Strobl 		XL_MII_DATA,		/* MII_BIT_MDO */
2998c1093fcSMarius Strobl 		XL_MII_DATA,		/* MII_BIT_MDI */
3008c1093fcSMarius Strobl 		XL_MII_CLK,		/* MII_BIT_MDC */
3018c1093fcSMarius Strobl 		XL_MII_DIR,		/* MII_BIT_DIR_HOST_PHY */
3028c1093fcSMarius Strobl 		0,			/* MII_BIT_DIR_PHY_HOST */
3038c1093fcSMarius Strobl 	}
3048c1093fcSMarius Strobl };
3058c1093fcSMarius Strobl 
30683825b71SWarner Losh static device_method_t xl_methods[] = {
30783825b71SWarner Losh 	/* Device interface */
30883825b71SWarner Losh 	DEVMETHOD(device_probe,		xl_probe),
30983825b71SWarner Losh 	DEVMETHOD(device_attach,	xl_attach),
31083825b71SWarner Losh 	DEVMETHOD(device_detach,	xl_detach),
31183825b71SWarner Losh 	DEVMETHOD(device_shutdown,	xl_shutdown),
31283825b71SWarner Losh 	DEVMETHOD(device_suspend,	xl_suspend),
31383825b71SWarner Losh 	DEVMETHOD(device_resume,	xl_resume),
31483825b71SWarner Losh 
31583825b71SWarner Losh 	/* MII interface */
31683825b71SWarner Losh 	DEVMETHOD(miibus_readreg,	xl_miibus_readreg),
31783825b71SWarner Losh 	DEVMETHOD(miibus_writereg,	xl_miibus_writereg),
31883825b71SWarner Losh 	DEVMETHOD(miibus_statchg,	xl_miibus_statchg),
31983825b71SWarner Losh 	DEVMETHOD(miibus_mediainit,	xl_miibus_mediainit),
32083825b71SWarner Losh 
3214b7ec270SMarius Strobl 	DEVMETHOD_END
32283825b71SWarner Losh };
32383825b71SWarner Losh 
32483825b71SWarner Losh static driver_t xl_driver = {
32583825b71SWarner Losh 	"xl",
32683825b71SWarner Losh 	xl_methods,
32783825b71SWarner Losh 	sizeof(struct xl_softc)
32883825b71SWarner Losh };
32983825b71SWarner Losh 
330ad9db232SJohn Baldwin DRIVER_MODULE_ORDERED(xl, pci, xl_driver, NULL, NULL, SI_ORDER_ANY);
3313e38757dSJohn Baldwin DRIVER_MODULE(miibus, xl, miibus_driver, NULL, NULL);
332329e817fSWarner Losh MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, xl, xl_devs,
333f0df5e27SWarner Losh     nitems(xl_devs) - 1);
33483825b71SWarner Losh 
33583825b71SWarner Losh static void
xl_dma_map_addr(void * arg,bus_dma_segment_t * segs,int nseg,int error)33683825b71SWarner Losh xl_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
33783825b71SWarner Losh {
33883825b71SWarner Losh 	u_int32_t *paddr;
33983825b71SWarner Losh 
34083825b71SWarner Losh 	paddr = arg;
34183825b71SWarner Losh 	*paddr = segs->ds_addr;
34283825b71SWarner Losh }
34383825b71SWarner Losh 
34483825b71SWarner Losh /*
34583825b71SWarner Losh  * Murphy's law says that it's possible the chip can wedge and
34683825b71SWarner Losh  * the 'command in progress' bit may never clear. Hence, we wait
34783825b71SWarner Losh  * only a finite amount of time to avoid getting caught in an
34883825b71SWarner Losh  * infinite loop. Normally this delay routine would be a macro,
34983825b71SWarner Losh  * but it isn't called during normal operation so we can afford
3503cb58987SWarner Losh  * to make it a function.  Suppress warning when card gone.
35183825b71SWarner Losh  */
35283825b71SWarner Losh static void
xl_wait(struct xl_softc * sc)35383825b71SWarner Losh xl_wait(struct xl_softc *sc)
35483825b71SWarner Losh {
3553e85b721SEd Maste 	int			i;
35683825b71SWarner Losh 
35783825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
35883825b71SWarner Losh 		if ((CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY) == 0)
35983825b71SWarner Losh 			break;
36083825b71SWarner Losh 	}
36183825b71SWarner Losh 
362aa0ea4afSWarner Losh 	if (i == XL_TIMEOUT && bus_child_present(sc->xl_dev))
36383825b71SWarner Losh 		device_printf(sc->xl_dev, "command never completed!\n");
36483825b71SWarner Losh }
36583825b71SWarner Losh 
36683825b71SWarner Losh /*
36783825b71SWarner Losh  * MII access routines are provided for adapters with external
36883825b71SWarner Losh  * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
36983825b71SWarner Losh  * autoneg logic that's faked up to look like a PHY (3c905B-TX).
37083825b71SWarner Losh  * Note: if you don't perform the MDIO operations just right,
37183825b71SWarner Losh  * it's possible to end up with code that works correctly with
37283825b71SWarner Losh  * some chips/CPUs/processor speeds/bus speeds/etc but not
37383825b71SWarner Losh  * with others.
37483825b71SWarner Losh  */
37583825b71SWarner Losh 
37683825b71SWarner Losh /*
3778c1093fcSMarius Strobl  * Read the MII serial port for the MII bit-bang module.
3788c1093fcSMarius Strobl  */
3798c1093fcSMarius Strobl static uint32_t
xl_mii_bitbang_read(device_t dev)3808c1093fcSMarius Strobl xl_mii_bitbang_read(device_t dev)
3818c1093fcSMarius Strobl {
3828c1093fcSMarius Strobl 	struct xl_softc		*sc;
3838c1093fcSMarius Strobl 	uint32_t		val;
3848c1093fcSMarius Strobl 
3858c1093fcSMarius Strobl 	sc = device_get_softc(dev);
3868c1093fcSMarius Strobl 
3878c1093fcSMarius Strobl 	/* We're already in window 4. */
3888c1093fcSMarius Strobl 	val = CSR_READ_2(sc, XL_W4_PHY_MGMT);
3898c1093fcSMarius Strobl 	CSR_BARRIER(sc, XL_W4_PHY_MGMT, 2,
3908c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
3918c1093fcSMarius Strobl 
3928c1093fcSMarius Strobl 	return (val);
3938c1093fcSMarius Strobl }
3948c1093fcSMarius Strobl 
3958c1093fcSMarius Strobl /*
3968c1093fcSMarius Strobl  * Write the MII serial port for the MII bit-bang module.
39783825b71SWarner Losh  */
39883825b71SWarner Losh static void
xl_mii_bitbang_write(device_t dev,uint32_t val)3998c1093fcSMarius Strobl xl_mii_bitbang_write(device_t dev, uint32_t val)
40083825b71SWarner Losh {
4018c1093fcSMarius Strobl 	struct xl_softc		*sc;
40283825b71SWarner Losh 
4038c1093fcSMarius Strobl 	sc = device_get_softc(dev);
40483825b71SWarner Losh 
4058c1093fcSMarius Strobl 	/* We're already in window 4. */
4068c1093fcSMarius Strobl 	CSR_WRITE_2(sc, XL_W4_PHY_MGMT,	val);
4078c1093fcSMarius Strobl 	CSR_BARRIER(sc, XL_W4_PHY_MGMT, 2,
4088c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
40983825b71SWarner Losh }
41083825b71SWarner Losh 
41183825b71SWarner Losh static int
xl_miibus_readreg(device_t dev,int phy,int reg)41283825b71SWarner Losh xl_miibus_readreg(device_t dev, int phy, int reg)
41383825b71SWarner Losh {
41483825b71SWarner Losh 	struct xl_softc		*sc;
41583825b71SWarner Losh 
41683825b71SWarner Losh 	sc = device_get_softc(dev);
41783825b71SWarner Losh 
4188c1093fcSMarius Strobl 	/* Select the window 4. */
4198c1093fcSMarius Strobl 	XL_SEL_WIN(4);
42083825b71SWarner Losh 
4218c1093fcSMarius Strobl 	return (mii_bitbang_readreg(dev, &xl_mii_bitbang_ops, phy, reg));
42283825b71SWarner Losh }
42383825b71SWarner Losh 
42483825b71SWarner Losh static int
xl_miibus_writereg(device_t dev,int phy,int reg,int data)42583825b71SWarner Losh xl_miibus_writereg(device_t dev, int phy, int reg, int data)
42683825b71SWarner Losh {
42783825b71SWarner Losh 	struct xl_softc		*sc;
42883825b71SWarner Losh 
42983825b71SWarner Losh 	sc = device_get_softc(dev);
43083825b71SWarner Losh 
4318c1093fcSMarius Strobl 	/* Select the window 4. */
4328c1093fcSMarius Strobl 	XL_SEL_WIN(4);
43383825b71SWarner Losh 
4348c1093fcSMarius Strobl 	mii_bitbang_writereg(dev, &xl_mii_bitbang_ops, phy, reg, data);
43583825b71SWarner Losh 
43683825b71SWarner Losh 	return (0);
43783825b71SWarner Losh }
43883825b71SWarner Losh 
43983825b71SWarner Losh static void
xl_miibus_statchg(device_t dev)44083825b71SWarner Losh xl_miibus_statchg(device_t dev)
44183825b71SWarner Losh {
44283825b71SWarner Losh 	struct xl_softc		*sc;
44383825b71SWarner Losh 	struct mii_data		*mii;
444aee0e786SPyun YongHyeon 	uint8_t			macctl;
44583825b71SWarner Losh 
44683825b71SWarner Losh 	sc = device_get_softc(dev);
44783825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
44883825b71SWarner Losh 
44983825b71SWarner Losh 	xl_setcfg(sc);
45083825b71SWarner Losh 
45183825b71SWarner Losh 	/* Set ASIC's duplex mode to match the PHY. */
45283825b71SWarner Losh 	XL_SEL_WIN(3);
453aee0e786SPyun YongHyeon 	macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
454aee0e786SPyun YongHyeon 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
455aee0e786SPyun YongHyeon 		macctl |= XL_MACCTRL_DUPLEX;
456aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B) {
457aee0e786SPyun YongHyeon 			if ((IFM_OPTIONS(mii->mii_media_active) &
458aee0e786SPyun YongHyeon 			    IFM_ETH_RXPAUSE) != 0)
459aee0e786SPyun YongHyeon 				macctl |= XL_MACCTRL_FLOW_CONTROL_ENB;
46083825b71SWarner Losh 			else
461aee0e786SPyun YongHyeon 				macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
462aee0e786SPyun YongHyeon 		}
463aee0e786SPyun YongHyeon 	} else {
464aee0e786SPyun YongHyeon 		macctl &= ~XL_MACCTRL_DUPLEX;
465aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B)
466aee0e786SPyun YongHyeon 			macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
467aee0e786SPyun YongHyeon 	}
468aee0e786SPyun YongHyeon 	CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
46983825b71SWarner Losh }
47083825b71SWarner Losh 
47183825b71SWarner Losh /*
47283825b71SWarner Losh  * Special support for the 3c905B-COMBO. This card has 10/100 support
47383825b71SWarner Losh  * plus BNC and AUI ports. This means we will have both an miibus attached
47483825b71SWarner Losh  * plus some non-MII media settings. In order to allow this, we have to
47583825b71SWarner Losh  * add the extra media to the miibus's ifmedia struct, but we can't do
47683825b71SWarner Losh  * that during xl_attach() because the miibus hasn't been attached yet.
47783825b71SWarner Losh  * So instead, we wait until the miibus probe/attach is done, at which
47883825b71SWarner Losh  * point we will get a callback telling is that it's safe to add our
47983825b71SWarner Losh  * extra media.
48083825b71SWarner Losh  */
48183825b71SWarner Losh static void
xl_miibus_mediainit(device_t dev)48283825b71SWarner Losh xl_miibus_mediainit(device_t dev)
48383825b71SWarner Losh {
48483825b71SWarner Losh 	struct xl_softc		*sc;
48583825b71SWarner Losh 	struct mii_data		*mii;
48683825b71SWarner Losh 	struct ifmedia		*ifm;
48783825b71SWarner Losh 
48883825b71SWarner Losh 	sc = device_get_softc(dev);
48983825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
49083825b71SWarner Losh 	ifm = &mii->mii_media;
49183825b71SWarner Losh 
49283825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI | XL_MEDIAOPT_10FL)) {
49383825b71SWarner Losh 		/*
49483825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
49583825b71SWarner Losh 		 */
49683825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
49783825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
49883825b71SWarner Losh 			if (bootverbose)
49983825b71SWarner Losh 				device_printf(sc->xl_dev, "found 10baseFL\n");
50083825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL, 0, NULL);
50183825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL|IFM_HDX, 0,
50283825b71SWarner Losh 			    NULL);
50383825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
50483825b71SWarner Losh 				ifmedia_add(ifm,
50583825b71SWarner Losh 				    IFM_ETHER | IFM_10_FL | IFM_FDX, 0, NULL);
50683825b71SWarner Losh 		} else {
50783825b71SWarner Losh 			if (bootverbose)
50883825b71SWarner Losh 				device_printf(sc->xl_dev, "found AUI\n");
50983825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_5, 0, NULL);
51083825b71SWarner Losh 		}
51183825b71SWarner Losh 	}
51283825b71SWarner Losh 
51383825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
51483825b71SWarner Losh 		if (bootverbose)
51583825b71SWarner Losh 			device_printf(sc->xl_dev, "found BNC\n");
51683825b71SWarner Losh 		ifmedia_add(ifm, IFM_ETHER | IFM_10_2, 0, NULL);
51783825b71SWarner Losh 	}
51883825b71SWarner Losh }
51983825b71SWarner Losh 
52083825b71SWarner Losh /*
52183825b71SWarner Losh  * The EEPROM is slow: give it time to come ready after issuing
52283825b71SWarner Losh  * it a command.
52383825b71SWarner Losh  */
52483825b71SWarner Losh static int
xl_eeprom_wait(struct xl_softc * sc)52583825b71SWarner Losh xl_eeprom_wait(struct xl_softc *sc)
52683825b71SWarner Losh {
52783825b71SWarner Losh 	int			i;
52883825b71SWarner Losh 
52983825b71SWarner Losh 	for (i = 0; i < 100; i++) {
53083825b71SWarner Losh 		if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
53183825b71SWarner Losh 			DELAY(162);
53283825b71SWarner Losh 		else
53383825b71SWarner Losh 			break;
53483825b71SWarner Losh 	}
53583825b71SWarner Losh 
53683825b71SWarner Losh 	if (i == 100) {
53783825b71SWarner Losh 		device_printf(sc->xl_dev, "eeprom failed to come ready\n");
53883825b71SWarner Losh 		return (1);
53983825b71SWarner Losh 	}
54083825b71SWarner Losh 
54183825b71SWarner Losh 	return (0);
54283825b71SWarner Losh }
54383825b71SWarner Losh 
54483825b71SWarner Losh /*
54583825b71SWarner Losh  * Read a sequence of words from the EEPROM. Note that ethernet address
54683825b71SWarner Losh  * data is stored in the EEPROM in network byte order.
54783825b71SWarner Losh  */
54883825b71SWarner Losh static int
xl_read_eeprom(struct xl_softc * sc,caddr_t dest,int off,int cnt,int swap)54983825b71SWarner Losh xl_read_eeprom(struct xl_softc *sc, caddr_t dest, int off, int cnt, int swap)
55083825b71SWarner Losh {
55183825b71SWarner Losh 	int			err = 0, i;
55283825b71SWarner Losh 	u_int16_t		word = 0, *ptr;
55383825b71SWarner Losh 
55483825b71SWarner Losh #define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
55583825b71SWarner Losh #define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
55683825b71SWarner Losh 	/*
55783825b71SWarner Losh 	 * XXX: WARNING! DANGER!
55883825b71SWarner Losh 	 * It's easy to accidentally overwrite the rom content!
55983825b71SWarner Losh 	 * Note: the 3c575 uses 8bit EEPROM offsets.
56083825b71SWarner Losh 	 */
56183825b71SWarner Losh 	XL_SEL_WIN(0);
56283825b71SWarner Losh 
56383825b71SWarner Losh 	if (xl_eeprom_wait(sc))
56483825b71SWarner Losh 		return (1);
56583825b71SWarner Losh 
56683825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
56783825b71SWarner Losh 		off += 0x30;
56883825b71SWarner Losh 
56983825b71SWarner Losh 	for (i = 0; i < cnt; i++) {
57083825b71SWarner Losh 		if (sc->xl_flags & XL_FLAG_8BITROM)
57183825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
57283825b71SWarner Losh 			    XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
57383825b71SWarner Losh 		else
57483825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
57583825b71SWarner Losh 			    XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
57683825b71SWarner Losh 		err = xl_eeprom_wait(sc);
57783825b71SWarner Losh 		if (err)
57883825b71SWarner Losh 			break;
57983825b71SWarner Losh 		word = CSR_READ_2(sc, XL_W0_EE_DATA);
58083825b71SWarner Losh 		ptr = (u_int16_t *)(dest + (i * 2));
58183825b71SWarner Losh 		if (swap)
58283825b71SWarner Losh 			*ptr = ntohs(word);
58383825b71SWarner Losh 		else
58483825b71SWarner Losh 			*ptr = word;
58583825b71SWarner Losh 	}
58683825b71SWarner Losh 
58783825b71SWarner Losh 	return (err ? 1 : 0);
58883825b71SWarner Losh }
58983825b71SWarner Losh 
590dd0a7688SPyun YongHyeon static void
xl_rxfilter(struct xl_softc * sc)591dd0a7688SPyun YongHyeon xl_rxfilter(struct xl_softc *sc)
592dd0a7688SPyun YongHyeon {
593dd0a7688SPyun YongHyeon 
594dd0a7688SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B)
595dd0a7688SPyun YongHyeon 		xl_rxfilter_90xB(sc);
596dd0a7688SPyun YongHyeon 	else
597dd0a7688SPyun YongHyeon 		xl_rxfilter_90x(sc);
598dd0a7688SPyun YongHyeon }
599dd0a7688SPyun YongHyeon 
60083825b71SWarner Losh /*
60183825b71SWarner Losh  * NICs older than the 3c905B have only one multicast option, which
60283825b71SWarner Losh  * is to enable reception of all multicast frames.
60383825b71SWarner Losh  */
604380a564dSGleb Smirnoff static u_int
xl_check_maddr_90x(void * arg,struct sockaddr_dl * sdl,u_int cnt)605380a564dSGleb Smirnoff xl_check_maddr_90x(void *arg, struct sockaddr_dl *sdl, u_int cnt)
606380a564dSGleb Smirnoff {
607380a564dSGleb Smirnoff 	uint8_t *rxfilt = arg;
608380a564dSGleb Smirnoff 
609380a564dSGleb Smirnoff 	*rxfilt |= XL_RXFILTER_ALLMULTI;
610380a564dSGleb Smirnoff 
611380a564dSGleb Smirnoff 	return (1);
612380a564dSGleb Smirnoff }
613380a564dSGleb Smirnoff 
61483825b71SWarner Losh static void
xl_rxfilter_90x(struct xl_softc * sc)615dd0a7688SPyun YongHyeon xl_rxfilter_90x(struct xl_softc *sc)
61683825b71SWarner Losh {
617*759ad4ddSJustin Hibbits 	if_t			ifp;
61883825b71SWarner Losh 	u_int8_t		rxfilt;
61983825b71SWarner Losh 
62083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
62183825b71SWarner Losh 
622dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
623dd0a7688SPyun YongHyeon 
62483825b71SWarner Losh 	XL_SEL_WIN(5);
62583825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
626dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
627dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL);
62883825b71SWarner Losh 
629dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
630dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
631dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
632*759ad4ddSJustin Hibbits 	if (if_getflags(ifp) & IFF_BROADCAST)
633dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
634dd0a7688SPyun YongHyeon 
635dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
636*759ad4ddSJustin Hibbits 	if (if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI)) {
637*759ad4ddSJustin Hibbits 		if (if_getflags(ifp) & IFF_PROMISC)
638dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
639*759ad4ddSJustin Hibbits 		if (if_getflags(ifp) & IFF_ALLMULTI)
64083825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
641380a564dSGleb Smirnoff 	} else
642380a564dSGleb Smirnoff 		if_foreach_llmaddr(sc->xl_ifp, xl_check_maddr_90x, &rxfilt);
64383825b71SWarner Losh 
644dd0a7688SPyun YongHyeon 	CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
645dd0a7688SPyun YongHyeon 	XL_SEL_WIN(7);
64683825b71SWarner Losh }
64783825b71SWarner Losh 
64883825b71SWarner Losh /*
64983825b71SWarner Losh  * 3c905B adapters have a hash filter that we can program.
650380a564dSGleb Smirnoff  * Note: the 3c905B currently only supports a 64-bit
651380a564dSGleb Smirnoff  * hash table, which means we really only need 6 bits,
652380a564dSGleb Smirnoff  * but the manual indicates that future chip revisions
653380a564dSGleb Smirnoff  * will have a 256-bit hash table, hence the routine
654380a564dSGleb Smirnoff  * is set up to calculate 8 bits of position info in
655380a564dSGleb Smirnoff  * case we need it some day.
656380a564dSGleb Smirnoff  * Note II, The Sequel: _CURRENT_ versions of the
657380a564dSGleb Smirnoff  * 3c905B have a 256 bit hash table. This means we have
658380a564dSGleb Smirnoff  * to use all 8 bits regardless.  On older cards, the
659380a564dSGleb Smirnoff  * upper 2 bits will be ignored. Grrrr....
66083825b71SWarner Losh  */
661380a564dSGleb Smirnoff static u_int
xl_check_maddr_90xB(void * arg,struct sockaddr_dl * sdl,u_int count)662380a564dSGleb Smirnoff xl_check_maddr_90xB(void *arg, struct sockaddr_dl *sdl, u_int count)
663380a564dSGleb Smirnoff {
664380a564dSGleb Smirnoff 	struct xl_softc *sc = arg;
665380a564dSGleb Smirnoff 	uint16_t h;
666380a564dSGleb Smirnoff 
667380a564dSGleb Smirnoff 	h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) & 0xFF;
668380a564dSGleb Smirnoff 	CSR_WRITE_2(sc, XL_COMMAND, h | XL_CMD_RX_SET_HASH | XL_HASH_SET);
669380a564dSGleb Smirnoff 
670380a564dSGleb Smirnoff 	return (1);
671380a564dSGleb Smirnoff }
672380a564dSGleb Smirnoff 
67383825b71SWarner Losh static void
xl_rxfilter_90xB(struct xl_softc * sc)674dd0a7688SPyun YongHyeon xl_rxfilter_90xB(struct xl_softc *sc)
67583825b71SWarner Losh {
676*759ad4ddSJustin Hibbits 	if_t			ifp;
677380a564dSGleb Smirnoff 	int			i;
67883825b71SWarner Losh 	u_int8_t		rxfilt;
67983825b71SWarner Losh 
68083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
68183825b71SWarner Losh 
682dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
683dd0a7688SPyun YongHyeon 
68483825b71SWarner Losh 	XL_SEL_WIN(5);
68583825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
686dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
687dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL |
688dd0a7688SPyun YongHyeon 	    XL_RXFILTER_MULTIHASH);
68983825b71SWarner Losh 
690dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
691dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
692dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
693*759ad4ddSJustin Hibbits 	if (if_getflags(ifp) & IFF_BROADCAST)
694dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
695dd0a7688SPyun YongHyeon 
696dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
697*759ad4ddSJustin Hibbits 	if (if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI)) {
698*759ad4ddSJustin Hibbits 		if (if_getflags(ifp) & IFF_PROMISC)
699dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
700*759ad4ddSJustin Hibbits 		if (if_getflags(ifp) & IFF_ALLMULTI)
70183825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
702dd0a7688SPyun YongHyeon 	} else {
703dd0a7688SPyun YongHyeon 		/* First, zot all the existing hash bits. */
70483825b71SWarner Losh 		for (i = 0; i < XL_HASHFILT_SIZE; i++)
70583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH | i);
70683825b71SWarner Losh 
707dd0a7688SPyun YongHyeon 		/* Now program new ones. */
708380a564dSGleb Smirnoff 		if (if_foreach_llmaddr(sc->xl_ifp, xl_check_maddr_90xB, sc) > 0)
70983825b71SWarner Losh 			rxfilt |= XL_RXFILTER_MULTIHASH;
710dd0a7688SPyun YongHyeon 	}
71183825b71SWarner Losh 
71283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
713dd0a7688SPyun YongHyeon 	XL_SEL_WIN(7);
71483825b71SWarner Losh }
71583825b71SWarner Losh 
71683825b71SWarner Losh static void
xl_setcfg(struct xl_softc * sc)71783825b71SWarner Losh xl_setcfg(struct xl_softc *sc)
71883825b71SWarner Losh {
71983825b71SWarner Losh 	u_int32_t		icfg;
72083825b71SWarner Losh 
72183825b71SWarner Losh 	/*XL_LOCK_ASSERT(sc);*/
72283825b71SWarner Losh 
72383825b71SWarner Losh 	XL_SEL_WIN(3);
72483825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
72583825b71SWarner Losh 	icfg &= ~XL_ICFG_CONNECTOR_MASK;
72683825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
72783825b71SWarner Losh 		sc->xl_media & XL_MEDIAOPT_BT4)
72883825b71SWarner Losh 		icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
72983825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BTX)
73083825b71SWarner Losh 		icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
73183825b71SWarner Losh 
73283825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
73383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
73483825b71SWarner Losh }
73583825b71SWarner Losh 
73683825b71SWarner Losh static void
xl_setmode(struct xl_softc * sc,int media)73783825b71SWarner Losh xl_setmode(struct xl_softc *sc, int media)
73883825b71SWarner Losh {
73983825b71SWarner Losh 	u_int32_t		icfg;
74083825b71SWarner Losh 	u_int16_t		mediastat;
74183825b71SWarner Losh 	char			*pmsg = "", *dmsg = "";
74283825b71SWarner Losh 
74383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
74483825b71SWarner Losh 
74583825b71SWarner Losh 	XL_SEL_WIN(4);
74683825b71SWarner Losh 	mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
74783825b71SWarner Losh 	XL_SEL_WIN(3);
74883825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
74983825b71SWarner Losh 
75083825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
75183825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_T) {
75283825b71SWarner Losh 			pmsg = "10baseT transceiver";
75383825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_10BT;
75483825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
75583825b71SWarner Losh 			icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
75683825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT |
75783825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD;
75883825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
75983825b71SWarner Losh 		}
76083825b71SWarner Losh 	}
76183825b71SWarner Losh 
76283825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
76383825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_100_FX) {
76483825b71SWarner Losh 			pmsg = "100baseFX port";
76583825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_100BFX;
76683825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
76783825b71SWarner Losh 			icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
76883825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT;
76983825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
77083825b71SWarner Losh 		}
77183825b71SWarner Losh 	}
77283825b71SWarner Losh 
77383825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
77483825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_5) {
77583825b71SWarner Losh 			pmsg = "AUI port";
77683825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
77783825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
77883825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
77983825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
78083825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
78183825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
78283825b71SWarner Losh 		}
78383825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_FL) {
78483825b71SWarner Losh 			pmsg = "10baseFL transceiver";
78583825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
78683825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
78783825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
78883825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
78983825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
79083825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
79183825b71SWarner Losh 		}
79283825b71SWarner Losh 	}
79383825b71SWarner Losh 
79483825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
79583825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_2) {
79683825b71SWarner Losh 			pmsg = "AUI port";
79783825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_COAX;
79883825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
79983825b71SWarner Losh 			icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
80083825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
80183825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD | XL_MEDIASTAT_SQEENB);
80283825b71SWarner Losh 		}
80383825b71SWarner Losh 	}
80483825b71SWarner Losh 
80583825b71SWarner Losh 	if ((media & IFM_GMASK) == IFM_FDX ||
80683825b71SWarner Losh 			IFM_SUBTYPE(media) == IFM_100_FX) {
80783825b71SWarner Losh 		dmsg = "full";
80883825b71SWarner Losh 		XL_SEL_WIN(3);
80983825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
81083825b71SWarner Losh 	} else {
81183825b71SWarner Losh 		dmsg = "half";
81283825b71SWarner Losh 		XL_SEL_WIN(3);
81383825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
81483825b71SWarner Losh 			(CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
81583825b71SWarner Losh 	}
81683825b71SWarner Losh 
81783825b71SWarner Losh 	if (IFM_SUBTYPE(media) == IFM_10_2)
81883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
81983825b71SWarner Losh 	else
82083825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
82183825b71SWarner Losh 
82283825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
82383825b71SWarner Losh 	XL_SEL_WIN(4);
82483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
82583825b71SWarner Losh 
82683825b71SWarner Losh 	DELAY(800);
82783825b71SWarner Losh 	XL_SEL_WIN(7);
82883825b71SWarner Losh 
82983825b71SWarner Losh 	device_printf(sc->xl_dev, "selecting %s, %s duplex\n", pmsg, dmsg);
83083825b71SWarner Losh }
83183825b71SWarner Losh 
83283825b71SWarner Losh static void
xl_reset(struct xl_softc * sc)83383825b71SWarner Losh xl_reset(struct xl_softc *sc)
83483825b71SWarner Losh {
8353e85b721SEd Maste 	int			i;
83683825b71SWarner Losh 
83783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
83883825b71SWarner Losh 
83983825b71SWarner Losh 	XL_SEL_WIN(0);
84083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
84183825b71SWarner Losh 	    ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
84283825b71SWarner Losh 	     XL_RESETOPT_DISADVFD:0));
84383825b71SWarner Losh 
84483825b71SWarner Losh 	/*
84583825b71SWarner Losh 	 * If we're using memory mapped register mode, pause briefly
84683825b71SWarner Losh 	 * after issuing the reset command before trying to access any
847f33a1c16SWarner Losh 	 * other registers. With my 3c575C CardBus card, failing to do
84883825b71SWarner Losh 	 * this results in the system locking up while trying to poll
84983825b71SWarner Losh 	 * the command busy bit in the status register.
85083825b71SWarner Losh 	 */
85183825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO)
85283825b71SWarner Losh 		DELAY(100000);
85383825b71SWarner Losh 
85483825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
85583825b71SWarner Losh 		DELAY(10);
85683825b71SWarner Losh 		if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
85783825b71SWarner Losh 			break;
85883825b71SWarner Losh 	}
85983825b71SWarner Losh 
86083825b71SWarner Losh 	if (i == XL_TIMEOUT)
86183825b71SWarner Losh 		device_printf(sc->xl_dev, "reset didn't complete\n");
86283825b71SWarner Losh 
86383825b71SWarner Losh 	/* Reset TX and RX. */
86483825b71SWarner Losh 	/* Note: the RX reset takes an absurd amount of time
86583825b71SWarner Losh 	 * on newer versions of the Tornado chips such as those
86683825b71SWarner Losh 	 * on the 3c905CX and newer 3c908C cards. We wait an
86783825b71SWarner Losh 	 * extra amount of time so that xl_wait() doesn't complain
86883825b71SWarner Losh 	 * and annoy the users.
86983825b71SWarner Losh 	 */
87083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
87183825b71SWarner Losh 	DELAY(100000);
87283825b71SWarner Losh 	xl_wait(sc);
87383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
87483825b71SWarner Losh 	xl_wait(sc);
87583825b71SWarner Losh 
87683825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
87783825b71SWarner Losh 	    sc->xl_flags & XL_FLAG_INVERT_MII_PWR) {
87883825b71SWarner Losh 		XL_SEL_WIN(2);
87983825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS,
88083825b71SWarner Losh 		    CSR_READ_2(sc, XL_W2_RESET_OPTIONS) |
88183825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR) ?
88283825b71SWarner Losh 		    XL_RESETOPT_INVERT_LED : 0) |
88383825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR) ?
88483825b71SWarner Losh 		    XL_RESETOPT_INVERT_MII : 0));
88583825b71SWarner Losh 	}
88683825b71SWarner Losh 
88783825b71SWarner Losh 	/* Wait a little while for the chip to get its brains in order. */
88883825b71SWarner Losh 	DELAY(100000);
88983825b71SWarner Losh }
89083825b71SWarner Losh 
89183825b71SWarner Losh /*
89283825b71SWarner Losh  * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device
89383825b71SWarner Losh  * IDs against our list and return a device name if we find a match.
89483825b71SWarner Losh  */
89583825b71SWarner Losh static int
xl_probe(device_t dev)89683825b71SWarner Losh xl_probe(device_t dev)
89783825b71SWarner Losh {
89883825b71SWarner Losh 	const struct xl_type	*t;
89983825b71SWarner Losh 
90083825b71SWarner Losh 	t = xl_devs;
90183825b71SWarner Losh 
90283825b71SWarner Losh 	while (t->xl_name != NULL) {
90383825b71SWarner Losh 		if ((pci_get_vendor(dev) == t->xl_vid) &&
90483825b71SWarner Losh 		    (pci_get_device(dev) == t->xl_did)) {
90583825b71SWarner Losh 			device_set_desc(dev, t->xl_name);
90683825b71SWarner Losh 			return (BUS_PROBE_DEFAULT);
90783825b71SWarner Losh 		}
90883825b71SWarner Losh 		t++;
90983825b71SWarner Losh 	}
91083825b71SWarner Losh 
91183825b71SWarner Losh 	return (ENXIO);
91283825b71SWarner Losh }
91383825b71SWarner Losh 
91483825b71SWarner Losh /*
91583825b71SWarner Losh  * This routine is a kludge to work around possible hardware faults
91683825b71SWarner Losh  * or manufacturing defects that can cause the media options register
91783825b71SWarner Losh  * (or reset options register, as it's called for the first generation
91883825b71SWarner Losh  * 3c90x adapters) to return an incorrect result. I have encountered
91983825b71SWarner Losh  * one Dell Latitude laptop docking station with an integrated 3c905-TX
92083825b71SWarner Losh  * which doesn't have any of the 'mediaopt' bits set. This screws up
92183825b71SWarner Losh  * the attach routine pretty badly because it doesn't know what media
92283825b71SWarner Losh  * to look for. If we find ourselves in this predicament, this routine
92383825b71SWarner Losh  * will try to guess the media options values and warn the user of a
92483825b71SWarner Losh  * possible manufacturing defect with his adapter/system/whatever.
92583825b71SWarner Losh  */
92683825b71SWarner Losh static void
xl_mediacheck(struct xl_softc * sc)92783825b71SWarner Losh xl_mediacheck(struct xl_softc *sc)
92883825b71SWarner Losh {
92983825b71SWarner Losh 
93083825b71SWarner Losh 	/*
93183825b71SWarner Losh 	 * If some of the media options bits are set, assume they are
93283825b71SWarner Losh 	 * correct. If not, try to figure it out down below.
93383825b71SWarner Losh 	 * XXX I should check for 10baseFL, but I don't have an adapter
93483825b71SWarner Losh 	 * to test with.
93583825b71SWarner Losh 	 */
93683825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
93783825b71SWarner Losh 		/*
93883825b71SWarner Losh 		 * Check the XCVR value. If it's not in the normal range
93983825b71SWarner Losh 		 * of values, we need to fake it up here.
94083825b71SWarner Losh 		 */
94183825b71SWarner Losh 		if (sc->xl_xcvr <= XL_XCVR_AUTO)
94283825b71SWarner Losh 			return;
94383825b71SWarner Losh 		else {
94483825b71SWarner Losh 			device_printf(sc->xl_dev,
94583825b71SWarner Losh 			    "bogus xcvr value in EEPROM (%x)\n", sc->xl_xcvr);
94683825b71SWarner Losh 			device_printf(sc->xl_dev,
94783825b71SWarner Losh 			    "choosing new default based on card type\n");
94883825b71SWarner Losh 		}
94983825b71SWarner Losh 	} else {
95083825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
95183825b71SWarner Losh 		    sc->xl_media & XL_MEDIAOPT_10FL)
95283825b71SWarner Losh 			return;
95383825b71SWarner Losh 		device_printf(sc->xl_dev,
95483825b71SWarner Losh "WARNING: no media options bits set in the media options register!!\n");
95583825b71SWarner Losh 		device_printf(sc->xl_dev,
95683825b71SWarner Losh "this could be a manufacturing defect in your adapter or system\n");
95783825b71SWarner Losh 		device_printf(sc->xl_dev,
95883825b71SWarner Losh "attempting to guess media type; you should probably consult your vendor\n");
95983825b71SWarner Losh 	}
96083825b71SWarner Losh 
96183825b71SWarner Losh 	xl_choose_xcvr(sc, 1);
96283825b71SWarner Losh }
96383825b71SWarner Losh 
96483825b71SWarner Losh static void
xl_choose_xcvr(struct xl_softc * sc,int verbose)96583825b71SWarner Losh xl_choose_xcvr(struct xl_softc *sc, int verbose)
96683825b71SWarner Losh {
96783825b71SWarner Losh 	u_int16_t		devid;
96883825b71SWarner Losh 
96983825b71SWarner Losh 	/*
97083825b71SWarner Losh 	 * Read the device ID from the EEPROM.
97183825b71SWarner Losh 	 * This is what's loaded into the PCI device ID register, so it has
97283825b71SWarner Losh 	 * to be correct otherwise we wouldn't have gotten this far.
97383825b71SWarner Losh 	 */
97483825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
97583825b71SWarner Losh 
97683825b71SWarner Losh 	switch (devid) {
97783825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT:	/* 3c900-TPO */
97883825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT:		/* 3c900B-TPO */
97983825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
98083825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
98183825b71SWarner Losh 		if (verbose)
98283825b71SWarner Losh 			device_printf(sc->xl_dev,
98383825b71SWarner Losh 			    "guessing 10BaseT transceiver\n");
98483825b71SWarner Losh 		break;
98583825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT_COMBO:	/* 3c900-COMBO */
98683825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_COMBO:	/* 3c900B-COMBO */
98783825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
98883825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
98983825b71SWarner Losh 		if (verbose)
99083825b71SWarner Losh 			device_printf(sc->xl_dev,
99183825b71SWarner Losh 			    "guessing COMBO (AUI/BNC/TP)\n");
99283825b71SWarner Losh 		break;
99383825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_TPC:	/* 3c900B-TPC */
99483825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
99583825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
99683825b71SWarner Losh 		if (verbose)
99783825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing TPC (BNC/TP)\n");
99883825b71SWarner Losh 		break;
99983825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10FL:		/* 3c900B-FL */
100083825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_10FL;
100183825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUI;
100283825b71SWarner Losh 		if (verbose)
100383825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10baseFL\n");
100483825b71SWarner Losh 		break;
100583825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
100683825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_555:		/* 3c555 */
100783825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556:		/* 3c556 */
100883825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556B:	/* 3c556B */
100983825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:	/* 3c575TX */
101083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:	/* 3c575B */
101183825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:	/* 3c575C */
101283825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656:		/* 3c656 */
101383825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656B:	/* 3c656B */
101483825b71SWarner Losh 	case TC_DEVICEID_TORNADO_656C:		/* 3c656C */
101583825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B:	/* 3c920B-EMB */
101683825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B_WNM:	/* 3c920B-EMB-WNM */
101783825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_MII;
101883825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
101983825b71SWarner Losh 		if (verbose)
102083825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing MII\n");
102183825b71SWarner Losh 		break;
102283825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_100BT4:	/* 3c905-T4 */
102383825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100BT4:	/* 3c905B-T4 */
102483825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT4;
102583825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
102683825b71SWarner Losh 		if (verbose)
102783825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 100baseT4/MII\n");
102883825b71SWarner Losh 		break;
102983825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT:	/* 3c905B-TX */
103083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
103183825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_SERV:	/* 3c980C-TX */
103283825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_SOHO100TX:	/* 3cSOHO100-TX */
103383825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT:	/* 3c905C-TX */
103483825b71SWarner Losh 	case TC_DEVICEID_TORNADO_HOMECONNECT:	/* 3c450-TX */
103583825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX;
103683825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
103783825b71SWarner Losh 		if (verbose)
103883825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10/100 internal\n");
103983825b71SWarner Losh 		break;
104083825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100_COMBO:	/* 3c905B-COMBO */
104183825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
104283825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
104383825b71SWarner Losh 		if (verbose)
104483825b71SWarner Losh 			device_printf(sc->xl_dev,
104583825b71SWarner Losh 			    "guessing 10/100 plus BNC/AUI\n");
104683825b71SWarner Losh 		break;
104783825b71SWarner Losh 	default:
104883825b71SWarner Losh 		device_printf(sc->xl_dev,
104983825b71SWarner Losh 		    "unknown device ID: %x -- defaulting to 10baseT\n", devid);
105083825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
105183825b71SWarner Losh 		break;
105283825b71SWarner Losh 	}
105383825b71SWarner Losh }
105483825b71SWarner Losh 
105583825b71SWarner Losh /*
105683825b71SWarner Losh  * Attach the interface. Allocate softc structures, do ifmedia
105783825b71SWarner Losh  * setup and ethernet/BPF attach.
105883825b71SWarner Losh  */
105983825b71SWarner Losh static int
xl_attach(device_t dev)106083825b71SWarner Losh xl_attach(device_t dev)
106183825b71SWarner Losh {
106283825b71SWarner Losh 	u_char			eaddr[ETHER_ADDR_LEN];
10639ae11bbaSPyun YongHyeon 	u_int16_t		sinfo2, xcvr[2];
106483825b71SWarner Losh 	struct xl_softc		*sc;
1065*759ad4ddSJustin Hibbits 	if_t			ifp;
10669ae11bbaSPyun YongHyeon 	int			media, pmcap;
1067c972f2c0SWarner Losh 	int			error = 0, phy, rid, res;
106883825b71SWarner Losh 	uint16_t		did;
106983825b71SWarner Losh 
107083825b71SWarner Losh 	sc = device_get_softc(dev);
107183825b71SWarner Losh 	sc->xl_dev = dev;
107283825b71SWarner Losh 
107383825b71SWarner Losh 	mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
107483825b71SWarner Losh 	    MTX_DEF);
107583825b71SWarner Losh 	ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
107683825b71SWarner Losh 
107783825b71SWarner Losh 	did = pci_get_device(dev);
107883825b71SWarner Losh 
107983825b71SWarner Losh 	sc->xl_flags = 0;
108083825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555)
108183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
108283825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556 ||
108383825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556B)
108483825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
108583825b71SWarner Losh 		    XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
108683825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
108783825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555 ||
108883825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556)
108983825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_8BITROM;
109083825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556B)
109183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
109283825b71SWarner Losh 
109383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575B ||
109483825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575C ||
109583825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B ||
109683825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_656C)
109783825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG;
109883825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575A ||
109983825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575B ||
110083825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575C ||
110183825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B ||
110283825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_656C)
110383825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 |
110483825b71SWarner Losh 		  XL_FLAG_8BITROM;
110583825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656)
110683825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
110783825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575B)
110883825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
110983825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575C)
111083825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
111183825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_656C)
111283825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
111383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656 ||
111483825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B)
111583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
111683825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR;
111783825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_10_100BT_920B ||
111883825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_10_100BT_920B_WNM)
111983825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK;
112083825b71SWarner Losh 
112183825b71SWarner Losh 	switch (did) {
112283825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
112383825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:
112483825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:
112583825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:
112683825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_MMIO;
112783825b71SWarner Losh 		break;
112883825b71SWarner Losh 	default:
112983825b71SWarner Losh 		break;
113083825b71SWarner Losh 	}
113183825b71SWarner Losh 
113283825b71SWarner Losh 	/*
113383825b71SWarner Losh 	 * Map control/status registers.
113483825b71SWarner Losh 	 */
113583825b71SWarner Losh 	pci_enable_busmaster(dev);
113683825b71SWarner Losh 
113783825b71SWarner Losh 	if ((sc->xl_flags & XL_FLAG_NO_MMIO) == 0) {
113883825b71SWarner Losh 		rid = XL_PCI_LOMEM;
113983825b71SWarner Losh 		res = SYS_RES_MEMORY;
114083825b71SWarner Losh 
114183825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
114283825b71SWarner Losh 	}
114383825b71SWarner Losh 
114483825b71SWarner Losh 	if (sc->xl_res != NULL) {
114583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_USE_MMIO;
114683825b71SWarner Losh 		if (bootverbose)
114783825b71SWarner Losh 			device_printf(dev, "using memory mapped I/O\n");
114883825b71SWarner Losh 	} else {
114983825b71SWarner Losh 		rid = XL_PCI_LOIO;
115083825b71SWarner Losh 		res = SYS_RES_IOPORT;
115183825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
115283825b71SWarner Losh 		if (sc->xl_res == NULL) {
115383825b71SWarner Losh 			device_printf(dev, "couldn't map ports/memory\n");
115483825b71SWarner Losh 			error = ENXIO;
115583825b71SWarner Losh 			goto fail;
115683825b71SWarner Losh 		}
115783825b71SWarner Losh 		if (bootverbose)
115883825b71SWarner Losh 			device_printf(dev, "using port I/O\n");
115983825b71SWarner Losh 	}
116083825b71SWarner Losh 
116183825b71SWarner Losh 	sc->xl_btag = rman_get_bustag(sc->xl_res);
116283825b71SWarner Losh 	sc->xl_bhandle = rman_get_bushandle(sc->xl_res);
116383825b71SWarner Losh 
116483825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG) {
116583825b71SWarner Losh 		rid = XL_PCI_FUNCMEM;
116683825b71SWarner Losh 		sc->xl_fres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
116783825b71SWarner Losh 		    RF_ACTIVE);
116883825b71SWarner Losh 
116983825b71SWarner Losh 		if (sc->xl_fres == NULL) {
117083825b71SWarner Losh 			device_printf(dev, "couldn't map funcreg memory\n");
117183825b71SWarner Losh 			error = ENXIO;
117283825b71SWarner Losh 			goto fail;
117383825b71SWarner Losh 		}
117483825b71SWarner Losh 
117583825b71SWarner Losh 		sc->xl_ftag = rman_get_bustag(sc->xl_fres);
117683825b71SWarner Losh 		sc->xl_fhandle = rman_get_bushandle(sc->xl_fres);
117783825b71SWarner Losh 	}
117883825b71SWarner Losh 
117983825b71SWarner Losh 	/* Allocate interrupt */
118083825b71SWarner Losh 	rid = 0;
118183825b71SWarner Losh 	sc->xl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
118283825b71SWarner Losh 	    RF_SHAREABLE | RF_ACTIVE);
118383825b71SWarner Losh 	if (sc->xl_irq == NULL) {
118483825b71SWarner Losh 		device_printf(dev, "couldn't map interrupt\n");
118583825b71SWarner Losh 		error = ENXIO;
118683825b71SWarner Losh 		goto fail;
118783825b71SWarner Losh 	}
118883825b71SWarner Losh 
118983825b71SWarner Losh 	/* Initialize interface name. */
119083825b71SWarner Losh 	ifp = sc->xl_ifp = if_alloc(IFT_ETHER);
1191*759ad4ddSJustin Hibbits 	if_setsoftc(ifp, sc);
119283825b71SWarner Losh 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
119383825b71SWarner Losh 
119483825b71SWarner Losh 	/* Reset the adapter. */
119583825b71SWarner Losh 	XL_LOCK(sc);
119683825b71SWarner Losh 	xl_reset(sc);
119783825b71SWarner Losh 	XL_UNLOCK(sc);
119883825b71SWarner Losh 
119983825b71SWarner Losh 	/*
120083825b71SWarner Losh 	 * Get station address from the EEPROM.
120183825b71SWarner Losh 	 */
120283825b71SWarner Losh 	if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) {
120383825b71SWarner Losh 		device_printf(dev, "failed to read station address\n");
120483825b71SWarner Losh 		error = ENXIO;
120583825b71SWarner Losh 		goto fail;
120683825b71SWarner Losh 	}
120783825b71SWarner Losh 
120848dcbc33SPyun YongHyeon 	callout_init_mtx(&sc->xl_tick_callout, &sc->xl_mtx, 0);
12096c3e93cbSGleb Smirnoff 	NET_TASK_INIT(&sc->xl_task, 0, xl_rxeof_task, sc);
121083825b71SWarner Losh 
121183825b71SWarner Losh 	/*
121283825b71SWarner Losh 	 * Now allocate a tag for the DMA descriptor lists and a chunk
121383825b71SWarner Losh 	 * of DMA-able memory based on the tag.  Also obtain the DMA
121483825b71SWarner Losh 	 * addresses of the RX and TX ring, which we'll need later.
121583825b71SWarner Losh 	 * All of our lists are allocated as a contiguous block
121683825b71SWarner Losh 	 * of memory.
121783825b71SWarner Losh 	 */
121883825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
121983825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
122083825b71SWarner Losh 	    XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, NULL, NULL,
122183825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_tag);
122283825b71SWarner Losh 	if (error) {
122383825b71SWarner Losh 		device_printf(dev, "failed to allocate rx dma tag\n");
122483825b71SWarner Losh 		goto fail;
122583825b71SWarner Losh 	}
122683825b71SWarner Losh 
122783825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag,
1228006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT |
1229006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_rx_dmamap);
123083825b71SWarner Losh 	if (error) {
123183825b71SWarner Losh 		device_printf(dev, "no memory for rx list buffers!\n");
123283825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
123383825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
123483825b71SWarner Losh 		goto fail;
123583825b71SWarner Losh 	}
123683825b71SWarner Losh 
123783825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag,
123883825b71SWarner Losh 	    sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list,
123983825b71SWarner Losh 	    XL_RX_LIST_SZ, xl_dma_map_addr,
124083825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT);
124183825b71SWarner Losh 	if (error) {
124283825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the rx ring!\n");
124383825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
124483825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
124583825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
124683825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
124783825b71SWarner Losh 		goto fail;
124883825b71SWarner Losh 	}
124983825b71SWarner Losh 
125083825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
125183825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
125283825b71SWarner Losh 	    XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, NULL, NULL,
125383825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_tag);
125483825b71SWarner Losh 	if (error) {
125583825b71SWarner Losh 		device_printf(dev, "failed to allocate tx dma tag\n");
125683825b71SWarner Losh 		goto fail;
125783825b71SWarner Losh 	}
125883825b71SWarner Losh 
125983825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag,
1260006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT |
1261006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_tx_dmamap);
126283825b71SWarner Losh 	if (error) {
126383825b71SWarner Losh 		device_printf(dev, "no memory for list buffers!\n");
126483825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
126583825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
126683825b71SWarner Losh 		goto fail;
126783825b71SWarner Losh 	}
126883825b71SWarner Losh 
126983825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag,
127083825b71SWarner Losh 	    sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list,
127183825b71SWarner Losh 	    XL_TX_LIST_SZ, xl_dma_map_addr,
127283825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT);
127383825b71SWarner Losh 	if (error) {
127483825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the tx ring!\n");
127583825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
127683825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
127783825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
127883825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
127983825b71SWarner Losh 		goto fail;
128083825b71SWarner Losh 	}
128183825b71SWarner Losh 
128283825b71SWarner Losh 	/*
128383825b71SWarner Losh 	 * Allocate a DMA tag for the mapping of mbufs.
128483825b71SWarner Losh 	 */
128583825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
128683825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
128783825b71SWarner Losh 	    MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0, NULL,
128883825b71SWarner Losh 	    NULL, &sc->xl_mtag);
128983825b71SWarner Losh 	if (error) {
129083825b71SWarner Losh 		device_printf(dev, "failed to allocate mbuf dma tag\n");
129183825b71SWarner Losh 		goto fail;
129283825b71SWarner Losh 	}
129383825b71SWarner Losh 
129483825b71SWarner Losh 	/* We need a spare DMA map for the RX ring. */
129583825b71SWarner Losh 	error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap);
129683825b71SWarner Losh 	if (error)
129783825b71SWarner Losh 		goto fail;
129883825b71SWarner Losh 
129983825b71SWarner Losh 	/*
130083825b71SWarner Losh 	 * Figure out the card type. 3c905B adapters have the
130183825b71SWarner Losh 	 * 'supportsNoTxLength' bit set in the capabilities
130283825b71SWarner Losh 	 * word in the EEPROM.
1303f33a1c16SWarner Losh 	 * Note: my 3c575C CardBus card lies. It returns a value
130483825b71SWarner Losh 	 * of 0x1578 for its capabilities word, which is somewhat
130583825b71SWarner Losh 	 * nonsensical. Another way to distinguish a 3c90x chip
130683825b71SWarner Losh 	 * from a 3c90xB/C chip is to check for the 'supportsLargePackets'
130783825b71SWarner Losh 	 * bit. This will only be set for 3c90x boomerage chips.
130883825b71SWarner Losh 	 */
130983825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
131083825b71SWarner Losh 	if (sc->xl_caps & XL_CAPS_NO_TXLENGTH ||
131183825b71SWarner Losh 	    !(sc->xl_caps & XL_CAPS_LARGE_PKTS))
131283825b71SWarner Losh 		sc->xl_type = XL_TYPE_905B;
131383825b71SWarner Losh 	else
131483825b71SWarner Losh 		sc->xl_type = XL_TYPE_90X;
131583825b71SWarner Losh 
13169ae11bbaSPyun YongHyeon 	/* Check availability of WOL. */
13179ae11bbaSPyun YongHyeon 	if ((sc->xl_caps & XL_CAPS_PWRMGMT) != 0 &&
13183b0a4aefSJohn Baldwin 	    pci_find_cap(dev, PCIY_PMG, &pmcap) == 0) {
13199ae11bbaSPyun YongHyeon 		sc->xl_pmcap = pmcap;
13209ae11bbaSPyun YongHyeon 		sc->xl_flags |= XL_FLAG_WOL;
13219ae11bbaSPyun YongHyeon 		sinfo2 = 0;
13229ae11bbaSPyun YongHyeon 		xl_read_eeprom(sc, (caddr_t)&sinfo2, XL_EE_SOFTINFO2, 1, 0);
13239ae11bbaSPyun YongHyeon 		if ((sinfo2 & XL_SINFO2_AUX_WOL_CON) == 0 && bootverbose)
13249ae11bbaSPyun YongHyeon 			device_printf(dev,
13259ae11bbaSPyun YongHyeon 			    "No auxiliary remote wakeup connector!\n");
13269ae11bbaSPyun YongHyeon 	}
13279ae11bbaSPyun YongHyeon 
132883825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
132983825b71SWarner Losh 	sc->xl_tx_thresh = XL_MIN_FRAMELEN;
133083825b71SWarner Losh 
1331*759ad4ddSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
1332*759ad4ddSJustin Hibbits 	if_setioctlfn(ifp, xl_ioctl);
1333*759ad4ddSJustin Hibbits 	if_setcapabilities(ifp, IFCAP_VLAN_MTU);
133483825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
1335*759ad4ddSJustin Hibbits 		if_sethwassist(ifp, XL905B_CSUM_FEATURES);
133683825b71SWarner Losh #ifdef XL905B_TXCSUM_BROKEN
1337*759ad4ddSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_RXCSUM, 0);
133883825b71SWarner Losh #else
1339*759ad4ddSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_HWCSUM, 0);
134083825b71SWarner Losh #endif
134183825b71SWarner Losh 	}
13429ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0)
1343*759ad4ddSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_WOL_MAGIC, 0);
1344*759ad4ddSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
134583825b71SWarner Losh #ifdef DEVICE_POLLING
1346*759ad4ddSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
134783825b71SWarner Losh #endif
1348*759ad4ddSJustin Hibbits 	if_setstartfn(ifp, xl_start);
1349*759ad4ddSJustin Hibbits 	if_setinitfn(ifp, xl_init);
1350*759ad4ddSJustin Hibbits 	if_setsendqlen(ifp, XL_TX_LIST_CNT - 1);
1351*759ad4ddSJustin Hibbits 	if_setsendqready(ifp);
135283825b71SWarner Losh 
135383825b71SWarner Losh 	/*
135483825b71SWarner Losh 	 * Now we have to see what sort of media we have.
135583825b71SWarner Losh 	 * This includes probing for an MII interace and a
135683825b71SWarner Losh 	 * possible PHY.
135783825b71SWarner Losh 	 */
135883825b71SWarner Losh 	XL_SEL_WIN(3);
135983825b71SWarner Losh 	sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
136083825b71SWarner Losh 	if (bootverbose)
136183825b71SWarner Losh 		device_printf(dev, "media options word: %x\n", sc->xl_media);
136283825b71SWarner Losh 
136383825b71SWarner Losh 	xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
136483825b71SWarner Losh 	sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
136583825b71SWarner Losh 	sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
136683825b71SWarner Losh 	sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
136783825b71SWarner Losh 
136883825b71SWarner Losh 	xl_mediacheck(sc);
136983825b71SWarner Losh 
137083825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
137183825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
137283825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
137383825b71SWarner Losh 		if (bootverbose)
137483825b71SWarner Losh 			device_printf(dev, "found MII/AUTO\n");
137583825b71SWarner Losh 		xl_setcfg(sc);
13768e5d93dbSMarius Strobl 		/*
13778e5d93dbSMarius Strobl 		 * Attach PHYs only at MII address 24 if !XL_FLAG_PHYOK.
13788e5d93dbSMarius Strobl 		 * This is to guard against problems with certain 3Com ASIC
13798e5d93dbSMarius Strobl 		 * revisions that incorrectly map the internal transceiver
13808e5d93dbSMarius Strobl 		 * control registers at all MII addresses.
13818e5d93dbSMarius Strobl 		 */
13828e5d93dbSMarius Strobl 		phy = MII_PHY_ANY;
13838edfedadSMarius Strobl 		if ((sc->xl_flags & XL_FLAG_PHYOK) == 0)
13848e5d93dbSMarius Strobl 			phy = 24;
13858e5d93dbSMarius Strobl 		error = mii_attach(dev, &sc->xl_miibus, ifp, xl_ifmedia_upd,
1386aee0e786SPyun YongHyeon 		    xl_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY,
1387aee0e786SPyun YongHyeon 		    sc->xl_type == XL_TYPE_905B ? MIIF_DOPAUSE : 0);
13888e5d93dbSMarius Strobl 		if (error != 0) {
13898e5d93dbSMarius Strobl 			device_printf(dev, "attaching PHYs failed\n");
139083825b71SWarner Losh 			goto fail;
139183825b71SWarner Losh 		}
139283825b71SWarner Losh 		goto done;
139383825b71SWarner Losh 	}
139483825b71SWarner Losh 
139583825b71SWarner Losh 	/*
139683825b71SWarner Losh 	 * Sanity check. If the user has selected "auto" and this isn't
139783825b71SWarner Losh 	 * a 10/100 card of some kind, we need to force the transceiver
139883825b71SWarner Losh 	 * type to something sane.
139983825b71SWarner Losh 	 */
140083825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_AUTO)
140183825b71SWarner Losh 		xl_choose_xcvr(sc, bootverbose);
140283825b71SWarner Losh 
140383825b71SWarner Losh 	/*
140483825b71SWarner Losh 	 * Do ifmedia setup.
140583825b71SWarner Losh 	 */
140683825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
140783825b71SWarner Losh 		if (bootverbose)
140883825b71SWarner Losh 			device_printf(dev, "found 10baseT\n");
140983825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
141083825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
141183825b71SWarner Losh 		if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
141283825b71SWarner Losh 			ifmedia_add(&sc->ifmedia,
141383825b71SWarner Losh 			    IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
141483825b71SWarner Losh 	}
141583825b71SWarner Losh 
141683825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
141783825b71SWarner Losh 		/*
141883825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
141983825b71SWarner Losh 		 */
142083825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
142183825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
142283825b71SWarner Losh 			if (bootverbose)
142383825b71SWarner Losh 				device_printf(dev, "found 10baseFL\n");
142483825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL);
142583825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX,
142683825b71SWarner Losh 			    0, NULL);
142783825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
142883825b71SWarner Losh 				ifmedia_add(&sc->ifmedia,
142983825b71SWarner Losh 				    IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL);
143083825b71SWarner Losh 		} else {
143183825b71SWarner Losh 			if (bootverbose)
143283825b71SWarner Losh 				device_printf(dev, "found AUI\n");
143383825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
143483825b71SWarner Losh 		}
143583825b71SWarner Losh 	}
143683825b71SWarner Losh 
143783825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
143883825b71SWarner Losh 		if (bootverbose)
143983825b71SWarner Losh 			device_printf(dev, "found BNC\n");
144083825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
144183825b71SWarner Losh 	}
144283825b71SWarner Losh 
144383825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
144483825b71SWarner Losh 		if (bootverbose)
144583825b71SWarner Losh 			device_printf(dev, "found 100baseFX\n");
144683825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL);
144783825b71SWarner Losh 	}
144883825b71SWarner Losh 
144983825b71SWarner Losh 	media = IFM_ETHER|IFM_100_TX|IFM_FDX;
145083825b71SWarner Losh 	xl_choose_media(sc, &media);
145183825b71SWarner Losh 
145283825b71SWarner Losh 	if (sc->xl_miibus == NULL)
145383825b71SWarner Losh 		ifmedia_set(&sc->ifmedia, media);
145483825b71SWarner Losh 
145583825b71SWarner Losh done:
145683825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
145783825b71SWarner Losh 		XL_SEL_WIN(0);
145883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
145983825b71SWarner Losh 	}
146083825b71SWarner Losh 
146183825b71SWarner Losh 	/*
146283825b71SWarner Losh 	 * Call MI attach routine.
146383825b71SWarner Losh 	 */
146483825b71SWarner Losh 	ether_ifattach(ifp, eaddr);
146583825b71SWarner Losh 
146683825b71SWarner Losh 	error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET | INTR_MPSAFE,
146783825b71SWarner Losh 	    NULL, xl_intr, sc, &sc->xl_intrhand);
146883825b71SWarner Losh 	if (error) {
146983825b71SWarner Losh 		device_printf(dev, "couldn't set up irq\n");
147083825b71SWarner Losh 		ether_ifdetach(ifp);
147183825b71SWarner Losh 		goto fail;
147283825b71SWarner Losh 	}
147383825b71SWarner Losh 
147483825b71SWarner Losh fail:
147583825b71SWarner Losh 	if (error)
147683825b71SWarner Losh 		xl_detach(dev);
147783825b71SWarner Losh 
147883825b71SWarner Losh 	return (error);
147983825b71SWarner Losh }
148083825b71SWarner Losh 
148183825b71SWarner Losh /*
148283825b71SWarner Losh  * Choose a default media.
148383825b71SWarner Losh  * XXX This is a leaf function only called by xl_attach() and
148483825b71SWarner Losh  *     acquires/releases the non-recursible driver mutex to
148583825b71SWarner Losh  *     satisfy lock assertions.
148683825b71SWarner Losh  */
148783825b71SWarner Losh static void
xl_choose_media(struct xl_softc * sc,int * media)148883825b71SWarner Losh xl_choose_media(struct xl_softc *sc, int *media)
148983825b71SWarner Losh {
149083825b71SWarner Losh 
149183825b71SWarner Losh 	XL_LOCK(sc);
149283825b71SWarner Losh 
149383825b71SWarner Losh 	switch (sc->xl_xcvr) {
149483825b71SWarner Losh 	case XL_XCVR_10BT:
149583825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
149683825b71SWarner Losh 		xl_setmode(sc, *media);
149783825b71SWarner Losh 		break;
149883825b71SWarner Losh 	case XL_XCVR_AUI:
149983825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
150083825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
150183825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_FL;
150283825b71SWarner Losh 			xl_setmode(sc, *media);
150383825b71SWarner Losh 		} else {
150483825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_5;
150583825b71SWarner Losh 			xl_setmode(sc, *media);
150683825b71SWarner Losh 		}
150783825b71SWarner Losh 		break;
150883825b71SWarner Losh 	case XL_XCVR_COAX:
150983825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_2;
151083825b71SWarner Losh 		xl_setmode(sc, *media);
151183825b71SWarner Losh 		break;
151283825b71SWarner Losh 	case XL_XCVR_AUTO:
151383825b71SWarner Losh 	case XL_XCVR_100BTX:
151483825b71SWarner Losh 	case XL_XCVR_MII:
151583825b71SWarner Losh 		/* Chosen by miibus */
151683825b71SWarner Losh 		break;
151783825b71SWarner Losh 	case XL_XCVR_100BFX:
151883825b71SWarner Losh 		*media = IFM_ETHER|IFM_100_FX;
151983825b71SWarner Losh 		break;
152083825b71SWarner Losh 	default:
152183825b71SWarner Losh 		device_printf(sc->xl_dev, "unknown XCVR type: %d\n",
152283825b71SWarner Losh 		    sc->xl_xcvr);
152383825b71SWarner Losh 		/*
152483825b71SWarner Losh 		 * This will probably be wrong, but it prevents
152583825b71SWarner Losh 		 * the ifmedia code from panicking.
152683825b71SWarner Losh 		 */
152783825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
152883825b71SWarner Losh 		break;
152983825b71SWarner Losh 	}
153083825b71SWarner Losh 
153183825b71SWarner Losh 	XL_UNLOCK(sc);
153283825b71SWarner Losh }
153383825b71SWarner Losh 
153483825b71SWarner Losh /*
153583825b71SWarner Losh  * Shutdown hardware and free up resources. This can be called any
153683825b71SWarner Losh  * time after the mutex has been initialized. It is called in both
153783825b71SWarner Losh  * the error case in attach and the normal detach case so it needs
153883825b71SWarner Losh  * to be careful about only freeing resources that have actually been
153983825b71SWarner Losh  * allocated.
154083825b71SWarner Losh  */
154183825b71SWarner Losh static int
xl_detach(device_t dev)154283825b71SWarner Losh xl_detach(device_t dev)
154383825b71SWarner Losh {
154483825b71SWarner Losh 	struct xl_softc		*sc;
1545*759ad4ddSJustin Hibbits 	if_t			ifp;
154683825b71SWarner Losh 	int			rid, res;
154783825b71SWarner Losh 
154883825b71SWarner Losh 	sc = device_get_softc(dev);
154983825b71SWarner Losh 	ifp = sc->xl_ifp;
155083825b71SWarner Losh 
155183825b71SWarner Losh 	KASSERT(mtx_initialized(&sc->xl_mtx), ("xl mutex not initialized"));
155283825b71SWarner Losh 
155383825b71SWarner Losh #ifdef DEVICE_POLLING
1554*759ad4ddSJustin Hibbits 	if (ifp && if_getcapenable(ifp) & IFCAP_POLLING)
155583825b71SWarner Losh 		ether_poll_deregister(ifp);
155683825b71SWarner Losh #endif
155783825b71SWarner Losh 
155883825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO) {
155983825b71SWarner Losh 		rid = XL_PCI_LOMEM;
156083825b71SWarner Losh 		res = SYS_RES_MEMORY;
156183825b71SWarner Losh 	} else {
156283825b71SWarner Losh 		rid = XL_PCI_LOIO;
156383825b71SWarner Losh 		res = SYS_RES_IOPORT;
156483825b71SWarner Losh 	}
156583825b71SWarner Losh 
156683825b71SWarner Losh 	/* These should only be active if attach succeeded */
156783825b71SWarner Losh 	if (device_is_attached(dev)) {
156883825b71SWarner Losh 		XL_LOCK(sc);
156983825b71SWarner Losh 		xl_stop(sc);
157083825b71SWarner Losh 		XL_UNLOCK(sc);
157183825b71SWarner Losh 		taskqueue_drain(taskqueue_swi, &sc->xl_task);
157248dcbc33SPyun YongHyeon 		callout_drain(&sc->xl_tick_callout);
157383825b71SWarner Losh 		ether_ifdetach(ifp);
157483825b71SWarner Losh 	}
157583825b71SWarner Losh 	if (sc->xl_miibus)
157683825b71SWarner Losh 		device_delete_child(dev, sc->xl_miibus);
157783825b71SWarner Losh 	bus_generic_detach(dev);
157883825b71SWarner Losh 	ifmedia_removeall(&sc->ifmedia);
157983825b71SWarner Losh 
158083825b71SWarner Losh 	if (sc->xl_intrhand)
158183825b71SWarner Losh 		bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
158283825b71SWarner Losh 	if (sc->xl_irq)
158383825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
158483825b71SWarner Losh 	if (sc->xl_fres != NULL)
158583825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_MEMORY,
158683825b71SWarner Losh 		    XL_PCI_FUNCMEM, sc->xl_fres);
158783825b71SWarner Losh 	if (sc->xl_res)
158883825b71SWarner Losh 		bus_release_resource(dev, res, rid, sc->xl_res);
158983825b71SWarner Losh 
159083825b71SWarner Losh 	if (ifp)
159183825b71SWarner Losh 		if_free(ifp);
159283825b71SWarner Losh 
159383825b71SWarner Losh 	if (sc->xl_mtag) {
159483825b71SWarner Losh 		bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap);
159583825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_mtag);
159683825b71SWarner Losh 	}
159783825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_tag) {
159883825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_rx_tag,
159983825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
160083825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
160183825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
160283825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
160383825b71SWarner Losh 	}
160483825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_tag) {
160583825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_tx_tag,
160683825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
160783825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
160883825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
160983825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
161083825b71SWarner Losh 	}
161183825b71SWarner Losh 
161283825b71SWarner Losh 	mtx_destroy(&sc->xl_mtx);
161383825b71SWarner Losh 
161483825b71SWarner Losh 	return (0);
161583825b71SWarner Losh }
161683825b71SWarner Losh 
161783825b71SWarner Losh /*
161883825b71SWarner Losh  * Initialize the transmit descriptors.
161983825b71SWarner Losh  */
162083825b71SWarner Losh static int
xl_list_tx_init(struct xl_softc * sc)162183825b71SWarner Losh xl_list_tx_init(struct xl_softc *sc)
162283825b71SWarner Losh {
162383825b71SWarner Losh 	struct xl_chain_data	*cd;
162483825b71SWarner Losh 	struct xl_list_data	*ld;
162583825b71SWarner Losh 	int			error, i;
162683825b71SWarner Losh 
162783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
162883825b71SWarner Losh 
162983825b71SWarner Losh 	cd = &sc->xl_cdata;
163083825b71SWarner Losh 	ld = &sc->xl_ldata;
163183825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
163283825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
163383825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
163483825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
163583825b71SWarner Losh 		if (error)
163683825b71SWarner Losh 			return (error);
163783825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
163883825b71SWarner Losh 		    i * sizeof(struct xl_list);
163983825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
164083825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = NULL;
164183825b71SWarner Losh 		else
164283825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
164383825b71SWarner Losh 	}
164483825b71SWarner Losh 
164583825b71SWarner Losh 	cd->xl_tx_free = &cd->xl_tx_chain[0];
164683825b71SWarner Losh 	cd->xl_tx_tail = cd->xl_tx_head = NULL;
164783825b71SWarner Losh 
164883825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
164983825b71SWarner Losh 	return (0);
165083825b71SWarner Losh }
165183825b71SWarner Losh 
165283825b71SWarner Losh /*
165383825b71SWarner Losh  * Initialize the transmit descriptors.
165483825b71SWarner Losh  */
165583825b71SWarner Losh static int
xl_list_tx_init_90xB(struct xl_softc * sc)165683825b71SWarner Losh xl_list_tx_init_90xB(struct xl_softc *sc)
165783825b71SWarner Losh {
165883825b71SWarner Losh 	struct xl_chain_data	*cd;
165983825b71SWarner Losh 	struct xl_list_data	*ld;
166083825b71SWarner Losh 	int			error, i;
166183825b71SWarner Losh 
166283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
166383825b71SWarner Losh 
166483825b71SWarner Losh 	cd = &sc->xl_cdata;
166583825b71SWarner Losh 	ld = &sc->xl_ldata;
166683825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
166783825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
166883825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
166983825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
167083825b71SWarner Losh 		if (error)
167183825b71SWarner Losh 			return (error);
167283825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
167383825b71SWarner Losh 		    i * sizeof(struct xl_list);
167483825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
167583825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0];
167683825b71SWarner Losh 		else
167783825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
167883825b71SWarner Losh 		if (i == 0)
167983825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
168083825b71SWarner Losh 			    &cd->xl_tx_chain[XL_TX_LIST_CNT - 1];
168183825b71SWarner Losh 		else
168283825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
168383825b71SWarner Losh 			    &cd->xl_tx_chain[i - 1];
168483825b71SWarner Losh 	}
168583825b71SWarner Losh 
168683825b71SWarner Losh 	bzero(ld->xl_tx_list, XL_TX_LIST_SZ);
168783825b71SWarner Losh 	ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY);
168883825b71SWarner Losh 
168983825b71SWarner Losh 	cd->xl_tx_prod = 1;
169083825b71SWarner Losh 	cd->xl_tx_cons = 1;
169183825b71SWarner Losh 	cd->xl_tx_cnt = 0;
169283825b71SWarner Losh 
169383825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
169483825b71SWarner Losh 	return (0);
169583825b71SWarner Losh }
169683825b71SWarner Losh 
169783825b71SWarner Losh /*
169883825b71SWarner Losh  * Initialize the RX descriptors and allocate mbufs for them. Note that
169983825b71SWarner Losh  * we arrange the descriptors in a closed ring, so that the last descriptor
170083825b71SWarner Losh  * points back to the first.
170183825b71SWarner Losh  */
170283825b71SWarner Losh static int
xl_list_rx_init(struct xl_softc * sc)170383825b71SWarner Losh xl_list_rx_init(struct xl_softc *sc)
170483825b71SWarner Losh {
170583825b71SWarner Losh 	struct xl_chain_data	*cd;
170683825b71SWarner Losh 	struct xl_list_data	*ld;
170783825b71SWarner Losh 	int			error, i, next;
170883825b71SWarner Losh 	u_int32_t		nextptr;
170983825b71SWarner Losh 
171083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
171183825b71SWarner Losh 
171283825b71SWarner Losh 	cd = &sc->xl_cdata;
171383825b71SWarner Losh 	ld = &sc->xl_ldata;
171483825b71SWarner Losh 
171583825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
171683825b71SWarner Losh 		cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i];
171783825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
171883825b71SWarner Losh 		    &cd->xl_rx_chain[i].xl_map);
171983825b71SWarner Losh 		if (error)
172083825b71SWarner Losh 			return (error);
172183825b71SWarner Losh 		error = xl_newbuf(sc, &cd->xl_rx_chain[i]);
172283825b71SWarner Losh 		if (error)
172383825b71SWarner Losh 			return (error);
172483825b71SWarner Losh 		if (i == (XL_RX_LIST_CNT - 1))
172583825b71SWarner Losh 			next = 0;
172683825b71SWarner Losh 		else
172783825b71SWarner Losh 			next = i + 1;
172883825b71SWarner Losh 		nextptr = ld->xl_rx_dmaaddr +
172983825b71SWarner Losh 		    next * sizeof(struct xl_list_onefrag);
173083825b71SWarner Losh 		cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next];
173183825b71SWarner Losh 		ld->xl_rx_list[i].xl_next = htole32(nextptr);
173283825b71SWarner Losh 	}
173383825b71SWarner Losh 
173483825b71SWarner Losh 	bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
173583825b71SWarner Losh 	cd->xl_rx_head = &cd->xl_rx_chain[0];
173683825b71SWarner Losh 
173783825b71SWarner Losh 	return (0);
173883825b71SWarner Losh }
173983825b71SWarner Losh 
174083825b71SWarner Losh /*
174183825b71SWarner Losh  * Initialize an RX descriptor and attach an MBUF cluster.
174283825b71SWarner Losh  * If we fail to do so, we need to leave the old mbuf and
174383825b71SWarner Losh  * the old DMA map untouched so that it can be reused.
174483825b71SWarner Losh  */
174583825b71SWarner Losh static int
xl_newbuf(struct xl_softc * sc,struct xl_chain_onefrag * c)174683825b71SWarner Losh xl_newbuf(struct xl_softc *sc, struct xl_chain_onefrag *c)
174783825b71SWarner Losh {
174883825b71SWarner Losh 	struct mbuf		*m_new = NULL;
174983825b71SWarner Losh 	bus_dmamap_t		map;
175083825b71SWarner Losh 	bus_dma_segment_t	segs[1];
175183825b71SWarner Losh 	int			error, nseg;
175283825b71SWarner Losh 
175383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
175483825b71SWarner Losh 
1755c6499eccSGleb Smirnoff 	m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
175683825b71SWarner Losh 	if (m_new == NULL)
175783825b71SWarner Losh 		return (ENOBUFS);
175883825b71SWarner Losh 
175983825b71SWarner Losh 	m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
176083825b71SWarner Losh 
176183825b71SWarner Losh 	/* Force longword alignment for packet payload. */
176283825b71SWarner Losh 	m_adj(m_new, ETHER_ALIGN);
176383825b71SWarner Losh 
176483825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, sc->xl_tmpmap, m_new,
176583825b71SWarner Losh 	    segs, &nseg, BUS_DMA_NOWAIT);
176683825b71SWarner Losh 	if (error) {
176783825b71SWarner Losh 		m_freem(m_new);
176883825b71SWarner Losh 		device_printf(sc->xl_dev, "can't map mbuf (error %d)\n",
176983825b71SWarner Losh 		    error);
177083825b71SWarner Losh 		return (error);
177183825b71SWarner Losh 	}
177283825b71SWarner Losh 	KASSERT(nseg == 1,
177383825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
177483825b71SWarner Losh 
177583825b71SWarner Losh 	bus_dmamap_unload(sc->xl_mtag, c->xl_map);
177683825b71SWarner Losh 	map = c->xl_map;
177783825b71SWarner Losh 	c->xl_map = sc->xl_tmpmap;
177883825b71SWarner Losh 	sc->xl_tmpmap = map;
177983825b71SWarner Losh 	c->xl_mbuf = m_new;
178083825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG);
178183825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_addr = htole32(segs->ds_addr);
1782f321edf9SPyun YongHyeon 	c->xl_ptr->xl_status = 0;
178383825b71SWarner Losh 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD);
178483825b71SWarner Losh 	return (0);
178583825b71SWarner Losh }
178683825b71SWarner Losh 
178783825b71SWarner Losh static int
xl_rx_resync(struct xl_softc * sc)178883825b71SWarner Losh xl_rx_resync(struct xl_softc *sc)
178983825b71SWarner Losh {
179083825b71SWarner Losh 	struct xl_chain_onefrag	*pos;
179183825b71SWarner Losh 	int			i;
179283825b71SWarner Losh 
179383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
179483825b71SWarner Losh 
179583825b71SWarner Losh 	pos = sc->xl_cdata.xl_rx_head;
179683825b71SWarner Losh 
179783825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
179883825b71SWarner Losh 		if (pos->xl_ptr->xl_status)
179983825b71SWarner Losh 			break;
180083825b71SWarner Losh 		pos = pos->xl_next;
180183825b71SWarner Losh 	}
180283825b71SWarner Losh 
180383825b71SWarner Losh 	if (i == XL_RX_LIST_CNT)
180483825b71SWarner Losh 		return (0);
180583825b71SWarner Losh 
180683825b71SWarner Losh 	sc->xl_cdata.xl_rx_head = pos;
180783825b71SWarner Losh 
180883825b71SWarner Losh 	return (EAGAIN);
180983825b71SWarner Losh }
181083825b71SWarner Losh 
181183825b71SWarner Losh /*
181283825b71SWarner Losh  * A frame has been uploaded: pass the resulting mbuf chain up to
181383825b71SWarner Losh  * the higher level protocols.
181483825b71SWarner Losh  */
18151abcdbd1SAttilio Rao static int
xl_rxeof(struct xl_softc * sc)181683825b71SWarner Losh xl_rxeof(struct xl_softc *sc)
181783825b71SWarner Losh {
181883825b71SWarner Losh 	struct mbuf		*m;
1819*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
182083825b71SWarner Losh 	struct xl_chain_onefrag	*cur_rx;
18215a13764bSPyun YongHyeon 	int			total_len;
18221abcdbd1SAttilio Rao 	int			rx_npkts = 0;
182383825b71SWarner Losh 	u_int32_t		rxstat;
182483825b71SWarner Losh 
182583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
182683825b71SWarner Losh again:
182783825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap,
182883825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
182983825b71SWarner Losh 	while ((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
183083825b71SWarner Losh #ifdef DEVICE_POLLING
1831*759ad4ddSJustin Hibbits 		if (if_getcapenable(ifp) & IFCAP_POLLING) {
183283825b71SWarner Losh 			if (sc->rxcycles <= 0)
183383825b71SWarner Losh 				break;
183483825b71SWarner Losh 			sc->rxcycles--;
183583825b71SWarner Losh 		}
183683825b71SWarner Losh #endif
183783825b71SWarner Losh 		cur_rx = sc->xl_cdata.xl_rx_head;
183883825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = cur_rx->xl_next;
183983825b71SWarner Losh 		total_len = rxstat & XL_RXSTAT_LENMASK;
18405a13764bSPyun YongHyeon 		rx_npkts++;
184183825b71SWarner Losh 
184283825b71SWarner Losh 		/*
184383825b71SWarner Losh 		 * Since we have told the chip to allow large frames,
184483825b71SWarner Losh 		 * we need to trap giant frame errors in software. We allow
184583825b71SWarner Losh 		 * a little more than the normal frame size to account for
184683825b71SWarner Losh 		 * frames with VLAN tags.
184783825b71SWarner Losh 		 */
184883825b71SWarner Losh 		if (total_len > XL_MAX_FRAMELEN)
184983825b71SWarner Losh 			rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE);
185083825b71SWarner Losh 
185183825b71SWarner Losh 		/*
185283825b71SWarner Losh 		 * If an error occurs, update stats, clear the
185383825b71SWarner Losh 		 * status word and leave the mbuf cluster in place:
185483825b71SWarner Losh 		 * it should simply get re-used next time this descriptor
185583825b71SWarner Losh 		 * comes up in the ring.
185683825b71SWarner Losh 		 */
185783825b71SWarner Losh 		if (rxstat & XL_RXSTAT_UP_ERROR) {
1858ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
185983825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
186083825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
186183825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
186283825b71SWarner Losh 			continue;
186383825b71SWarner Losh 		}
186483825b71SWarner Losh 
186583825b71SWarner Losh 		/*
186683825b71SWarner Losh 		 * If the error bit was not set, the upload complete
186783825b71SWarner Losh 		 * bit should be set which means we have a valid packet.
186883825b71SWarner Losh 		 * If not, something truly strange has happened.
186983825b71SWarner Losh 		 */
187083825b71SWarner Losh 		if (!(rxstat & XL_RXSTAT_UP_CMPLT)) {
187183825b71SWarner Losh 			device_printf(sc->xl_dev,
187283825b71SWarner Losh 			    "bad receive status -- packet dropped\n");
1873ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
187483825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
187583825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
187683825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
187783825b71SWarner Losh 			continue;
187883825b71SWarner Losh 		}
187983825b71SWarner Losh 
188083825b71SWarner Losh 		/* No errors; receive the packet. */
188183825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map,
188283825b71SWarner Losh 		    BUS_DMASYNC_POSTREAD);
188383825b71SWarner Losh 		m = cur_rx->xl_mbuf;
188483825b71SWarner Losh 
188583825b71SWarner Losh 		/*
188683825b71SWarner Losh 		 * Try to conjure up a new mbuf cluster. If that
188783825b71SWarner Losh 		 * fails, it means we have an out of memory condition and
188883825b71SWarner Losh 		 * should leave the buffer in place and continue. This will
188983825b71SWarner Losh 		 * result in a lost packet, but there's little else we
189083825b71SWarner Losh 		 * can do in this situation.
189183825b71SWarner Losh 		 */
189283825b71SWarner Losh 		if (xl_newbuf(sc, cur_rx)) {
1893ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
189483825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
189583825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
189683825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
189783825b71SWarner Losh 			continue;
189883825b71SWarner Losh 		}
189983825b71SWarner Losh 		bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
190083825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
190183825b71SWarner Losh 
1902ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
190383825b71SWarner Losh 		m->m_pkthdr.rcvif = ifp;
190483825b71SWarner Losh 		m->m_pkthdr.len = m->m_len = total_len;
190583825b71SWarner Losh 
1906*759ad4ddSJustin Hibbits 		if (if_getcapenable(ifp) & IFCAP_RXCSUM) {
190783825b71SWarner Losh 			/* Do IP checksum checking. */
190883825b71SWarner Losh 			if (rxstat & XL_RXSTAT_IPCKOK)
190983825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
191083825b71SWarner Losh 			if (!(rxstat & XL_RXSTAT_IPCKERR))
191183825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
191283825b71SWarner Losh 			if ((rxstat & XL_RXSTAT_TCPCOK &&
191383825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_TCPCKERR)) ||
191483825b71SWarner Losh 			    (rxstat & XL_RXSTAT_UDPCKOK &&
191583825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_UDPCKERR))) {
191683825b71SWarner Losh 				m->m_pkthdr.csum_flags |=
191783825b71SWarner Losh 					CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
191883825b71SWarner Losh 				m->m_pkthdr.csum_data = 0xffff;
191983825b71SWarner Losh 			}
192083825b71SWarner Losh 		}
192183825b71SWarner Losh 
192283825b71SWarner Losh 		XL_UNLOCK(sc);
1923*759ad4ddSJustin Hibbits 		if_input(ifp, m);
192483825b71SWarner Losh 		XL_LOCK(sc);
192583825b71SWarner Losh 
192683825b71SWarner Losh 		/*
192783825b71SWarner Losh 		 * If we are running from the taskqueue, the interface
192883825b71SWarner Losh 		 * might have been stopped while we were passing the last
192983825b71SWarner Losh 		 * packet up the network stack.
193083825b71SWarner Losh 		 */
1931*759ad4ddSJustin Hibbits 		if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
19321abcdbd1SAttilio Rao 			return (rx_npkts);
193383825b71SWarner Losh 	}
193483825b71SWarner Losh 
193583825b71SWarner Losh 	/*
193683825b71SWarner Losh 	 * Handle the 'end of channel' condition. When the upload
193783825b71SWarner Losh 	 * engine hits the end of the RX ring, it will stall. This
193883825b71SWarner Losh 	 * is our cue to flush the RX ring, reload the uplist pointer
193983825b71SWarner Losh 	 * register and unstall the engine.
194083825b71SWarner Losh 	 * XXX This is actually a little goofy. With the ThunderLAN
194183825b71SWarner Losh 	 * chip, you get an interrupt when the receiver hits the end
194283825b71SWarner Losh 	 * of the receive ring, which tells you exactly when you
194383825b71SWarner Losh 	 * you need to reload the ring pointer. Here we have to
194483825b71SWarner Losh 	 * fake it. I'm mad at myself for not being clever enough
194583825b71SWarner Losh 	 * to avoid the use of a goto here.
194683825b71SWarner Losh 	 */
194783825b71SWarner Losh 	if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 ||
194883825b71SWarner Losh 		CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) {
194983825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
195083825b71SWarner Losh 		xl_wait(sc);
195183825b71SWarner Losh 		CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
195283825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0];
195383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
195483825b71SWarner Losh 		goto again;
195583825b71SWarner Losh 	}
19561abcdbd1SAttilio Rao 	return (rx_npkts);
195783825b71SWarner Losh }
195883825b71SWarner Losh 
195983825b71SWarner Losh /*
196083825b71SWarner Losh  * Taskqueue wrapper for xl_rxeof().
196183825b71SWarner Losh  */
196283825b71SWarner Losh static void
xl_rxeof_task(void * arg,int pending)196383825b71SWarner Losh xl_rxeof_task(void *arg, int pending)
196483825b71SWarner Losh {
196583825b71SWarner Losh 	struct xl_softc *sc = (struct xl_softc *)arg;
196683825b71SWarner Losh 
196783825b71SWarner Losh 	XL_LOCK(sc);
1968*759ad4ddSJustin Hibbits 	if (if_getdrvflags(sc->xl_ifp) & IFF_DRV_RUNNING)
196983825b71SWarner Losh 		xl_rxeof(sc);
197083825b71SWarner Losh 	XL_UNLOCK(sc);
197183825b71SWarner Losh }
197283825b71SWarner Losh 
197383825b71SWarner Losh /*
197483825b71SWarner Losh  * A frame was downloaded to the chip. It's safe for us to clean up
197583825b71SWarner Losh  * the list buffers.
197683825b71SWarner Losh  */
197783825b71SWarner Losh static void
xl_txeof(struct xl_softc * sc)197883825b71SWarner Losh xl_txeof(struct xl_softc *sc)
197983825b71SWarner Losh {
198083825b71SWarner Losh 	struct xl_chain		*cur_tx;
1981*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
198283825b71SWarner Losh 
198383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
198483825b71SWarner Losh 
198583825b71SWarner Losh 	/*
198683825b71SWarner Losh 	 * Go through our tx list and free mbufs for those
198783825b71SWarner Losh 	 * frames that have been uploaded. Note: the 3c905B
198883825b71SWarner Losh 	 * sets a special bit in the status word to let us
198983825b71SWarner Losh 	 * know that a frame has been downloaded, but the
199083825b71SWarner Losh 	 * original 3c900/3c905 adapters don't do that.
199183825b71SWarner Losh 	 * Consequently, we have to use a different test if
199283825b71SWarner Losh 	 * xl_type != XL_TYPE_905B.
199383825b71SWarner Losh 	 */
199483825b71SWarner Losh 	while (sc->xl_cdata.xl_tx_head != NULL) {
199583825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_head;
199683825b71SWarner Losh 
199783825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DOWNLIST_PTR))
199883825b71SWarner Losh 			break;
199983825b71SWarner Losh 
200083825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = cur_tx->xl_next;
200183825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
200283825b71SWarner Losh 		    BUS_DMASYNC_POSTWRITE);
200383825b71SWarner Losh 		bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
200483825b71SWarner Losh 		m_freem(cur_tx->xl_mbuf);
200583825b71SWarner Losh 		cur_tx->xl_mbuf = NULL;
2006ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
2007*759ad4ddSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
200883825b71SWarner Losh 
200983825b71SWarner Losh 		cur_tx->xl_next = sc->xl_cdata.xl_tx_free;
201083825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx;
201183825b71SWarner Losh 	}
201283825b71SWarner Losh 
201383825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head == NULL) {
201483825b71SWarner Losh 		sc->xl_wdog_timer = 0;
201583825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = NULL;
201683825b71SWarner Losh 	} else {
201783825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED ||
201883825b71SWarner Losh 			!CSR_READ_4(sc, XL_DOWNLIST_PTR)) {
201983825b71SWarner Losh 			CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
202083825b71SWarner Losh 				sc->xl_cdata.xl_tx_head->xl_phys);
202183825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
202283825b71SWarner Losh 		}
202383825b71SWarner Losh 	}
202483825b71SWarner Losh }
202583825b71SWarner Losh 
202683825b71SWarner Losh static void
xl_txeof_90xB(struct xl_softc * sc)202783825b71SWarner Losh xl_txeof_90xB(struct xl_softc *sc)
202883825b71SWarner Losh {
202983825b71SWarner Losh 	struct xl_chain		*cur_tx = NULL;
2030*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
203183825b71SWarner Losh 	int			idx;
203283825b71SWarner Losh 
203383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
203483825b71SWarner Losh 
203583825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
203683825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
203783825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_cons;
203883825b71SWarner Losh 	while (idx != sc->xl_cdata.xl_tx_prod) {
203983825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
204083825b71SWarner Losh 
204183825b71SWarner Losh 		if (!(le32toh(cur_tx->xl_ptr->xl_status) &
204283825b71SWarner Losh 		      XL_TXSTAT_DL_COMPLETE))
204383825b71SWarner Losh 			break;
204483825b71SWarner Losh 
204583825b71SWarner Losh 		if (cur_tx->xl_mbuf != NULL) {
204683825b71SWarner Losh 			bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
204783825b71SWarner Losh 			    BUS_DMASYNC_POSTWRITE);
204883825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
204983825b71SWarner Losh 			m_freem(cur_tx->xl_mbuf);
205083825b71SWarner Losh 			cur_tx->xl_mbuf = NULL;
205183825b71SWarner Losh 		}
205283825b71SWarner Losh 
2053ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
205483825b71SWarner Losh 
205583825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt--;
205683825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
205783825b71SWarner Losh 	}
205883825b71SWarner Losh 
205983825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_cnt == 0)
206083825b71SWarner Losh 		sc->xl_wdog_timer = 0;
206183825b71SWarner Losh 	sc->xl_cdata.xl_tx_cons = idx;
206283825b71SWarner Losh 
206383825b71SWarner Losh 	if (cur_tx != NULL)
2064*759ad4ddSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
206583825b71SWarner Losh }
206683825b71SWarner Losh 
206783825b71SWarner Losh /*
206883825b71SWarner Losh  * TX 'end of channel' interrupt handler. Actually, we should
206983825b71SWarner Losh  * only get a 'TX complete' interrupt if there's a transmit error,
207083825b71SWarner Losh  * so this is really TX error handler.
207183825b71SWarner Losh  */
207283825b71SWarner Losh static void
xl_txeoc(struct xl_softc * sc)207383825b71SWarner Losh xl_txeoc(struct xl_softc *sc)
207483825b71SWarner Losh {
207583825b71SWarner Losh 	u_int8_t		txstat;
207683825b71SWarner Losh 
207783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
207883825b71SWarner Losh 
207983825b71SWarner Losh 	while ((txstat = CSR_READ_1(sc, XL_TX_STATUS))) {
208083825b71SWarner Losh 		if (txstat & XL_TXSTATUS_UNDERRUN ||
208183825b71SWarner Losh 			txstat & XL_TXSTATUS_JABBER ||
208283825b71SWarner Losh 			txstat & XL_TXSTATUS_RECLAIM) {
208383825b71SWarner Losh 			device_printf(sc->xl_dev,
20847498e81aSPyun YongHyeon 			    "transmission error: 0x%02x\n", txstat);
208583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
208683825b71SWarner Losh 			xl_wait(sc);
208783825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
208883825b71SWarner Losh 				if (sc->xl_cdata.xl_tx_cnt) {
208983825b71SWarner Losh 					int			i;
209083825b71SWarner Losh 					struct xl_chain		*c;
209183825b71SWarner Losh 
209283825b71SWarner Losh 					i = sc->xl_cdata.xl_tx_cons;
209383825b71SWarner Losh 					c = &sc->xl_cdata.xl_tx_chain[i];
209483825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
209583825b71SWarner Losh 					    c->xl_phys);
209683825b71SWarner Losh 					CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
20977498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
209883825b71SWarner Losh 				}
209983825b71SWarner Losh 			} else {
21007498e81aSPyun YongHyeon 				if (sc->xl_cdata.xl_tx_head != NULL) {
210183825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
210283825b71SWarner Losh 					    sc->xl_cdata.xl_tx_head->xl_phys);
21037498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
21047498e81aSPyun YongHyeon 				}
210583825b71SWarner Losh 			}
210683825b71SWarner Losh 			/*
210783825b71SWarner Losh 			 * Remember to set this for the
210883825b71SWarner Losh 			 * first generation 3c90X chips.
210983825b71SWarner Losh 			 */
211083825b71SWarner Losh 			CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
211183825b71SWarner Losh 			if (txstat & XL_TXSTATUS_UNDERRUN &&
211283825b71SWarner Losh 			    sc->xl_tx_thresh < XL_PACKET_SIZE) {
211383825b71SWarner Losh 				sc->xl_tx_thresh += XL_MIN_FRAMELEN;
211483825b71SWarner Losh 				device_printf(sc->xl_dev,
211583825b71SWarner Losh "tx underrun, increasing tx start threshold to %d bytes\n", sc->xl_tx_thresh);
211683825b71SWarner Losh 			}
211783825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
211883825b71SWarner Losh 			    XL_CMD_TX_SET_START|sc->xl_tx_thresh);
211983825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
212083825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND,
212183825b71SWarner Losh 				XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
212283825b71SWarner Losh 			}
212383825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
212483825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
212583825b71SWarner Losh 		} else {
212683825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
212783825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
212883825b71SWarner Losh 		}
212983825b71SWarner Losh 		/*
213083825b71SWarner Losh 		 * Write an arbitrary byte to the TX_STATUS register
213183825b71SWarner Losh 		 * to clear this interrupt/error and advance to the next.
213283825b71SWarner Losh 		 */
213383825b71SWarner Losh 		CSR_WRITE_1(sc, XL_TX_STATUS, 0x01);
213483825b71SWarner Losh 	}
213583825b71SWarner Losh }
213683825b71SWarner Losh 
213783825b71SWarner Losh static void
xl_intr(void * arg)213883825b71SWarner Losh xl_intr(void *arg)
213983825b71SWarner Losh {
214083825b71SWarner Losh 	struct xl_softc		*sc = arg;
2141*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
214283825b71SWarner Losh 	u_int16_t		status;
214383825b71SWarner Losh 
214483825b71SWarner Losh 	XL_LOCK(sc);
214583825b71SWarner Losh 
214683825b71SWarner Losh #ifdef DEVICE_POLLING
2147*759ad4ddSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_POLLING) {
214883825b71SWarner Losh 		XL_UNLOCK(sc);
214983825b71SWarner Losh 		return;
215083825b71SWarner Losh 	}
215183825b71SWarner Losh #endif
215283825b71SWarner Losh 
215374517b07SPyun YongHyeon 	for (;;) {
215474517b07SPyun YongHyeon 		status = CSR_READ_2(sc, XL_STATUS);
215574517b07SPyun YongHyeon 		if ((status & XL_INTRS) == 0 || status == 0xFFFF)
215674517b07SPyun YongHyeon 			break;
215783825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
215883825b71SWarner Losh 		    XL_CMD_INTR_ACK|(status & XL_INTRS));
2159*759ad4ddSJustin Hibbits 		if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
216074517b07SPyun YongHyeon 			break;
216183825b71SWarner Losh 
216283825b71SWarner Losh 		if (status & XL_STAT_UP_COMPLETE) {
21635a13764bSPyun YongHyeon 			if (xl_rxeof(sc) == 0) {
216483825b71SWarner Losh 				while (xl_rx_resync(sc))
216583825b71SWarner Losh 					xl_rxeof(sc);
216683825b71SWarner Losh 			}
216783825b71SWarner Losh 		}
216883825b71SWarner Losh 
216983825b71SWarner Losh 		if (status & XL_STAT_DOWN_COMPLETE) {
217083825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B)
217183825b71SWarner Losh 				xl_txeof_90xB(sc);
217283825b71SWarner Losh 			else
217383825b71SWarner Losh 				xl_txeof(sc);
217483825b71SWarner Losh 		}
217583825b71SWarner Losh 
217683825b71SWarner Losh 		if (status & XL_STAT_TX_COMPLETE) {
2177ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
217883825b71SWarner Losh 			xl_txeoc(sc);
217983825b71SWarner Losh 		}
218083825b71SWarner Losh 
218183825b71SWarner Losh 		if (status & XL_STAT_ADFAIL) {
2182*759ad4ddSJustin Hibbits 			if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
218383825b71SWarner Losh 			xl_init_locked(sc);
218474517b07SPyun YongHyeon 			break;
218583825b71SWarner Losh 		}
218683825b71SWarner Losh 
218748dcbc33SPyun YongHyeon 		if (status & XL_STAT_STATSOFLOW)
218848dcbc33SPyun YongHyeon 			xl_stats_update(sc);
218983825b71SWarner Losh 	}
219083825b71SWarner Losh 
2191*759ad4ddSJustin Hibbits 	if (!if_sendq_empty(ifp) &&
2192*759ad4ddSJustin Hibbits 	    if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
219383825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
219483825b71SWarner Losh 			xl_start_90xB_locked(ifp);
219583825b71SWarner Losh 		else
219683825b71SWarner Losh 			xl_start_locked(ifp);
219783825b71SWarner Losh 	}
219883825b71SWarner Losh 
219983825b71SWarner Losh 	XL_UNLOCK(sc);
220083825b71SWarner Losh }
220183825b71SWarner Losh 
220283825b71SWarner Losh #ifdef DEVICE_POLLING
22031abcdbd1SAttilio Rao static int
xl_poll(if_t ifp,enum poll_cmd cmd,int count)2204*759ad4ddSJustin Hibbits xl_poll(if_t ifp, enum poll_cmd cmd, int count)
220583825b71SWarner Losh {
2206*759ad4ddSJustin Hibbits 	struct xl_softc *sc = if_getsoftc(ifp);
22071abcdbd1SAttilio Rao 	int rx_npkts = 0;
220883825b71SWarner Losh 
220983825b71SWarner Losh 	XL_LOCK(sc);
2210*759ad4ddSJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
22111abcdbd1SAttilio Rao 		rx_npkts = xl_poll_locked(ifp, cmd, count);
221283825b71SWarner Losh 	XL_UNLOCK(sc);
22131abcdbd1SAttilio Rao 	return (rx_npkts);
221483825b71SWarner Losh }
221583825b71SWarner Losh 
22161abcdbd1SAttilio Rao static int
xl_poll_locked(if_t ifp,enum poll_cmd cmd,int count)2217*759ad4ddSJustin Hibbits xl_poll_locked(if_t ifp, enum poll_cmd cmd, int count)
221883825b71SWarner Losh {
2219*759ad4ddSJustin Hibbits 	struct xl_softc *sc = if_getsoftc(ifp);
22201abcdbd1SAttilio Rao 	int rx_npkts;
222183825b71SWarner Losh 
222283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
222383825b71SWarner Losh 
222483825b71SWarner Losh 	sc->rxcycles = count;
22251abcdbd1SAttilio Rao 	rx_npkts = xl_rxeof(sc);
222683825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
222783825b71SWarner Losh 		xl_txeof_90xB(sc);
222883825b71SWarner Losh 	else
222983825b71SWarner Losh 		xl_txeof(sc);
223083825b71SWarner Losh 
2231*759ad4ddSJustin Hibbits 	if (!if_sendq_empty(ifp)) {
223283825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
223383825b71SWarner Losh 			xl_start_90xB_locked(ifp);
223483825b71SWarner Losh 		else
223583825b71SWarner Losh 			xl_start_locked(ifp);
223683825b71SWarner Losh 	}
223783825b71SWarner Losh 
223883825b71SWarner Losh 	if (cmd == POLL_AND_CHECK_STATUS) {
223983825b71SWarner Losh 		u_int16_t status;
224083825b71SWarner Losh 
224183825b71SWarner Losh 		status = CSR_READ_2(sc, XL_STATUS);
224283825b71SWarner Losh 		if (status & XL_INTRS && status != 0xFFFF) {
224383825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
224483825b71SWarner Losh 			    XL_CMD_INTR_ACK|(status & XL_INTRS));
224583825b71SWarner Losh 
224683825b71SWarner Losh 			if (status & XL_STAT_TX_COMPLETE) {
2247ec4a8977SGleb Smirnoff 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
224883825b71SWarner Losh 				xl_txeoc(sc);
224983825b71SWarner Losh 			}
225083825b71SWarner Losh 
225183825b71SWarner Losh 			if (status & XL_STAT_ADFAIL) {
2252*759ad4ddSJustin Hibbits 				if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
225383825b71SWarner Losh 				xl_init_locked(sc);
225483825b71SWarner Losh 			}
225583825b71SWarner Losh 
225648dcbc33SPyun YongHyeon 			if (status & XL_STAT_STATSOFLOW)
2257de53eba0SPyun YongHyeon 				xl_stats_update(sc);
225883825b71SWarner Losh 		}
225983825b71SWarner Losh 	}
22601abcdbd1SAttilio Rao 	return (rx_npkts);
226183825b71SWarner Losh }
226283825b71SWarner Losh #endif /* DEVICE_POLLING */
226383825b71SWarner Losh 
226483825b71SWarner Losh static void
xl_tick(void * xsc)226548dcbc33SPyun YongHyeon xl_tick(void *xsc)
226683825b71SWarner Losh {
226783825b71SWarner Losh 	struct xl_softc *sc = xsc;
226848dcbc33SPyun YongHyeon 	struct mii_data *mii;
226983825b71SWarner Losh 
227083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
227183825b71SWarner Losh 
227248dcbc33SPyun YongHyeon 	if (sc->xl_miibus != NULL) {
227348dcbc33SPyun YongHyeon 		mii = device_get_softc(sc->xl_miibus);
227448dcbc33SPyun YongHyeon 		mii_tick(mii);
227548dcbc33SPyun YongHyeon 	}
227648dcbc33SPyun YongHyeon 
227748dcbc33SPyun YongHyeon 	xl_stats_update(sc);
227883825b71SWarner Losh 	if (xl_watchdog(sc) == EJUSTRETURN)
227983825b71SWarner Losh 		return;
228083825b71SWarner Losh 
228148dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
228283825b71SWarner Losh }
228383825b71SWarner Losh 
228483825b71SWarner Losh static void
xl_stats_update(struct xl_softc * sc)228548dcbc33SPyun YongHyeon xl_stats_update(struct xl_softc *sc)
228683825b71SWarner Losh {
2287*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
228883825b71SWarner Losh 	struct xl_stats		xl_stats;
228983825b71SWarner Losh 	u_int8_t		*p;
229083825b71SWarner Losh 	int			i;
229183825b71SWarner Losh 
229283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
229383825b71SWarner Losh 
229483825b71SWarner Losh 	bzero((char *)&xl_stats, sizeof(struct xl_stats));
229583825b71SWarner Losh 
229683825b71SWarner Losh 	p = (u_int8_t *)&xl_stats;
229783825b71SWarner Losh 
229883825b71SWarner Losh 	/* Read all the stats registers. */
229983825b71SWarner Losh 	XL_SEL_WIN(6);
230083825b71SWarner Losh 
230183825b71SWarner Losh 	for (i = 0; i < 16; i++)
230283825b71SWarner Losh 		*p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i);
230383825b71SWarner Losh 
2304ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IERRORS, xl_stats.xl_rx_overrun);
230583825b71SWarner Losh 
2306ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
2307ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_multi_collision +
2308ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_single_collision +
2309ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_late_collision);
231083825b71SWarner Losh 
231183825b71SWarner Losh 	/*
231283825b71SWarner Losh 	 * Boomerang and cyclone chips have an extra stats counter
231383825b71SWarner Losh 	 * in window 4 (BadSSD). We have to read this too in order
231483825b71SWarner Losh 	 * to clear out all the stats registers and avoid a statsoflow
231583825b71SWarner Losh 	 * interrupt.
231683825b71SWarner Losh 	 */
231783825b71SWarner Losh 	XL_SEL_WIN(4);
231883825b71SWarner Losh 	CSR_READ_1(sc, XL_W4_BADSSD);
231983825b71SWarner Losh 	XL_SEL_WIN(7);
232083825b71SWarner Losh }
232183825b71SWarner Losh 
232283825b71SWarner Losh /*
232383825b71SWarner Losh  * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
232483825b71SWarner Losh  * pointers to the fragment pointers.
232583825b71SWarner Losh  */
232683825b71SWarner Losh static int
xl_encap(struct xl_softc * sc,struct xl_chain * c,struct mbuf ** m_head)232783825b71SWarner Losh xl_encap(struct xl_softc *sc, struct xl_chain *c, struct mbuf **m_head)
232883825b71SWarner Losh {
232983825b71SWarner Losh 	struct mbuf		*m_new;
2330*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
233183825b71SWarner Losh 	int			error, i, nseg, total_len;
233283825b71SWarner Losh 	u_int32_t		status;
233383825b71SWarner Losh 
233483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
233583825b71SWarner Losh 
233683825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map, *m_head,
233783825b71SWarner Losh 	    sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
233883825b71SWarner Losh 
233983825b71SWarner Losh 	if (error && error != EFBIG) {
234083825b71SWarner Losh 		if_printf(ifp, "can't map mbuf (error %d)\n", error);
234183825b71SWarner Losh 		return (error);
234283825b71SWarner Losh 	}
234383825b71SWarner Losh 
234483825b71SWarner Losh 	/*
234583825b71SWarner Losh 	 * Handle special case: we used up all 63 fragments,
234683825b71SWarner Losh 	 * but we have more mbufs left in the chain. Copy the
234783825b71SWarner Losh 	 * data into an mbuf cluster. Note that we don't
234883825b71SWarner Losh 	 * bother clearing the values in the other fragment
234983825b71SWarner Losh 	 * pointers/counters; it wouldn't gain us anything,
235083825b71SWarner Losh 	 * and would waste cycles.
235183825b71SWarner Losh 	 */
235283825b71SWarner Losh 	if (error) {
2353c6499eccSGleb Smirnoff 		m_new = m_collapse(*m_head, M_NOWAIT, XL_MAXFRAGS);
235483825b71SWarner Losh 		if (m_new == NULL) {
235583825b71SWarner Losh 			m_freem(*m_head);
235683825b71SWarner Losh 			*m_head = NULL;
235783825b71SWarner Losh 			return (ENOBUFS);
235883825b71SWarner Losh 		}
235983825b71SWarner Losh 		*m_head = m_new;
236083825b71SWarner Losh 
236183825b71SWarner Losh 		error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map,
236283825b71SWarner Losh 		    *m_head, sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
236383825b71SWarner Losh 		if (error) {
236483825b71SWarner Losh 			m_freem(*m_head);
236583825b71SWarner Losh 			*m_head = NULL;
236683825b71SWarner Losh 			if_printf(ifp, "can't map mbuf (error %d)\n", error);
236783825b71SWarner Losh 			return (error);
236883825b71SWarner Losh 		}
236983825b71SWarner Losh 	}
237083825b71SWarner Losh 
237183825b71SWarner Losh 	KASSERT(nseg <= XL_MAXFRAGS,
237283825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
237383825b71SWarner Losh 	if (nseg == 0) {
237483825b71SWarner Losh 		m_freem(*m_head);
237583825b71SWarner Losh 		*m_head = NULL;
237683825b71SWarner Losh 		return (EIO);
237783825b71SWarner Losh 	}
23784f58a95cSPyun YongHyeon 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE);
237983825b71SWarner Losh 
238083825b71SWarner Losh 	total_len = 0;
238183825b71SWarner Losh 	for (i = 0; i < nseg; i++) {
238283825b71SWarner Losh 		KASSERT(sc->xl_cdata.xl_tx_segs[i].ds_len <= MCLBYTES,
238383825b71SWarner Losh 		    ("segment size too large"));
238483825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_addr =
238583825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_addr);
238683825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_len =
238783825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_len);
238883825b71SWarner Losh 		total_len += sc->xl_cdata.xl_tx_segs[i].ds_len;
238983825b71SWarner Losh 	}
239078564edaSPyun YongHyeon 	c->xl_ptr->xl_frag[nseg - 1].xl_len |= htole32(XL_LAST_FRAG);
239183825b71SWarner Losh 
239283825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
239383825b71SWarner Losh 		status = XL_TXSTAT_RND_DEFEAT;
239483825b71SWarner Losh 
239583825b71SWarner Losh #ifndef XL905B_TXCSUM_BROKEN
23968e95322aSPyun YongHyeon 		if ((*m_head)->m_pkthdr.csum_flags) {
23978e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP)
239883825b71SWarner Losh 				status |= XL_TXSTAT_IPCKSUM;
23998e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
240083825b71SWarner Losh 				status |= XL_TXSTAT_TCPCKSUM;
24018e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
240283825b71SWarner Losh 				status |= XL_TXSTAT_UDPCKSUM;
240383825b71SWarner Losh 		}
240483825b71SWarner Losh #endif
24054f58a95cSPyun YongHyeon 	} else
24064f58a95cSPyun YongHyeon 		status = total_len;
240783825b71SWarner Losh 	c->xl_ptr->xl_status = htole32(status);
24084f58a95cSPyun YongHyeon 	c->xl_ptr->xl_next = 0;
240983825b71SWarner Losh 
241083825b71SWarner Losh 	c->xl_mbuf = *m_head;
241183825b71SWarner Losh 	return (0);
241283825b71SWarner Losh }
241383825b71SWarner Losh 
241483825b71SWarner Losh /*
241583825b71SWarner Losh  * Main transmit routine. To avoid having to do mbuf copies, we put pointers
241683825b71SWarner Losh  * to the mbuf data regions directly in the transmit lists. We also save a
241783825b71SWarner Losh  * copy of the pointers since the transmit list fragment pointers are
241883825b71SWarner Losh  * physical addresses.
241983825b71SWarner Losh  */
242083825b71SWarner Losh 
242183825b71SWarner Losh static void
xl_start(if_t ifp)2422*759ad4ddSJustin Hibbits xl_start(if_t ifp)
242383825b71SWarner Losh {
2424*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
242583825b71SWarner Losh 
242683825b71SWarner Losh 	XL_LOCK(sc);
242783825b71SWarner Losh 
242883825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
242983825b71SWarner Losh 		xl_start_90xB_locked(ifp);
243083825b71SWarner Losh 	else
243183825b71SWarner Losh 		xl_start_locked(ifp);
243283825b71SWarner Losh 
243383825b71SWarner Losh 	XL_UNLOCK(sc);
243483825b71SWarner Losh }
243583825b71SWarner Losh 
243683825b71SWarner Losh static void
xl_start_locked(if_t ifp)2437*759ad4ddSJustin Hibbits xl_start_locked(if_t ifp)
243883825b71SWarner Losh {
2439*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
24404a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
244183825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
24424a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
244383825b71SWarner Losh 	int			error;
244483825b71SWarner Losh 
244583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
244683825b71SWarner Losh 
2447*759ad4ddSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2448ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
2449ba65e0ccSPyun YongHyeon 		return;
245083825b71SWarner Losh 	/*
245183825b71SWarner Losh 	 * Check for an available queue slot. If there are none,
245283825b71SWarner Losh 	 * punt.
245383825b71SWarner Losh 	 */
245483825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_free == NULL) {
245583825b71SWarner Losh 		xl_txeoc(sc);
245683825b71SWarner Losh 		xl_txeof(sc);
245783825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_free == NULL) {
2458*759ad4ddSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
245983825b71SWarner Losh 			return;
246083825b71SWarner Losh 		}
246183825b71SWarner Losh 	}
246283825b71SWarner Losh 
246383825b71SWarner Losh 	start_tx = sc->xl_cdata.xl_tx_free;
246483825b71SWarner Losh 
2465*759ad4ddSJustin Hibbits 	for (; !if_sendq_empty(ifp) &&
246683825b71SWarner Losh 	    sc->xl_cdata.xl_tx_free != NULL;) {
2467*759ad4ddSJustin Hibbits 		m_head = if_dequeue(ifp);
246883825b71SWarner Losh 		if (m_head == NULL)
246983825b71SWarner Losh 			break;
247083825b71SWarner Losh 
247183825b71SWarner Losh 		/* Pick a descriptor off the free list. */
24724a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
247383825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_free;
247483825b71SWarner Losh 
247583825b71SWarner Losh 		/* Pack the data into the descriptor. */
247683825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
247783825b71SWarner Losh 		if (error) {
24784a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
247983825b71SWarner Losh 			if (m_head == NULL)
248083825b71SWarner Losh 				break;
2481*759ad4ddSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
2482*759ad4ddSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
248383825b71SWarner Losh 			break;
248483825b71SWarner Losh 		}
248583825b71SWarner Losh 
248683825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
248783825b71SWarner Losh 		cur_tx->xl_next = NULL;
248883825b71SWarner Losh 
248983825b71SWarner Losh 		/* Chain it together. */
249083825b71SWarner Losh 		if (prev != NULL) {
249183825b71SWarner Losh 			prev->xl_next = cur_tx;
249283825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
249383825b71SWarner Losh 		}
249483825b71SWarner Losh 		prev = cur_tx;
249583825b71SWarner Losh 
249683825b71SWarner Losh 		/*
249783825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
249883825b71SWarner Losh 		 * to him.
249983825b71SWarner Losh 		 */
250083825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
250183825b71SWarner Losh 	}
250283825b71SWarner Losh 
250383825b71SWarner Losh 	/*
250483825b71SWarner Losh 	 * If there are no packets queued, bail.
250583825b71SWarner Losh 	 */
250683825b71SWarner Losh 	if (cur_tx == NULL)
250783825b71SWarner Losh 		return;
250883825b71SWarner Losh 
250983825b71SWarner Losh 	/*
251083825b71SWarner Losh 	 * Place the request for the upload interrupt
251183825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
251283825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
251383825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
251483825b71SWarner Losh 	 * once for each packet.
251583825b71SWarner Losh 	 */
251678564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
251783825b71SWarner Losh 
251883825b71SWarner Losh 	/*
251983825b71SWarner Losh 	 * Queue the packets. If the TX channel is clear, update
252083825b71SWarner Losh 	 * the downlist pointer register.
252183825b71SWarner Losh 	 */
252283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
252383825b71SWarner Losh 	xl_wait(sc);
252483825b71SWarner Losh 
252583825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head != NULL) {
252683825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_next = start_tx;
252783825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next =
252883825b71SWarner Losh 		    htole32(start_tx->xl_phys);
252978564edaSPyun YongHyeon 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &=
253078564edaSPyun YongHyeon 		    htole32(~XL_TXSTAT_DL_INTR);
253183825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
253283825b71SWarner Losh 	} else {
253383825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = start_tx;
253483825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
253583825b71SWarner Losh 	}
25360ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
25370ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
253883825b71SWarner Losh 	if (!CSR_READ_4(sc, XL_DOWNLIST_PTR))
253983825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys);
254083825b71SWarner Losh 
254183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
254283825b71SWarner Losh 
254383825b71SWarner Losh 	XL_SEL_WIN(7);
254483825b71SWarner Losh 
254583825b71SWarner Losh 	/*
254683825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
254783825b71SWarner Losh 	 */
254883825b71SWarner Losh 	sc->xl_wdog_timer = 5;
254983825b71SWarner Losh 
255083825b71SWarner Losh 	/*
255183825b71SWarner Losh 	 * XXX Under certain conditions, usually on slower machines
255283825b71SWarner Losh 	 * where interrupts may be dropped, it's possible for the
255383825b71SWarner Losh 	 * adapter to chew up all the buffers in the receive ring
255483825b71SWarner Losh 	 * and stall, without us being able to do anything about it.
255583825b71SWarner Losh 	 * To guard against this, we need to make a pass over the
255683825b71SWarner Losh 	 * RX queue to make sure there aren't any packets pending.
255783825b71SWarner Losh 	 * Doing it here means we can flush the receive ring at the
255883825b71SWarner Losh 	 * same time the chip is DMAing the transmit descriptors we
255983825b71SWarner Losh 	 * just gave it.
256083825b71SWarner Losh 	 *
256183825b71SWarner Losh 	 * 3Com goes to some lengths to emphasize the Parallel Tasking (tm)
256283825b71SWarner Losh 	 * nature of their chips in all their marketing literature;
256383825b71SWarner Losh 	 * we may as well take advantage of it. :)
256483825b71SWarner Losh 	 */
256583825b71SWarner Losh 	taskqueue_enqueue(taskqueue_swi, &sc->xl_task);
256683825b71SWarner Losh }
256783825b71SWarner Losh 
256883825b71SWarner Losh static void
xl_start_90xB_locked(if_t ifp)2569*759ad4ddSJustin Hibbits xl_start_90xB_locked(if_t ifp)
257083825b71SWarner Losh {
2571*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
25724a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
257383825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
25744a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
257583825b71SWarner Losh 	int			error, idx;
257683825b71SWarner Losh 
257783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
257883825b71SWarner Losh 
2579*759ad4ddSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2580ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
258183825b71SWarner Losh 		return;
258283825b71SWarner Losh 
258383825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_prod;
258483825b71SWarner Losh 	start_tx = &sc->xl_cdata.xl_tx_chain[idx];
258583825b71SWarner Losh 
2586*759ad4ddSJustin Hibbits 	for (; !if_sendq_empty(ifp) &&
258783825b71SWarner Losh 	    sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL;) {
258883825b71SWarner Losh 		if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) {
2589*759ad4ddSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
259083825b71SWarner Losh 			break;
259183825b71SWarner Losh 		}
259283825b71SWarner Losh 
2593*759ad4ddSJustin Hibbits 		m_head = if_dequeue(ifp);
259483825b71SWarner Losh 		if (m_head == NULL)
259583825b71SWarner Losh 			break;
259683825b71SWarner Losh 
25974a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
259883825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
259983825b71SWarner Losh 
260083825b71SWarner Losh 		/* Pack the data into the descriptor. */
260183825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
260283825b71SWarner Losh 		if (error) {
26034a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
260483825b71SWarner Losh 			if (m_head == NULL)
260583825b71SWarner Losh 				break;
2606*759ad4ddSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
2607*759ad4ddSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
260883825b71SWarner Losh 			break;
260983825b71SWarner Losh 		}
261083825b71SWarner Losh 
261183825b71SWarner Losh 		/* Chain it together. */
261283825b71SWarner Losh 		if (prev != NULL)
261383825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
261483825b71SWarner Losh 		prev = cur_tx;
261583825b71SWarner Losh 
261683825b71SWarner Losh 		/*
261783825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
261883825b71SWarner Losh 		 * to him.
261983825b71SWarner Losh 		 */
262083825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
262183825b71SWarner Losh 
262283825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
262383825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt++;
262483825b71SWarner Losh 	}
262583825b71SWarner Losh 
262683825b71SWarner Losh 	/*
262783825b71SWarner Losh 	 * If there are no packets queued, bail.
262883825b71SWarner Losh 	 */
262983825b71SWarner Losh 	if (cur_tx == NULL)
263083825b71SWarner Losh 		return;
263183825b71SWarner Losh 
263283825b71SWarner Losh 	/*
263383825b71SWarner Losh 	 * Place the request for the upload interrupt
263483825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
263583825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
263683825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
263783825b71SWarner Losh 	 * once for each packet.
263883825b71SWarner Losh 	 */
263978564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
264083825b71SWarner Losh 
264183825b71SWarner Losh 	/* Start transmission */
264283825b71SWarner Losh 	sc->xl_cdata.xl_tx_prod = idx;
264383825b71SWarner Losh 	start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys);
26440ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
26450ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
264683825b71SWarner Losh 
264783825b71SWarner Losh 	/*
264883825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
264983825b71SWarner Losh 	 */
265083825b71SWarner Losh 	sc->xl_wdog_timer = 5;
265183825b71SWarner Losh }
265283825b71SWarner Losh 
265383825b71SWarner Losh static void
xl_init(void * xsc)265483825b71SWarner Losh xl_init(void *xsc)
265583825b71SWarner Losh {
265683825b71SWarner Losh 	struct xl_softc		*sc = xsc;
265783825b71SWarner Losh 
265883825b71SWarner Losh 	XL_LOCK(sc);
265983825b71SWarner Losh 	xl_init_locked(sc);
266083825b71SWarner Losh 	XL_UNLOCK(sc);
266183825b71SWarner Losh }
266283825b71SWarner Losh 
266383825b71SWarner Losh static void
xl_init_locked(struct xl_softc * sc)266483825b71SWarner Losh xl_init_locked(struct xl_softc *sc)
266583825b71SWarner Losh {
2666*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
266783825b71SWarner Losh 	int			error, i;
266883825b71SWarner Losh 	struct mii_data		*mii = NULL;
266983825b71SWarner Losh 
267083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
267183825b71SWarner Losh 
2672*759ad4ddSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
267327b031a9SPyun YongHyeon 		return;
267483825b71SWarner Losh 	/*
267583825b71SWarner Losh 	 * Cancel pending I/O and free all RX/TX buffers.
267683825b71SWarner Losh 	 */
267783825b71SWarner Losh 	xl_stop(sc);
267883825b71SWarner Losh 
2679ac681091SPyun YongHyeon 	/* Reset the chip to a known state. */
2680ac681091SPyun YongHyeon 	xl_reset(sc);
2681ac681091SPyun YongHyeon 
268283825b71SWarner Losh 	if (sc->xl_miibus == NULL) {
268383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
268483825b71SWarner Losh 		xl_wait(sc);
268583825b71SWarner Losh 	}
268683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
268783825b71SWarner Losh 	xl_wait(sc);
268883825b71SWarner Losh 	DELAY(10000);
268983825b71SWarner Losh 
269083825b71SWarner Losh 	if (sc->xl_miibus != NULL)
269183825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
269283825b71SWarner Losh 
26939ae11bbaSPyun YongHyeon 	/*
26949ae11bbaSPyun YongHyeon 	 * Clear WOL status and disable all WOL feature as WOL
26959ae11bbaSPyun YongHyeon 	 * would interfere Rx operation under normal environments.
26969ae11bbaSPyun YongHyeon 	 */
26979ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0) {
26989ae11bbaSPyun YongHyeon 		XL_SEL_WIN(7);
26999ae11bbaSPyun YongHyeon 		CSR_READ_2(sc, XL_W7_BM_PME);
27009ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_W7_BM_PME, 0);
27019ae11bbaSPyun YongHyeon 	}
270283825b71SWarner Losh 	/* Init our MAC address */
270383825b71SWarner Losh 	XL_SEL_WIN(2);
270483825b71SWarner Losh 	for (i = 0; i < ETHER_ADDR_LEN; i++) {
270583825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i,
2706*759ad4ddSJustin Hibbits 				if_getlladdr(sc->xl_ifp)[i]);
270783825b71SWarner Losh 	}
270883825b71SWarner Losh 
270983825b71SWarner Losh 	/* Clear the station mask. */
271083825b71SWarner Losh 	for (i = 0; i < 3; i++)
271183825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
271283825b71SWarner Losh #ifdef notdef
271383825b71SWarner Losh 	/* Reset TX and RX. */
271483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
271583825b71SWarner Losh 	xl_wait(sc);
271683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
271783825b71SWarner Losh 	xl_wait(sc);
271883825b71SWarner Losh #endif
271983825b71SWarner Losh 	/* Init circular RX list. */
272083825b71SWarner Losh 	error = xl_list_rx_init(sc);
272183825b71SWarner Losh 	if (error) {
272283825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the rx ring failed (%d)\n",
272383825b71SWarner Losh 		    error);
272483825b71SWarner Losh 		xl_stop(sc);
272583825b71SWarner Losh 		return;
272683825b71SWarner Losh 	}
272783825b71SWarner Losh 
272883825b71SWarner Losh 	/* Init TX descriptors. */
272983825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
273083825b71SWarner Losh 		error = xl_list_tx_init_90xB(sc);
273183825b71SWarner Losh 	else
273283825b71SWarner Losh 		error = xl_list_tx_init(sc);
273383825b71SWarner Losh 	if (error) {
273483825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the tx ring failed (%d)\n",
273583825b71SWarner Losh 		    error);
273683825b71SWarner Losh 		xl_stop(sc);
273783825b71SWarner Losh 		return;
273883825b71SWarner Losh 	}
273983825b71SWarner Losh 
274083825b71SWarner Losh 	/*
274183825b71SWarner Losh 	 * Set the TX freethresh value.
274283825b71SWarner Losh 	 * Note that this has no effect on 3c905B "cyclone"
274383825b71SWarner Losh 	 * cards but is required for 3c900/3c905 "boomerang"
274483825b71SWarner Losh 	 * cards in order to enable the download engine.
274583825b71SWarner Losh 	 */
274683825b71SWarner Losh 	CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
274783825b71SWarner Losh 
274883825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
274983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
275083825b71SWarner Losh 
275183825b71SWarner Losh 	/*
275283825b71SWarner Losh 	 * If this is a 3c905B, also set the tx reclaim threshold.
275383825b71SWarner Losh 	 * This helps cut down on the number of tx reclaim errors
275483825b71SWarner Losh 	 * that could happen on a busy network. The chip multiplies
275583825b71SWarner Losh 	 * the register value by 16 to obtain the actual threshold
275683825b71SWarner Losh 	 * in bytes, so we divide by 16 when setting the value here.
275783825b71SWarner Losh 	 * The existing threshold value can be examined by reading
275883825b71SWarner Losh 	 * the register at offset 9 in window 5.
275983825b71SWarner Losh 	 */
276083825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
276183825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
276283825b71SWarner Losh 		    XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
276383825b71SWarner Losh 	}
276483825b71SWarner Losh 
276583825b71SWarner Losh 	/* Set RX filter bits. */
2766dd0a7688SPyun YongHyeon 	xl_rxfilter(sc);
276783825b71SWarner Losh 
276883825b71SWarner Losh 	/*
276983825b71SWarner Losh 	 * Load the address of the RX list. We have to
277083825b71SWarner Losh 	 * stall the upload engine before we can manipulate
277183825b71SWarner Losh 	 * the uplist pointer register, then unstall it when
277283825b71SWarner Losh 	 * we're finished. We also have to wait for the
277383825b71SWarner Losh 	 * stall command to complete before proceeding.
277483825b71SWarner Losh 	 * Note that we have to do this after any RX resets
277583825b71SWarner Losh 	 * have completed since the uplist register is cleared
277683825b71SWarner Losh 	 * by a reset.
277783825b71SWarner Losh 	 */
277883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
277983825b71SWarner Losh 	xl_wait(sc);
278083825b71SWarner Losh 	CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
278183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
278283825b71SWarner Losh 	xl_wait(sc);
278383825b71SWarner Losh 
278483825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
278583825b71SWarner Losh 		/* Set polling interval */
278683825b71SWarner Losh 		CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
278783825b71SWarner Losh 		/* Load the address of the TX list */
278883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
278983825b71SWarner Losh 		xl_wait(sc);
279083825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
279183825b71SWarner Losh 		    sc->xl_cdata.xl_tx_chain[0].xl_phys);
279283825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
279383825b71SWarner Losh 		xl_wait(sc);
279483825b71SWarner Losh 	}
279583825b71SWarner Losh 
279683825b71SWarner Losh 	/*
279783825b71SWarner Losh 	 * If the coax transceiver is on, make sure to enable
279883825b71SWarner Losh 	 * the DC-DC converter.
279983825b71SWarner Losh 	 */
280083825b71SWarner Losh 	XL_SEL_WIN(3);
280183825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_COAX)
280283825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
280383825b71SWarner Losh 	else
280483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
280583825b71SWarner Losh 
280683825b71SWarner Losh 	/*
280783825b71SWarner Losh 	 * increase packet size to allow reception of 802.1q or ISL packets.
280883825b71SWarner Losh 	 * For the 3c90x chip, set the 'allow large packets' bit in the MAC
280983825b71SWarner Losh 	 * control register. For 3c90xB/C chips, use the RX packet size
281083825b71SWarner Losh 	 * register.
281183825b71SWarner Losh 	 */
281283825b71SWarner Losh 
281383825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
281483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
281583825b71SWarner Losh 	else {
281683825b71SWarner Losh 		u_int8_t macctl;
281783825b71SWarner Losh 		macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
281883825b71SWarner Losh 		macctl |= XL_MACCTRL_ALLOW_LARGE_PACK;
281983825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
282083825b71SWarner Losh 	}
282183825b71SWarner Losh 
282283825b71SWarner Losh 	/* Clear out the stats counters. */
282383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
282448dcbc33SPyun YongHyeon 	xl_stats_update(sc);
282583825b71SWarner Losh 	XL_SEL_WIN(4);
282683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
282783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
282883825b71SWarner Losh 
282983825b71SWarner Losh 	/*
283083825b71SWarner Losh 	 * Enable interrupts.
283183825b71SWarner Losh 	 */
283283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
283383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
283483825b71SWarner Losh #ifdef DEVICE_POLLING
283583825b71SWarner Losh 	/* Disable interrupts if we are polling. */
2836*759ad4ddSJustin Hibbits 	if (if_getcapenable(ifp) & IFCAP_POLLING)
283783825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
283883825b71SWarner Losh 	else
283983825b71SWarner Losh #endif
284083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
284183825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
284283825b71SWarner Losh 	    bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
284383825b71SWarner Losh 
284483825b71SWarner Losh 	/* Set the RX early threshold */
284583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
28460b96ba12SPyun YongHyeon 	CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY);
284783825b71SWarner Losh 
284883825b71SWarner Losh 	/* Enable receiver and transmitter. */
284983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
285083825b71SWarner Losh 	xl_wait(sc);
285183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
285283825b71SWarner Losh 	xl_wait(sc);
285383825b71SWarner Losh 
285483825b71SWarner Losh 	/* XXX Downcall to miibus. */
285583825b71SWarner Losh 	if (mii != NULL)
285683825b71SWarner Losh 		mii_mediachg(mii);
285783825b71SWarner Losh 
285883825b71SWarner Losh 	/* Select window 7 for normal operations. */
285983825b71SWarner Losh 	XL_SEL_WIN(7);
286083825b71SWarner Losh 
2861*759ad4ddSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
2862*759ad4ddSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
286383825b71SWarner Losh 
286483825b71SWarner Losh 	sc->xl_wdog_timer = 0;
286548dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
286683825b71SWarner Losh }
286783825b71SWarner Losh 
286883825b71SWarner Losh /*
286983825b71SWarner Losh  * Set media options.
287083825b71SWarner Losh  */
287183825b71SWarner Losh static int
xl_ifmedia_upd(if_t ifp)2872*759ad4ddSJustin Hibbits xl_ifmedia_upd(if_t ifp)
287383825b71SWarner Losh {
2874*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
287583825b71SWarner Losh 	struct ifmedia		*ifm = NULL;
287683825b71SWarner Losh 	struct mii_data		*mii = NULL;
287783825b71SWarner Losh 
287883825b71SWarner Losh 	XL_LOCK(sc);
287983825b71SWarner Losh 
288083825b71SWarner Losh 	if (sc->xl_miibus != NULL)
288183825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
288283825b71SWarner Losh 	if (mii == NULL)
288383825b71SWarner Losh 		ifm = &sc->ifmedia;
288483825b71SWarner Losh 	else
288583825b71SWarner Losh 		ifm = &mii->mii_media;
288683825b71SWarner Losh 
288783825b71SWarner Losh 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
288883825b71SWarner Losh 	case IFM_100_FX:
288983825b71SWarner Losh 	case IFM_10_FL:
289083825b71SWarner Losh 	case IFM_10_2:
289183825b71SWarner Losh 	case IFM_10_5:
289283825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
289383825b71SWarner Losh 		XL_UNLOCK(sc);
289483825b71SWarner Losh 		return (0);
289583825b71SWarner Losh 	}
289683825b71SWarner Losh 
289783825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
289883825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
289983825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
2900*759ad4ddSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
290183825b71SWarner Losh 		xl_init_locked(sc);
290283825b71SWarner Losh 	} else {
290383825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
290483825b71SWarner Losh 	}
290583825b71SWarner Losh 
290683825b71SWarner Losh 	XL_UNLOCK(sc);
290783825b71SWarner Losh 
290883825b71SWarner Losh 	return (0);
290983825b71SWarner Losh }
291083825b71SWarner Losh 
291183825b71SWarner Losh /*
291283825b71SWarner Losh  * Report current media status.
291383825b71SWarner Losh  */
291483825b71SWarner Losh static void
xl_ifmedia_sts(if_t ifp,struct ifmediareq * ifmr)2915*759ad4ddSJustin Hibbits xl_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
291683825b71SWarner Losh {
2917*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
291883825b71SWarner Losh 	u_int32_t		icfg;
291983825b71SWarner Losh 	u_int16_t		status = 0;
292083825b71SWarner Losh 	struct mii_data		*mii = NULL;
292183825b71SWarner Losh 
292283825b71SWarner Losh 	XL_LOCK(sc);
292383825b71SWarner Losh 
292483825b71SWarner Losh 	if (sc->xl_miibus != NULL)
292583825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
292683825b71SWarner Losh 
292783825b71SWarner Losh 	XL_SEL_WIN(4);
292883825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
292983825b71SWarner Losh 
293083825b71SWarner Losh 	XL_SEL_WIN(3);
293183825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK;
293283825b71SWarner Losh 	icfg >>= XL_ICFG_CONNECTOR_BITS;
293383825b71SWarner Losh 
293483825b71SWarner Losh 	ifmr->ifm_active = IFM_ETHER;
293583825b71SWarner Losh 	ifmr->ifm_status = IFM_AVALID;
293683825b71SWarner Losh 
293783825b71SWarner Losh 	if ((status & XL_MEDIASTAT_CARRIER) == 0)
293883825b71SWarner Losh 		ifmr->ifm_status |= IFM_ACTIVE;
293983825b71SWarner Losh 
294083825b71SWarner Losh 	switch (icfg) {
294183825b71SWarner Losh 	case XL_XCVR_10BT:
294283825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_T;
294383825b71SWarner Losh 		if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
294483825b71SWarner Losh 			ifmr->ifm_active |= IFM_FDX;
294583825b71SWarner Losh 		else
294683825b71SWarner Losh 			ifmr->ifm_active |= IFM_HDX;
294783825b71SWarner Losh 		break;
294883825b71SWarner Losh 	case XL_XCVR_AUI:
294983825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
295083825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
295183825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_FL;
295283825b71SWarner Losh 			if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
295383825b71SWarner Losh 				ifmr->ifm_active |= IFM_FDX;
295483825b71SWarner Losh 			else
295583825b71SWarner Losh 				ifmr->ifm_active |= IFM_HDX;
295683825b71SWarner Losh 		} else
295783825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_5;
295883825b71SWarner Losh 		break;
295983825b71SWarner Losh 	case XL_XCVR_COAX:
296083825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_2;
296183825b71SWarner Losh 		break;
296283825b71SWarner Losh 	/*
296383825b71SWarner Losh 	 * XXX MII and BTX/AUTO should be separate cases.
296483825b71SWarner Losh 	 */
296583825b71SWarner Losh 
296683825b71SWarner Losh 	case XL_XCVR_100BTX:
296783825b71SWarner Losh 	case XL_XCVR_AUTO:
296883825b71SWarner Losh 	case XL_XCVR_MII:
296983825b71SWarner Losh 		if (mii != NULL) {
297083825b71SWarner Losh 			mii_pollstat(mii);
297183825b71SWarner Losh 			ifmr->ifm_active = mii->mii_media_active;
297283825b71SWarner Losh 			ifmr->ifm_status = mii->mii_media_status;
297383825b71SWarner Losh 		}
297483825b71SWarner Losh 		break;
297583825b71SWarner Losh 	case XL_XCVR_100BFX:
297683825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_100_FX;
297783825b71SWarner Losh 		break;
297883825b71SWarner Losh 	default:
297983825b71SWarner Losh 		if_printf(ifp, "unknown XCVR type: %d\n", icfg);
298083825b71SWarner Losh 		break;
298183825b71SWarner Losh 	}
298283825b71SWarner Losh 
298383825b71SWarner Losh 	XL_UNLOCK(sc);
298483825b71SWarner Losh }
298583825b71SWarner Losh 
298683825b71SWarner Losh static int
xl_ioctl(if_t ifp,u_long command,caddr_t data)2987*759ad4ddSJustin Hibbits xl_ioctl(if_t ifp, u_long command, caddr_t data)
298883825b71SWarner Losh {
2989*759ad4ddSJustin Hibbits 	struct xl_softc		*sc = if_getsoftc(ifp);
299083825b71SWarner Losh 	struct ifreq		*ifr = (struct ifreq *) data;
2991a3835274SPyun YongHyeon 	int			error = 0, mask;
299283825b71SWarner Losh 	struct mii_data		*mii = NULL;
299383825b71SWarner Losh 
299483825b71SWarner Losh 	switch (command) {
299583825b71SWarner Losh 	case SIOCSIFFLAGS:
299683825b71SWarner Losh 		XL_LOCK(sc);
2997*759ad4ddSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
2998*759ad4ddSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING &&
2999*759ad4ddSJustin Hibbits 			    (if_getflags(ifp) ^ sc->xl_if_flags) &
3000dd0a7688SPyun YongHyeon 			    (IFF_PROMISC | IFF_ALLMULTI))
3001dd0a7688SPyun YongHyeon 				xl_rxfilter(sc);
3002dd0a7688SPyun YongHyeon 			else
300383825b71SWarner Losh 				xl_init_locked(sc);
300483825b71SWarner Losh 		} else {
3005*759ad4ddSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
300683825b71SWarner Losh 				xl_stop(sc);
300783825b71SWarner Losh 		}
3008*759ad4ddSJustin Hibbits 		sc->xl_if_flags = if_getflags(ifp);
300983825b71SWarner Losh 		XL_UNLOCK(sc);
301083825b71SWarner Losh 		break;
301183825b71SWarner Losh 	case SIOCADDMULTI:
301283825b71SWarner Losh 	case SIOCDELMULTI:
301383825b71SWarner Losh 		/* XXX Downcall from if_addmulti() possibly with locks held. */
301483825b71SWarner Losh 		XL_LOCK(sc);
3015*759ad4ddSJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
3016dd0a7688SPyun YongHyeon 			xl_rxfilter(sc);
301783825b71SWarner Losh 		XL_UNLOCK(sc);
301883825b71SWarner Losh 		break;
301983825b71SWarner Losh 	case SIOCGIFMEDIA:
302083825b71SWarner Losh 	case SIOCSIFMEDIA:
302183825b71SWarner Losh 		if (sc->xl_miibus != NULL)
302283825b71SWarner Losh 			mii = device_get_softc(sc->xl_miibus);
302383825b71SWarner Losh 		if (mii == NULL)
302483825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
302583825b71SWarner Losh 			    &sc->ifmedia, command);
302683825b71SWarner Losh 		else
302783825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
302883825b71SWarner Losh 			    &mii->mii_media, command);
302983825b71SWarner Losh 		break;
303083825b71SWarner Losh 	case SIOCSIFCAP:
3031*759ad4ddSJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
303283825b71SWarner Losh #ifdef DEVICE_POLLING
3033a3835274SPyun YongHyeon 		if ((mask & IFCAP_POLLING) != 0 &&
3034*759ad4ddSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_POLLING) != 0) {
3035*759ad4ddSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_POLLING);
3036*759ad4ddSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_POLLING) != 0) {
303783825b71SWarner Losh 				error = ether_poll_register(xl_poll, ifp);
303883825b71SWarner Losh 				if (error)
3039a3835274SPyun YongHyeon 					break;
304083825b71SWarner Losh 				XL_LOCK(sc);
304183825b71SWarner Losh 				/* Disable interrupts */
304283825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
3043*759ad4ddSJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_POLLING, 0);
304483825b71SWarner Losh 				XL_UNLOCK(sc);
3045a3835274SPyun YongHyeon 			} else {
304683825b71SWarner Losh 				error = ether_poll_deregister(ifp);
304783825b71SWarner Losh 				/* Enable interrupts. */
304883825b71SWarner Losh 				XL_LOCK(sc);
3049a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3050a3835274SPyun YongHyeon 				    XL_CMD_INTR_ACK | 0xFF);
3051a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3052a3835274SPyun YongHyeon 				    XL_CMD_INTR_ENB | XL_INTRS);
305383825b71SWarner Losh 				if (sc->xl_flags & XL_FLAG_FUNCREG)
3054a3835274SPyun YongHyeon 					bus_space_write_4(sc->xl_ftag,
3055a3835274SPyun YongHyeon 					    sc->xl_fhandle, 4, 0x8000);
305683825b71SWarner Losh 				XL_UNLOCK(sc);
3057a3835274SPyun YongHyeon 			}
305883825b71SWarner Losh 		}
305983825b71SWarner Losh #endif /* DEVICE_POLLING */
306083825b71SWarner Losh 		XL_LOCK(sc);
3061a3835274SPyun YongHyeon 		if ((mask & IFCAP_TXCSUM) != 0 &&
3062*759ad4ddSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_TXCSUM) != 0) {
3063*759ad4ddSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TXCSUM);
3064*759ad4ddSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
3065*759ad4ddSJustin Hibbits 				if_sethwassistbits(ifp, XL905B_CSUM_FEATURES, 0);
306683825b71SWarner Losh 			else
3067*759ad4ddSJustin Hibbits 				if_sethwassistbits(ifp, 0, XL905B_CSUM_FEATURES);
3068a3835274SPyun YongHyeon 		}
3069a3835274SPyun YongHyeon 		if ((mask & IFCAP_RXCSUM) != 0 &&
3070*759ad4ddSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_RXCSUM) != 0)
3071*759ad4ddSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_RXCSUM);
30729ae11bbaSPyun YongHyeon 		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
3073*759ad4ddSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_WOL_MAGIC) != 0)
3074*759ad4ddSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
307583825b71SWarner Losh 		XL_UNLOCK(sc);
307683825b71SWarner Losh 		break;
307783825b71SWarner Losh 	default:
307883825b71SWarner Losh 		error = ether_ioctl(ifp, command, data);
307983825b71SWarner Losh 		break;
308083825b71SWarner Losh 	}
308183825b71SWarner Losh 
308283825b71SWarner Losh 	return (error);
308383825b71SWarner Losh }
308483825b71SWarner Losh 
308583825b71SWarner Losh static int
xl_watchdog(struct xl_softc * sc)308683825b71SWarner Losh xl_watchdog(struct xl_softc *sc)
308783825b71SWarner Losh {
3088*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
308983825b71SWarner Losh 	u_int16_t		status = 0;
30902b574f31SPyun YongHyeon 	int			misintr;
309183825b71SWarner Losh 
309283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
309383825b71SWarner Losh 
309483825b71SWarner Losh 	if (sc->xl_wdog_timer == 0 || --sc->xl_wdog_timer != 0)
309583825b71SWarner Losh 		return (0);
309683825b71SWarner Losh 
30972b574f31SPyun YongHyeon 	xl_rxeof(sc);
30982b574f31SPyun YongHyeon 	xl_txeoc(sc);
30992b574f31SPyun YongHyeon 	misintr = 0;
31002b574f31SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B) {
31012b574f31SPyun YongHyeon 		xl_txeof_90xB(sc);
31022b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_cnt == 0)
31032b574f31SPyun YongHyeon 			misintr++;
31042b574f31SPyun YongHyeon 	} else {
31052b574f31SPyun YongHyeon 		xl_txeof(sc);
31062b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_head == NULL)
31072b574f31SPyun YongHyeon 			misintr++;
31082b574f31SPyun YongHyeon 	}
31092b574f31SPyun YongHyeon 	if (misintr != 0) {
31102b574f31SPyun YongHyeon 		device_printf(sc->xl_dev,
31112b574f31SPyun YongHyeon 		    "watchdog timeout (missed Tx interrupts) -- recovering\n");
31122b574f31SPyun YongHyeon 		return (0);
31132b574f31SPyun YongHyeon 	}
31142b574f31SPyun YongHyeon 
3115ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
311683825b71SWarner Losh 	XL_SEL_WIN(4);
311783825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
311883825b71SWarner Losh 	device_printf(sc->xl_dev, "watchdog timeout\n");
311983825b71SWarner Losh 
312083825b71SWarner Losh 	if (status & XL_MEDIASTAT_CARRIER)
312183825b71SWarner Losh 		device_printf(sc->xl_dev,
312283825b71SWarner Losh 		    "no carrier - transceiver cable problem?\n");
312383825b71SWarner Losh 
3124*759ad4ddSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
312583825b71SWarner Losh 	xl_init_locked(sc);
312683825b71SWarner Losh 
3127*759ad4ddSJustin Hibbits 	if (!if_sendq_empty(ifp)) {
312883825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
312983825b71SWarner Losh 			xl_start_90xB_locked(ifp);
313083825b71SWarner Losh 		else
313183825b71SWarner Losh 			xl_start_locked(ifp);
313283825b71SWarner Losh 	}
313383825b71SWarner Losh 
313483825b71SWarner Losh 	return (EJUSTRETURN);
313583825b71SWarner Losh }
313683825b71SWarner Losh 
313783825b71SWarner Losh /*
313883825b71SWarner Losh  * Stop the adapter and free any mbufs allocated to the
313983825b71SWarner Losh  * RX and TX lists.
314083825b71SWarner Losh  */
314183825b71SWarner Losh static void
xl_stop(struct xl_softc * sc)314283825b71SWarner Losh xl_stop(struct xl_softc *sc)
314383825b71SWarner Losh {
31443e85b721SEd Maste 	int			i;
3145*759ad4ddSJustin Hibbits 	if_t			ifp = sc->xl_ifp;
314683825b71SWarner Losh 
314783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
314883825b71SWarner Losh 
314983825b71SWarner Losh 	sc->xl_wdog_timer = 0;
315083825b71SWarner Losh 
315183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
315283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
315383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
315483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
315583825b71SWarner Losh 	xl_wait(sc);
315683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
315783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
315883825b71SWarner Losh 	DELAY(800);
315983825b71SWarner Losh 
316083825b71SWarner Losh #ifdef foo
316183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
316283825b71SWarner Losh 	xl_wait(sc);
316383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
316483825b71SWarner Losh 	xl_wait(sc);
316583825b71SWarner Losh #endif
316683825b71SWarner Losh 
316783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
316883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
316983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
317083825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
317183825b71SWarner Losh 		bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
317283825b71SWarner Losh 
317383825b71SWarner Losh 	/* Stop the stats updater. */
317448dcbc33SPyun YongHyeon 	callout_stop(&sc->xl_tick_callout);
317583825b71SWarner Losh 
317683825b71SWarner Losh 	/*
317783825b71SWarner Losh 	 * Free data in the RX lists.
317883825b71SWarner Losh 	 */
317983825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
318083825b71SWarner Losh 		if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) {
318183825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
318283825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
318383825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
318483825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
318583825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf);
318683825b71SWarner Losh 			sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL;
318783825b71SWarner Losh 		}
318883825b71SWarner Losh 	}
318983825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_list != NULL)
319083825b71SWarner Losh 		bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
319183825b71SWarner Losh 	/*
319283825b71SWarner Losh 	 * Free the TX list buffers.
319383825b71SWarner Losh 	 */
319483825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
319583825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) {
319683825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
319783825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
319883825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
319983825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
320083825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf);
320183825b71SWarner Losh 			sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL;
320283825b71SWarner Losh 		}
320383825b71SWarner Losh 	}
320483825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_list != NULL)
320583825b71SWarner Losh 		bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
320683825b71SWarner Losh 
3207*759ad4ddSJustin Hibbits 	if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
320883825b71SWarner Losh }
320983825b71SWarner Losh 
321083825b71SWarner Losh /*
321183825b71SWarner Losh  * Stop all chip I/O so that the kernel's probe routines don't
321283825b71SWarner Losh  * get confused by errant DMAs when rebooting.
321383825b71SWarner Losh  */
321483825b71SWarner Losh static int
xl_shutdown(device_t dev)321583825b71SWarner Losh xl_shutdown(device_t dev)
321683825b71SWarner Losh {
321783825b71SWarner Losh 
32189ae11bbaSPyun YongHyeon 	return (xl_suspend(dev));
321983825b71SWarner Losh }
322083825b71SWarner Losh 
322183825b71SWarner Losh static int
xl_suspend(device_t dev)322283825b71SWarner Losh xl_suspend(device_t dev)
322383825b71SWarner Losh {
322483825b71SWarner Losh 	struct xl_softc		*sc;
322583825b71SWarner Losh 
322683825b71SWarner Losh 	sc = device_get_softc(dev);
322783825b71SWarner Losh 
322883825b71SWarner Losh 	XL_LOCK(sc);
322983825b71SWarner Losh 	xl_stop(sc);
32309ae11bbaSPyun YongHyeon 	xl_setwol(sc);
323183825b71SWarner Losh 	XL_UNLOCK(sc);
323283825b71SWarner Losh 
323383825b71SWarner Losh 	return (0);
323483825b71SWarner Losh }
323583825b71SWarner Losh 
323683825b71SWarner Losh static int
xl_resume(device_t dev)323783825b71SWarner Losh xl_resume(device_t dev)
323883825b71SWarner Losh {
323983825b71SWarner Losh 	struct xl_softc		*sc;
3240*759ad4ddSJustin Hibbits 	if_t			ifp;
324183825b71SWarner Losh 
324283825b71SWarner Losh 	sc = device_get_softc(dev);
324383825b71SWarner Losh 	ifp = sc->xl_ifp;
324483825b71SWarner Losh 
324583825b71SWarner Losh 	XL_LOCK(sc);
324683825b71SWarner Losh 
3247*759ad4ddSJustin Hibbits 	if (if_getflags(ifp) & IFF_UP) {
3248*759ad4ddSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
324983825b71SWarner Losh 		xl_init_locked(sc);
325027b031a9SPyun YongHyeon 	}
325183825b71SWarner Losh 
325283825b71SWarner Losh 	XL_UNLOCK(sc);
325383825b71SWarner Losh 
325483825b71SWarner Losh 	return (0);
325583825b71SWarner Losh }
32569ae11bbaSPyun YongHyeon 
32579ae11bbaSPyun YongHyeon static void
xl_setwol(struct xl_softc * sc)32589ae11bbaSPyun YongHyeon xl_setwol(struct xl_softc *sc)
32599ae11bbaSPyun YongHyeon {
3260*759ad4ddSJustin Hibbits 	if_t			ifp;
32619ae11bbaSPyun YongHyeon 	u_int16_t		cfg, pmstat;
32629ae11bbaSPyun YongHyeon 
32639ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) == 0)
32649ae11bbaSPyun YongHyeon 		return;
32659ae11bbaSPyun YongHyeon 
32669ae11bbaSPyun YongHyeon 	ifp = sc->xl_ifp;
32679ae11bbaSPyun YongHyeon 	XL_SEL_WIN(7);
32689ae11bbaSPyun YongHyeon 	/* Clear any pending PME events. */
32699ae11bbaSPyun YongHyeon 	CSR_READ_2(sc, XL_W7_BM_PME);
32709ae11bbaSPyun YongHyeon 	cfg = 0;
3271*759ad4ddSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
32729ae11bbaSPyun YongHyeon 		cfg |= XL_BM_PME_MAGIC;
32739ae11bbaSPyun YongHyeon 	CSR_WRITE_2(sc, XL_W7_BM_PME, cfg);
32749ae11bbaSPyun YongHyeon 	/* Enable RX. */
3275*759ad4ddSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
32769ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
32779ae11bbaSPyun YongHyeon 	/* Request PME. */
32789ae11bbaSPyun YongHyeon 	pmstat = pci_read_config(sc->xl_dev,
32799ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, 2);
3280*759ad4ddSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
32819ae11bbaSPyun YongHyeon 		pmstat |= PCIM_PSTAT_PMEENABLE;
32829ae11bbaSPyun YongHyeon 	else
32839ae11bbaSPyun YongHyeon 		pmstat &= ~PCIM_PSTAT_PMEENABLE;
32849ae11bbaSPyun YongHyeon 	pci_write_config(sc->xl_dev,
32859ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, pmstat, 2);
32869ae11bbaSPyun YongHyeon }
3287