xref: /freebsd/sys/dev/xl/if_xl.c (revision 3e85b721d653d65a5b6ff655ed1551113f0e0d5b)
183825b71SWarner Losh /*-
283825b71SWarner Losh  * Copyright (c) 1997, 1998, 1999
383825b71SWarner Losh  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
483825b71SWarner Losh  *
583825b71SWarner Losh  * Redistribution and use in source and binary forms, with or without
683825b71SWarner Losh  * modification, are permitted provided that the following conditions
783825b71SWarner Losh  * are met:
883825b71SWarner Losh  * 1. Redistributions of source code must retain the above copyright
983825b71SWarner Losh  *    notice, this list of conditions and the following disclaimer.
1083825b71SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
1183825b71SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
1283825b71SWarner Losh  *    documentation and/or other materials provided with the distribution.
1383825b71SWarner Losh  * 3. All advertising materials mentioning features or use of this software
1483825b71SWarner Losh  *    must display the following acknowledgement:
1583825b71SWarner Losh  *	This product includes software developed by Bill Paul.
1683825b71SWarner Losh  * 4. Neither the name of the author nor the names of any co-contributors
1783825b71SWarner Losh  *    may be used to endorse or promote products derived from this software
1883825b71SWarner Losh  *    without specific prior written permission.
1983825b71SWarner Losh  *
2083825b71SWarner Losh  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2183825b71SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2283825b71SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2383825b71SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2483825b71SWarner Losh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2583825b71SWarner Losh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2683825b71SWarner Losh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2783825b71SWarner Losh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2883825b71SWarner Losh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2983825b71SWarner Losh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3083825b71SWarner Losh  * THE POSSIBILITY OF SUCH DAMAGE.
3183825b71SWarner Losh  */
3283825b71SWarner Losh 
3383825b71SWarner Losh #include <sys/cdefs.h>
3483825b71SWarner Losh __FBSDID("$FreeBSD$");
3583825b71SWarner Losh 
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 *);
24183825b71SWarner Losh static void xl_start(struct ifnet *);
24283825b71SWarner Losh static void xl_start_locked(struct ifnet *);
24383825b71SWarner Losh static void xl_start_90xB_locked(struct ifnet *);
24483825b71SWarner Losh static int xl_ioctl(struct ifnet *, 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
2551abcdbd1SAttilio Rao static int xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count);
2561abcdbd1SAttilio Rao static int xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count);
25783825b71SWarner Losh #endif
25883825b71SWarner Losh 
25983825b71SWarner Losh static int xl_ifmedia_upd(struct ifnet *);
26083825b71SWarner Losh static void xl_ifmedia_sts(struct ifnet *, 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 
33083825b71SWarner Losh static devclass_t xl_devclass;
33183825b71SWarner Losh 
332e4029d4cSMarius Strobl DRIVER_MODULE_ORDERED(xl, pci, xl_driver, xl_devclass, NULL, NULL,
333e4029d4cSMarius Strobl     SI_ORDER_ANY);
334e4029d4cSMarius Strobl DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, NULL, NULL);
33583825b71SWarner Losh 
33683825b71SWarner Losh static void
33783825b71SWarner Losh xl_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
33883825b71SWarner Losh {
33983825b71SWarner Losh 	u_int32_t *paddr;
34083825b71SWarner Losh 
34183825b71SWarner Losh 	paddr = arg;
34283825b71SWarner Losh 	*paddr = segs->ds_addr;
34383825b71SWarner Losh }
34483825b71SWarner Losh 
34583825b71SWarner Losh /*
34683825b71SWarner Losh  * Murphy's law says that it's possible the chip can wedge and
34783825b71SWarner Losh  * the 'command in progress' bit may never clear. Hence, we wait
34883825b71SWarner Losh  * only a finite amount of time to avoid getting caught in an
34983825b71SWarner Losh  * infinite loop. Normally this delay routine would be a macro,
35083825b71SWarner Losh  * but it isn't called during normal operation so we can afford
3513cb58987SWarner Losh  * to make it a function.  Suppress warning when card gone.
35283825b71SWarner Losh  */
35383825b71SWarner Losh static void
35483825b71SWarner Losh xl_wait(struct xl_softc *sc)
35583825b71SWarner Losh {
356*3e85b721SEd Maste 	int			i;
35783825b71SWarner Losh 
35883825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
35983825b71SWarner Losh 		if ((CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY) == 0)
36083825b71SWarner Losh 			break;
36183825b71SWarner Losh 	}
36283825b71SWarner Losh 
363aa0ea4afSWarner Losh 	if (i == XL_TIMEOUT && bus_child_present(sc->xl_dev))
36483825b71SWarner Losh 		device_printf(sc->xl_dev, "command never completed!\n");
36583825b71SWarner Losh }
36683825b71SWarner Losh 
36783825b71SWarner Losh /*
36883825b71SWarner Losh  * MII access routines are provided for adapters with external
36983825b71SWarner Losh  * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
37083825b71SWarner Losh  * autoneg logic that's faked up to look like a PHY (3c905B-TX).
37183825b71SWarner Losh  * Note: if you don't perform the MDIO operations just right,
37283825b71SWarner Losh  * it's possible to end up with code that works correctly with
37383825b71SWarner Losh  * some chips/CPUs/processor speeds/bus speeds/etc but not
37483825b71SWarner Losh  * with others.
37583825b71SWarner Losh  */
37683825b71SWarner Losh 
37783825b71SWarner Losh /*
3788c1093fcSMarius Strobl  * Read the MII serial port for the MII bit-bang module.
3798c1093fcSMarius Strobl  */
3808c1093fcSMarius Strobl static uint32_t
3818c1093fcSMarius Strobl xl_mii_bitbang_read(device_t dev)
3828c1093fcSMarius Strobl {
3838c1093fcSMarius Strobl 	struct xl_softc		*sc;
3848c1093fcSMarius Strobl 	uint32_t		val;
3858c1093fcSMarius Strobl 
3868c1093fcSMarius Strobl 	sc = device_get_softc(dev);
3878c1093fcSMarius Strobl 
3888c1093fcSMarius Strobl 	/* We're already in window 4. */
3898c1093fcSMarius Strobl 	val = CSR_READ_2(sc, XL_W4_PHY_MGMT);
3908c1093fcSMarius Strobl 	CSR_BARRIER(sc, XL_W4_PHY_MGMT, 2,
3918c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
3928c1093fcSMarius Strobl 
3938c1093fcSMarius Strobl 	return (val);
3948c1093fcSMarius Strobl }
3958c1093fcSMarius Strobl 
3968c1093fcSMarius Strobl /*
3978c1093fcSMarius Strobl  * Write the MII serial port for the MII bit-bang module.
39883825b71SWarner Losh  */
39983825b71SWarner Losh static void
4008c1093fcSMarius Strobl xl_mii_bitbang_write(device_t dev, uint32_t val)
40183825b71SWarner Losh {
4028c1093fcSMarius Strobl 	struct xl_softc		*sc;
40383825b71SWarner Losh 
4048c1093fcSMarius Strobl 	sc = device_get_softc(dev);
40583825b71SWarner Losh 
4068c1093fcSMarius Strobl 	/* We're already in window 4. */
4078c1093fcSMarius Strobl 	CSR_WRITE_2(sc, XL_W4_PHY_MGMT,	val);
4088c1093fcSMarius Strobl 	CSR_BARRIER(sc, XL_W4_PHY_MGMT, 2,
4098c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
41083825b71SWarner Losh }
41183825b71SWarner Losh 
41283825b71SWarner Losh static int
41383825b71SWarner Losh xl_miibus_readreg(device_t dev, int phy, int reg)
41483825b71SWarner Losh {
41583825b71SWarner Losh 	struct xl_softc		*sc;
41683825b71SWarner Losh 
41783825b71SWarner Losh 	sc = device_get_softc(dev);
41883825b71SWarner Losh 
4198c1093fcSMarius Strobl 	/* Select the window 4. */
4208c1093fcSMarius Strobl 	XL_SEL_WIN(4);
42183825b71SWarner Losh 
4228c1093fcSMarius Strobl 	return (mii_bitbang_readreg(dev, &xl_mii_bitbang_ops, phy, reg));
42383825b71SWarner Losh }
42483825b71SWarner Losh 
42583825b71SWarner Losh static int
42683825b71SWarner Losh xl_miibus_writereg(device_t dev, int phy, int reg, int data)
42783825b71SWarner Losh {
42883825b71SWarner Losh 	struct xl_softc		*sc;
42983825b71SWarner Losh 
43083825b71SWarner Losh 	sc = device_get_softc(dev);
43183825b71SWarner Losh 
4328c1093fcSMarius Strobl 	/* Select the window 4. */
4338c1093fcSMarius Strobl 	XL_SEL_WIN(4);
43483825b71SWarner Losh 
4358c1093fcSMarius Strobl 	mii_bitbang_writereg(dev, &xl_mii_bitbang_ops, phy, reg, data);
43683825b71SWarner Losh 
43783825b71SWarner Losh 	return (0);
43883825b71SWarner Losh }
43983825b71SWarner Losh 
44083825b71SWarner Losh static void
44183825b71SWarner Losh xl_miibus_statchg(device_t dev)
44283825b71SWarner Losh {
44383825b71SWarner Losh 	struct xl_softc		*sc;
44483825b71SWarner Losh 	struct mii_data		*mii;
445aee0e786SPyun YongHyeon 	uint8_t			macctl;
44683825b71SWarner Losh 
44783825b71SWarner Losh 	sc = device_get_softc(dev);
44883825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
44983825b71SWarner Losh 
45083825b71SWarner Losh 	xl_setcfg(sc);
45183825b71SWarner Losh 
45283825b71SWarner Losh 	/* Set ASIC's duplex mode to match the PHY. */
45383825b71SWarner Losh 	XL_SEL_WIN(3);
454aee0e786SPyun YongHyeon 	macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
455aee0e786SPyun YongHyeon 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
456aee0e786SPyun YongHyeon 		macctl |= XL_MACCTRL_DUPLEX;
457aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B) {
458aee0e786SPyun YongHyeon 			if ((IFM_OPTIONS(mii->mii_media_active) &
459aee0e786SPyun YongHyeon 			    IFM_ETH_RXPAUSE) != 0)
460aee0e786SPyun YongHyeon 				macctl |= XL_MACCTRL_FLOW_CONTROL_ENB;
46183825b71SWarner Losh 			else
462aee0e786SPyun YongHyeon 				macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
463aee0e786SPyun YongHyeon 		}
464aee0e786SPyun YongHyeon 	} else {
465aee0e786SPyun YongHyeon 		macctl &= ~XL_MACCTRL_DUPLEX;
466aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B)
467aee0e786SPyun YongHyeon 			macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
468aee0e786SPyun YongHyeon 	}
469aee0e786SPyun YongHyeon 	CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
47083825b71SWarner Losh }
47183825b71SWarner Losh 
47283825b71SWarner Losh /*
47383825b71SWarner Losh  * Special support for the 3c905B-COMBO. This card has 10/100 support
47483825b71SWarner Losh  * plus BNC and AUI ports. This means we will have both an miibus attached
47583825b71SWarner Losh  * plus some non-MII media settings. In order to allow this, we have to
47683825b71SWarner Losh  * add the extra media to the miibus's ifmedia struct, but we can't do
47783825b71SWarner Losh  * that during xl_attach() because the miibus hasn't been attached yet.
47883825b71SWarner Losh  * So instead, we wait until the miibus probe/attach is done, at which
47983825b71SWarner Losh  * point we will get a callback telling is that it's safe to add our
48083825b71SWarner Losh  * extra media.
48183825b71SWarner Losh  */
48283825b71SWarner Losh static void
48383825b71SWarner Losh xl_miibus_mediainit(device_t dev)
48483825b71SWarner Losh {
48583825b71SWarner Losh 	struct xl_softc		*sc;
48683825b71SWarner Losh 	struct mii_data		*mii;
48783825b71SWarner Losh 	struct ifmedia		*ifm;
48883825b71SWarner Losh 
48983825b71SWarner Losh 	sc = device_get_softc(dev);
49083825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
49183825b71SWarner Losh 	ifm = &mii->mii_media;
49283825b71SWarner Losh 
49383825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI | XL_MEDIAOPT_10FL)) {
49483825b71SWarner Losh 		/*
49583825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
49683825b71SWarner Losh 		 */
49783825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
49883825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
49983825b71SWarner Losh 			if (bootverbose)
50083825b71SWarner Losh 				device_printf(sc->xl_dev, "found 10baseFL\n");
50183825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL, 0, NULL);
50283825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL|IFM_HDX, 0,
50383825b71SWarner Losh 			    NULL);
50483825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
50583825b71SWarner Losh 				ifmedia_add(ifm,
50683825b71SWarner Losh 				    IFM_ETHER | IFM_10_FL | IFM_FDX, 0, NULL);
50783825b71SWarner Losh 		} else {
50883825b71SWarner Losh 			if (bootverbose)
50983825b71SWarner Losh 				device_printf(sc->xl_dev, "found AUI\n");
51083825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_5, 0, NULL);
51183825b71SWarner Losh 		}
51283825b71SWarner Losh 	}
51383825b71SWarner Losh 
51483825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
51583825b71SWarner Losh 		if (bootverbose)
51683825b71SWarner Losh 			device_printf(sc->xl_dev, "found BNC\n");
51783825b71SWarner Losh 		ifmedia_add(ifm, IFM_ETHER | IFM_10_2, 0, NULL);
51883825b71SWarner Losh 	}
51983825b71SWarner Losh }
52083825b71SWarner Losh 
52183825b71SWarner Losh /*
52283825b71SWarner Losh  * The EEPROM is slow: give it time to come ready after issuing
52383825b71SWarner Losh  * it a command.
52483825b71SWarner Losh  */
52583825b71SWarner Losh static int
52683825b71SWarner Losh xl_eeprom_wait(struct xl_softc *sc)
52783825b71SWarner Losh {
52883825b71SWarner Losh 	int			i;
52983825b71SWarner Losh 
53083825b71SWarner Losh 	for (i = 0; i < 100; i++) {
53183825b71SWarner Losh 		if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
53283825b71SWarner Losh 			DELAY(162);
53383825b71SWarner Losh 		else
53483825b71SWarner Losh 			break;
53583825b71SWarner Losh 	}
53683825b71SWarner Losh 
53783825b71SWarner Losh 	if (i == 100) {
53883825b71SWarner Losh 		device_printf(sc->xl_dev, "eeprom failed to come ready\n");
53983825b71SWarner Losh 		return (1);
54083825b71SWarner Losh 	}
54183825b71SWarner Losh 
54283825b71SWarner Losh 	return (0);
54383825b71SWarner Losh }
54483825b71SWarner Losh 
54583825b71SWarner Losh /*
54683825b71SWarner Losh  * Read a sequence of words from the EEPROM. Note that ethernet address
54783825b71SWarner Losh  * data is stored in the EEPROM in network byte order.
54883825b71SWarner Losh  */
54983825b71SWarner Losh static int
55083825b71SWarner Losh xl_read_eeprom(struct xl_softc *sc, caddr_t dest, int off, int cnt, int swap)
55183825b71SWarner Losh {
55283825b71SWarner Losh 	int			err = 0, i;
55383825b71SWarner Losh 	u_int16_t		word = 0, *ptr;
55483825b71SWarner Losh 
55583825b71SWarner Losh #define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
55683825b71SWarner Losh #define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
55783825b71SWarner Losh 	/*
55883825b71SWarner Losh 	 * XXX: WARNING! DANGER!
55983825b71SWarner Losh 	 * It's easy to accidentally overwrite the rom content!
56083825b71SWarner Losh 	 * Note: the 3c575 uses 8bit EEPROM offsets.
56183825b71SWarner Losh 	 */
56283825b71SWarner Losh 	XL_SEL_WIN(0);
56383825b71SWarner Losh 
56483825b71SWarner Losh 	if (xl_eeprom_wait(sc))
56583825b71SWarner Losh 		return (1);
56683825b71SWarner Losh 
56783825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
56883825b71SWarner Losh 		off += 0x30;
56983825b71SWarner Losh 
57083825b71SWarner Losh 	for (i = 0; i < cnt; i++) {
57183825b71SWarner Losh 		if (sc->xl_flags & XL_FLAG_8BITROM)
57283825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
57383825b71SWarner Losh 			    XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
57483825b71SWarner Losh 		else
57583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
57683825b71SWarner Losh 			    XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
57783825b71SWarner Losh 		err = xl_eeprom_wait(sc);
57883825b71SWarner Losh 		if (err)
57983825b71SWarner Losh 			break;
58083825b71SWarner Losh 		word = CSR_READ_2(sc, XL_W0_EE_DATA);
58183825b71SWarner Losh 		ptr = (u_int16_t *)(dest + (i * 2));
58283825b71SWarner Losh 		if (swap)
58383825b71SWarner Losh 			*ptr = ntohs(word);
58483825b71SWarner Losh 		else
58583825b71SWarner Losh 			*ptr = word;
58683825b71SWarner Losh 	}
58783825b71SWarner Losh 
58883825b71SWarner Losh 	return (err ? 1 : 0);
58983825b71SWarner Losh }
59083825b71SWarner Losh 
591dd0a7688SPyun YongHyeon static void
592dd0a7688SPyun YongHyeon xl_rxfilter(struct xl_softc *sc)
593dd0a7688SPyun YongHyeon {
594dd0a7688SPyun YongHyeon 
595dd0a7688SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B)
596dd0a7688SPyun YongHyeon 		xl_rxfilter_90xB(sc);
597dd0a7688SPyun YongHyeon 	else
598dd0a7688SPyun YongHyeon 		xl_rxfilter_90x(sc);
599dd0a7688SPyun YongHyeon }
600dd0a7688SPyun YongHyeon 
60183825b71SWarner Losh /*
60283825b71SWarner Losh  * NICs older than the 3c905B have only one multicast option, which
60383825b71SWarner Losh  * is to enable reception of all multicast frames.
60483825b71SWarner Losh  */
60583825b71SWarner Losh static void
606dd0a7688SPyun YongHyeon xl_rxfilter_90x(struct xl_softc *sc)
60783825b71SWarner Losh {
608dd0a7688SPyun YongHyeon 	struct ifnet		*ifp;
60983825b71SWarner Losh 	struct ifmultiaddr	*ifma;
61083825b71SWarner Losh 	u_int8_t		rxfilt;
61183825b71SWarner Losh 
61283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
61383825b71SWarner Losh 
614dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
615dd0a7688SPyun YongHyeon 
61683825b71SWarner Losh 	XL_SEL_WIN(5);
61783825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
618dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
619dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL);
62083825b71SWarner Losh 
621dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
622dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
623dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
624dd0a7688SPyun YongHyeon 	if (ifp->if_flags & IFF_BROADCAST)
625dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
626dd0a7688SPyun YongHyeon 
627dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
628dd0a7688SPyun YongHyeon 	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
629dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_PROMISC)
630dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
631dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_ALLMULTI)
63283825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
633dd0a7688SPyun YongHyeon 	} else {
634dd0a7688SPyun YongHyeon 		if_maddr_rlock(ifp);
635dd0a7688SPyun YongHyeon 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
636dd0a7688SPyun YongHyeon 			if (ifma->ifma_addr->sa_family != AF_LINK)
637dd0a7688SPyun YongHyeon 				continue;
638dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLMULTI;
639dd0a7688SPyun YongHyeon 			break;
640dd0a7688SPyun YongHyeon 		}
641dd0a7688SPyun YongHyeon 		if_maddr_runlock(ifp);
64283825b71SWarner Losh 	}
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.
65083825b71SWarner Losh  */
65183825b71SWarner Losh static void
652dd0a7688SPyun YongHyeon xl_rxfilter_90xB(struct xl_softc *sc)
65383825b71SWarner Losh {
654dd0a7688SPyun YongHyeon 	struct ifnet		*ifp;
65583825b71SWarner Losh 	struct ifmultiaddr	*ifma;
656dd0a7688SPyun YongHyeon 	int			i, mcnt;
657dd0a7688SPyun YongHyeon 	u_int16_t		h;
65883825b71SWarner Losh 	u_int8_t		rxfilt;
65983825b71SWarner Losh 
66083825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
66183825b71SWarner Losh 
662dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
663dd0a7688SPyun YongHyeon 
66483825b71SWarner Losh 	XL_SEL_WIN(5);
66583825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
666dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
667dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL |
668dd0a7688SPyun YongHyeon 	    XL_RXFILTER_MULTIHASH);
66983825b71SWarner Losh 
670dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
671dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
672dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
673dd0a7688SPyun YongHyeon 	if (ifp->if_flags & IFF_BROADCAST)
674dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
675dd0a7688SPyun YongHyeon 
676dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
677dd0a7688SPyun YongHyeon 	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
678dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_PROMISC)
679dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
680dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_ALLMULTI)
68183825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
682dd0a7688SPyun YongHyeon 	} else {
683dd0a7688SPyun YongHyeon 		/* First, zot all the existing hash bits. */
68483825b71SWarner Losh 		for (i = 0; i < XL_HASHFILT_SIZE; i++)
68583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH | i);
68683825b71SWarner Losh 
687dd0a7688SPyun YongHyeon 		/* Now program new ones. */
688dd0a7688SPyun YongHyeon 		mcnt = 0;
689eb956cd0SRobert Watson 		if_maddr_rlock(ifp);
69083825b71SWarner Losh 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
69183825b71SWarner Losh 			if (ifma->ifma_addr->sa_family != AF_LINK)
69283825b71SWarner Losh 				continue;
69383825b71SWarner Losh 			/*
694dd0a7688SPyun YongHyeon 			 * Note: the 3c905B currently only supports a 64-bit
695dd0a7688SPyun YongHyeon 			 * hash table, which means we really only need 6 bits,
696dd0a7688SPyun YongHyeon 			 * but the manual indicates that future chip revisions
697dd0a7688SPyun YongHyeon 			 * will have a 256-bit hash table, hence the routine
698dd0a7688SPyun YongHyeon 			 * is set up to calculate 8 bits of position info in
699dd0a7688SPyun YongHyeon 			 * case we need it some day.
700dd0a7688SPyun YongHyeon 			 * Note II, The Sequel: _CURRENT_ versions of the
701dd0a7688SPyun YongHyeon 			 * 3c905B have a 256 bit hash table. This means we have
702dd0a7688SPyun YongHyeon 			 * to use all 8 bits regardless.  On older cards, the
703dd0a7688SPyun YongHyeon 			 * upper 2 bits will be ignored. Grrrr....
70483825b71SWarner Losh 			 */
70583825b71SWarner Losh 			h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
70683825b71SWarner Losh 			    ifma->ifma_addr), ETHER_ADDR_LEN) & 0xFF;
70783825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
70883825b71SWarner Losh 			    h | XL_CMD_RX_SET_HASH | XL_HASH_SET);
70983825b71SWarner Losh 			mcnt++;
71083825b71SWarner Losh 		}
711eb956cd0SRobert Watson 		if_maddr_runlock(ifp);
712dd0a7688SPyun YongHyeon 		if (mcnt > 0)
71383825b71SWarner Losh 			rxfilt |= XL_RXFILTER_MULTIHASH;
714dd0a7688SPyun YongHyeon 	}
71583825b71SWarner Losh 
71683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
717dd0a7688SPyun YongHyeon 	XL_SEL_WIN(7);
71883825b71SWarner Losh }
71983825b71SWarner Losh 
72083825b71SWarner Losh static void
72183825b71SWarner Losh xl_setcfg(struct xl_softc *sc)
72283825b71SWarner Losh {
72383825b71SWarner Losh 	u_int32_t		icfg;
72483825b71SWarner Losh 
72583825b71SWarner Losh 	/*XL_LOCK_ASSERT(sc);*/
72683825b71SWarner Losh 
72783825b71SWarner Losh 	XL_SEL_WIN(3);
72883825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
72983825b71SWarner Losh 	icfg &= ~XL_ICFG_CONNECTOR_MASK;
73083825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
73183825b71SWarner Losh 		sc->xl_media & XL_MEDIAOPT_BT4)
73283825b71SWarner Losh 		icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
73383825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BTX)
73483825b71SWarner Losh 		icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
73583825b71SWarner Losh 
73683825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
73783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
73883825b71SWarner Losh }
73983825b71SWarner Losh 
74083825b71SWarner Losh static void
74183825b71SWarner Losh xl_setmode(struct xl_softc *sc, int media)
74283825b71SWarner Losh {
74383825b71SWarner Losh 	u_int32_t		icfg;
74483825b71SWarner Losh 	u_int16_t		mediastat;
74583825b71SWarner Losh 	char			*pmsg = "", *dmsg = "";
74683825b71SWarner Losh 
74783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
74883825b71SWarner Losh 
74983825b71SWarner Losh 	XL_SEL_WIN(4);
75083825b71SWarner Losh 	mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
75183825b71SWarner Losh 	XL_SEL_WIN(3);
75283825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
75383825b71SWarner Losh 
75483825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
75583825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_T) {
75683825b71SWarner Losh 			pmsg = "10baseT transceiver";
75783825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_10BT;
75883825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
75983825b71SWarner Losh 			icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
76083825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT |
76183825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD;
76283825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
76383825b71SWarner Losh 		}
76483825b71SWarner Losh 	}
76583825b71SWarner Losh 
76683825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
76783825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_100_FX) {
76883825b71SWarner Losh 			pmsg = "100baseFX port";
76983825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_100BFX;
77083825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
77183825b71SWarner Losh 			icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
77283825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT;
77383825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
77483825b71SWarner Losh 		}
77583825b71SWarner Losh 	}
77683825b71SWarner Losh 
77783825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
77883825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_5) {
77983825b71SWarner Losh 			pmsg = "AUI port";
78083825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
78183825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
78283825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
78383825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
78483825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
78583825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
78683825b71SWarner Losh 		}
78783825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_FL) {
78883825b71SWarner Losh 			pmsg = "10baseFL transceiver";
78983825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
79083825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
79183825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
79283825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
79383825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
79483825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
79583825b71SWarner Losh 		}
79683825b71SWarner Losh 	}
79783825b71SWarner Losh 
79883825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
79983825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_2) {
80083825b71SWarner Losh 			pmsg = "AUI port";
80183825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_COAX;
80283825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
80383825b71SWarner Losh 			icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
80483825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
80583825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD | XL_MEDIASTAT_SQEENB);
80683825b71SWarner Losh 		}
80783825b71SWarner Losh 	}
80883825b71SWarner Losh 
80983825b71SWarner Losh 	if ((media & IFM_GMASK) == IFM_FDX ||
81083825b71SWarner Losh 			IFM_SUBTYPE(media) == IFM_100_FX) {
81183825b71SWarner Losh 		dmsg = "full";
81283825b71SWarner Losh 		XL_SEL_WIN(3);
81383825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
81483825b71SWarner Losh 	} else {
81583825b71SWarner Losh 		dmsg = "half";
81683825b71SWarner Losh 		XL_SEL_WIN(3);
81783825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
81883825b71SWarner Losh 			(CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
81983825b71SWarner Losh 	}
82083825b71SWarner Losh 
82183825b71SWarner Losh 	if (IFM_SUBTYPE(media) == IFM_10_2)
82283825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
82383825b71SWarner Losh 	else
82483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
82583825b71SWarner Losh 
82683825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
82783825b71SWarner Losh 	XL_SEL_WIN(4);
82883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
82983825b71SWarner Losh 
83083825b71SWarner Losh 	DELAY(800);
83183825b71SWarner Losh 	XL_SEL_WIN(7);
83283825b71SWarner Losh 
83383825b71SWarner Losh 	device_printf(sc->xl_dev, "selecting %s, %s duplex\n", pmsg, dmsg);
83483825b71SWarner Losh }
83583825b71SWarner Losh 
83683825b71SWarner Losh static void
83783825b71SWarner Losh xl_reset(struct xl_softc *sc)
83883825b71SWarner Losh {
839*3e85b721SEd Maste 	int			i;
84083825b71SWarner Losh 
84183825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
84283825b71SWarner Losh 
84383825b71SWarner Losh 	XL_SEL_WIN(0);
84483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
84583825b71SWarner Losh 	    ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
84683825b71SWarner Losh 	     XL_RESETOPT_DISADVFD:0));
84783825b71SWarner Losh 
84883825b71SWarner Losh 	/*
84983825b71SWarner Losh 	 * If we're using memory mapped register mode, pause briefly
85083825b71SWarner Losh 	 * after issuing the reset command before trying to access any
851f33a1c16SWarner Losh 	 * other registers. With my 3c575C CardBus card, failing to do
85283825b71SWarner Losh 	 * this results in the system locking up while trying to poll
85383825b71SWarner Losh 	 * the command busy bit in the status register.
85483825b71SWarner Losh 	 */
85583825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO)
85683825b71SWarner Losh 		DELAY(100000);
85783825b71SWarner Losh 
85883825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
85983825b71SWarner Losh 		DELAY(10);
86083825b71SWarner Losh 		if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
86183825b71SWarner Losh 			break;
86283825b71SWarner Losh 	}
86383825b71SWarner Losh 
86483825b71SWarner Losh 	if (i == XL_TIMEOUT)
86583825b71SWarner Losh 		device_printf(sc->xl_dev, "reset didn't complete\n");
86683825b71SWarner Losh 
86783825b71SWarner Losh 	/* Reset TX and RX. */
86883825b71SWarner Losh 	/* Note: the RX reset takes an absurd amount of time
86983825b71SWarner Losh 	 * on newer versions of the Tornado chips such as those
87083825b71SWarner Losh 	 * on the 3c905CX and newer 3c908C cards. We wait an
87183825b71SWarner Losh 	 * extra amount of time so that xl_wait() doesn't complain
87283825b71SWarner Losh 	 * and annoy the users.
87383825b71SWarner Losh 	 */
87483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
87583825b71SWarner Losh 	DELAY(100000);
87683825b71SWarner Losh 	xl_wait(sc);
87783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
87883825b71SWarner Losh 	xl_wait(sc);
87983825b71SWarner Losh 
88083825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
88183825b71SWarner Losh 	    sc->xl_flags & XL_FLAG_INVERT_MII_PWR) {
88283825b71SWarner Losh 		XL_SEL_WIN(2);
88383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS,
88483825b71SWarner Losh 		    CSR_READ_2(sc, XL_W2_RESET_OPTIONS) |
88583825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR) ?
88683825b71SWarner Losh 		    XL_RESETOPT_INVERT_LED : 0) |
88783825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR) ?
88883825b71SWarner Losh 		    XL_RESETOPT_INVERT_MII : 0));
88983825b71SWarner Losh 	}
89083825b71SWarner Losh 
89183825b71SWarner Losh 	/* Wait a little while for the chip to get its brains in order. */
89283825b71SWarner Losh 	DELAY(100000);
89383825b71SWarner Losh }
89483825b71SWarner Losh 
89583825b71SWarner Losh /*
89683825b71SWarner Losh  * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device
89783825b71SWarner Losh  * IDs against our list and return a device name if we find a match.
89883825b71SWarner Losh  */
89983825b71SWarner Losh static int
90083825b71SWarner Losh xl_probe(device_t dev)
90183825b71SWarner Losh {
90283825b71SWarner Losh 	const struct xl_type	*t;
90383825b71SWarner Losh 
90483825b71SWarner Losh 	t = xl_devs;
90583825b71SWarner Losh 
90683825b71SWarner Losh 	while (t->xl_name != NULL) {
90783825b71SWarner Losh 		if ((pci_get_vendor(dev) == t->xl_vid) &&
90883825b71SWarner Losh 		    (pci_get_device(dev) == t->xl_did)) {
90983825b71SWarner Losh 			device_set_desc(dev, t->xl_name);
91083825b71SWarner Losh 			return (BUS_PROBE_DEFAULT);
91183825b71SWarner Losh 		}
91283825b71SWarner Losh 		t++;
91383825b71SWarner Losh 	}
91483825b71SWarner Losh 
91583825b71SWarner Losh 	return (ENXIO);
91683825b71SWarner Losh }
91783825b71SWarner Losh 
91883825b71SWarner Losh /*
91983825b71SWarner Losh  * This routine is a kludge to work around possible hardware faults
92083825b71SWarner Losh  * or manufacturing defects that can cause the media options register
92183825b71SWarner Losh  * (or reset options register, as it's called for the first generation
92283825b71SWarner Losh  * 3c90x adapters) to return an incorrect result. I have encountered
92383825b71SWarner Losh  * one Dell Latitude laptop docking station with an integrated 3c905-TX
92483825b71SWarner Losh  * which doesn't have any of the 'mediaopt' bits set. This screws up
92583825b71SWarner Losh  * the attach routine pretty badly because it doesn't know what media
92683825b71SWarner Losh  * to look for. If we find ourselves in this predicament, this routine
92783825b71SWarner Losh  * will try to guess the media options values and warn the user of a
92883825b71SWarner Losh  * possible manufacturing defect with his adapter/system/whatever.
92983825b71SWarner Losh  */
93083825b71SWarner Losh static void
93183825b71SWarner Losh xl_mediacheck(struct xl_softc *sc)
93283825b71SWarner Losh {
93383825b71SWarner Losh 
93483825b71SWarner Losh 	/*
93583825b71SWarner Losh 	 * If some of the media options bits are set, assume they are
93683825b71SWarner Losh 	 * correct. If not, try to figure it out down below.
93783825b71SWarner Losh 	 * XXX I should check for 10baseFL, but I don't have an adapter
93883825b71SWarner Losh 	 * to test with.
93983825b71SWarner Losh 	 */
94083825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
94183825b71SWarner Losh 		/*
94283825b71SWarner Losh 		 * Check the XCVR value. If it's not in the normal range
94383825b71SWarner Losh 		 * of values, we need to fake it up here.
94483825b71SWarner Losh 		 */
94583825b71SWarner Losh 		if (sc->xl_xcvr <= XL_XCVR_AUTO)
94683825b71SWarner Losh 			return;
94783825b71SWarner Losh 		else {
94883825b71SWarner Losh 			device_printf(sc->xl_dev,
94983825b71SWarner Losh 			    "bogus xcvr value in EEPROM (%x)\n", sc->xl_xcvr);
95083825b71SWarner Losh 			device_printf(sc->xl_dev,
95183825b71SWarner Losh 			    "choosing new default based on card type\n");
95283825b71SWarner Losh 		}
95383825b71SWarner Losh 	} else {
95483825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
95583825b71SWarner Losh 		    sc->xl_media & XL_MEDIAOPT_10FL)
95683825b71SWarner Losh 			return;
95783825b71SWarner Losh 		device_printf(sc->xl_dev,
95883825b71SWarner Losh "WARNING: no media options bits set in the media options register!!\n");
95983825b71SWarner Losh 		device_printf(sc->xl_dev,
96083825b71SWarner Losh "this could be a manufacturing defect in your adapter or system\n");
96183825b71SWarner Losh 		device_printf(sc->xl_dev,
96283825b71SWarner Losh "attempting to guess media type; you should probably consult your vendor\n");
96383825b71SWarner Losh 	}
96483825b71SWarner Losh 
96583825b71SWarner Losh 	xl_choose_xcvr(sc, 1);
96683825b71SWarner Losh }
96783825b71SWarner Losh 
96883825b71SWarner Losh static void
96983825b71SWarner Losh xl_choose_xcvr(struct xl_softc *sc, int verbose)
97083825b71SWarner Losh {
97183825b71SWarner Losh 	u_int16_t		devid;
97283825b71SWarner Losh 
97383825b71SWarner Losh 	/*
97483825b71SWarner Losh 	 * Read the device ID from the EEPROM.
97583825b71SWarner Losh 	 * This is what's loaded into the PCI device ID register, so it has
97683825b71SWarner Losh 	 * to be correct otherwise we wouldn't have gotten this far.
97783825b71SWarner Losh 	 */
97883825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
97983825b71SWarner Losh 
98083825b71SWarner Losh 	switch (devid) {
98183825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT:	/* 3c900-TPO */
98283825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT:		/* 3c900B-TPO */
98383825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
98483825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
98583825b71SWarner Losh 		if (verbose)
98683825b71SWarner Losh 			device_printf(sc->xl_dev,
98783825b71SWarner Losh 			    "guessing 10BaseT transceiver\n");
98883825b71SWarner Losh 		break;
98983825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT_COMBO:	/* 3c900-COMBO */
99083825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_COMBO:	/* 3c900B-COMBO */
99183825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
99283825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
99383825b71SWarner Losh 		if (verbose)
99483825b71SWarner Losh 			device_printf(sc->xl_dev,
99583825b71SWarner Losh 			    "guessing COMBO (AUI/BNC/TP)\n");
99683825b71SWarner Losh 		break;
99783825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_TPC:	/* 3c900B-TPC */
99883825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
99983825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
100083825b71SWarner Losh 		if (verbose)
100183825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing TPC (BNC/TP)\n");
100283825b71SWarner Losh 		break;
100383825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10FL:		/* 3c900B-FL */
100483825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_10FL;
100583825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUI;
100683825b71SWarner Losh 		if (verbose)
100783825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10baseFL\n");
100883825b71SWarner Losh 		break;
100983825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
101083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_555:		/* 3c555 */
101183825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556:		/* 3c556 */
101283825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556B:	/* 3c556B */
101383825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:	/* 3c575TX */
101483825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:	/* 3c575B */
101583825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:	/* 3c575C */
101683825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656:		/* 3c656 */
101783825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656B:	/* 3c656B */
101883825b71SWarner Losh 	case TC_DEVICEID_TORNADO_656C:		/* 3c656C */
101983825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B:	/* 3c920B-EMB */
102083825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B_WNM:	/* 3c920B-EMB-WNM */
102183825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_MII;
102283825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
102383825b71SWarner Losh 		if (verbose)
102483825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing MII\n");
102583825b71SWarner Losh 		break;
102683825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_100BT4:	/* 3c905-T4 */
102783825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100BT4:	/* 3c905B-T4 */
102883825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT4;
102983825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
103083825b71SWarner Losh 		if (verbose)
103183825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 100baseT4/MII\n");
103283825b71SWarner Losh 		break;
103383825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT:	/* 3c905B-TX */
103483825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
103583825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_SERV:	/* 3c980C-TX */
103683825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_SOHO100TX:	/* 3cSOHO100-TX */
103783825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT:	/* 3c905C-TX */
103883825b71SWarner Losh 	case TC_DEVICEID_TORNADO_HOMECONNECT:	/* 3c450-TX */
103983825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX;
104083825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
104183825b71SWarner Losh 		if (verbose)
104283825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10/100 internal\n");
104383825b71SWarner Losh 		break;
104483825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100_COMBO:	/* 3c905B-COMBO */
104583825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
104683825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
104783825b71SWarner Losh 		if (verbose)
104883825b71SWarner Losh 			device_printf(sc->xl_dev,
104983825b71SWarner Losh 			    "guessing 10/100 plus BNC/AUI\n");
105083825b71SWarner Losh 		break;
105183825b71SWarner Losh 	default:
105283825b71SWarner Losh 		device_printf(sc->xl_dev,
105383825b71SWarner Losh 		    "unknown device ID: %x -- defaulting to 10baseT\n", devid);
105483825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
105583825b71SWarner Losh 		break;
105683825b71SWarner Losh 	}
105783825b71SWarner Losh }
105883825b71SWarner Losh 
105983825b71SWarner Losh /*
106083825b71SWarner Losh  * Attach the interface. Allocate softc structures, do ifmedia
106183825b71SWarner Losh  * setup and ethernet/BPF attach.
106283825b71SWarner Losh  */
106383825b71SWarner Losh static int
106483825b71SWarner Losh xl_attach(device_t dev)
106583825b71SWarner Losh {
106683825b71SWarner Losh 	u_char			eaddr[ETHER_ADDR_LEN];
10679ae11bbaSPyun YongHyeon 	u_int16_t		sinfo2, xcvr[2];
106883825b71SWarner Losh 	struct xl_softc		*sc;
106983825b71SWarner Losh 	struct ifnet		*ifp;
10709ae11bbaSPyun YongHyeon 	int			media, pmcap;
10718e5d93dbSMarius Strobl 	int			error = 0, phy, rid, res, unit;
107283825b71SWarner Losh 	uint16_t		did;
107383825b71SWarner Losh 
107483825b71SWarner Losh 	sc = device_get_softc(dev);
107583825b71SWarner Losh 	sc->xl_dev = dev;
107683825b71SWarner Losh 
107783825b71SWarner Losh 	unit = device_get_unit(dev);
107883825b71SWarner Losh 
107983825b71SWarner Losh 	mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
108083825b71SWarner Losh 	    MTX_DEF);
108183825b71SWarner Losh 	ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
108283825b71SWarner Losh 
108383825b71SWarner Losh 	did = pci_get_device(dev);
108483825b71SWarner Losh 
108583825b71SWarner Losh 	sc->xl_flags = 0;
108683825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555)
108783825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
108883825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556 ||
108983825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556B)
109083825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
109183825b71SWarner Losh 		    XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
109283825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
109383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555 ||
109483825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556)
109583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_8BITROM;
109683825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556B)
109783825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
109883825b71SWarner Losh 
109983825b71SWarner Losh 	if (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_FUNCREG;
110483825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575A ||
110583825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575B ||
110683825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575C ||
110783825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B ||
110883825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_656C)
110983825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 |
111083825b71SWarner Losh 		  XL_FLAG_8BITROM;
111183825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656)
111283825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
111383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575B)
111483825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
111583825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575C)
111683825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
111783825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_656C)
111883825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
111983825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656 ||
112083825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B)
112183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
112283825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR;
112383825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_10_100BT_920B ||
112483825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_10_100BT_920B_WNM)
112583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK;
112683825b71SWarner Losh 
112783825b71SWarner Losh 	switch (did) {
112883825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
112983825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:
113083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:
113183825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:
113283825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_MMIO;
113383825b71SWarner Losh 		break;
113483825b71SWarner Losh 	default:
113583825b71SWarner Losh 		break;
113683825b71SWarner Losh 	}
113783825b71SWarner Losh 
113883825b71SWarner Losh 	/*
113983825b71SWarner Losh 	 * Map control/status registers.
114083825b71SWarner Losh 	 */
114183825b71SWarner Losh 	pci_enable_busmaster(dev);
114283825b71SWarner Losh 
114383825b71SWarner Losh 	if ((sc->xl_flags & XL_FLAG_NO_MMIO) == 0) {
114483825b71SWarner Losh 		rid = XL_PCI_LOMEM;
114583825b71SWarner Losh 		res = SYS_RES_MEMORY;
114683825b71SWarner Losh 
114783825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
114883825b71SWarner Losh 	}
114983825b71SWarner Losh 
115083825b71SWarner Losh 	if (sc->xl_res != NULL) {
115183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_USE_MMIO;
115283825b71SWarner Losh 		if (bootverbose)
115383825b71SWarner Losh 			device_printf(dev, "using memory mapped I/O\n");
115483825b71SWarner Losh 	} else {
115583825b71SWarner Losh 		rid = XL_PCI_LOIO;
115683825b71SWarner Losh 		res = SYS_RES_IOPORT;
115783825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
115883825b71SWarner Losh 		if (sc->xl_res == NULL) {
115983825b71SWarner Losh 			device_printf(dev, "couldn't map ports/memory\n");
116083825b71SWarner Losh 			error = ENXIO;
116183825b71SWarner Losh 			goto fail;
116283825b71SWarner Losh 		}
116383825b71SWarner Losh 		if (bootverbose)
116483825b71SWarner Losh 			device_printf(dev, "using port I/O\n");
116583825b71SWarner Losh 	}
116683825b71SWarner Losh 
116783825b71SWarner Losh 	sc->xl_btag = rman_get_bustag(sc->xl_res);
116883825b71SWarner Losh 	sc->xl_bhandle = rman_get_bushandle(sc->xl_res);
116983825b71SWarner Losh 
117083825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG) {
117183825b71SWarner Losh 		rid = XL_PCI_FUNCMEM;
117283825b71SWarner Losh 		sc->xl_fres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
117383825b71SWarner Losh 		    RF_ACTIVE);
117483825b71SWarner Losh 
117583825b71SWarner Losh 		if (sc->xl_fres == NULL) {
117683825b71SWarner Losh 			device_printf(dev, "couldn't map funcreg memory\n");
117783825b71SWarner Losh 			error = ENXIO;
117883825b71SWarner Losh 			goto fail;
117983825b71SWarner Losh 		}
118083825b71SWarner Losh 
118183825b71SWarner Losh 		sc->xl_ftag = rman_get_bustag(sc->xl_fres);
118283825b71SWarner Losh 		sc->xl_fhandle = rman_get_bushandle(sc->xl_fres);
118383825b71SWarner Losh 	}
118483825b71SWarner Losh 
118583825b71SWarner Losh 	/* Allocate interrupt */
118683825b71SWarner Losh 	rid = 0;
118783825b71SWarner Losh 	sc->xl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
118883825b71SWarner Losh 	    RF_SHAREABLE | RF_ACTIVE);
118983825b71SWarner Losh 	if (sc->xl_irq == NULL) {
119083825b71SWarner Losh 		device_printf(dev, "couldn't map interrupt\n");
119183825b71SWarner Losh 		error = ENXIO;
119283825b71SWarner Losh 		goto fail;
119383825b71SWarner Losh 	}
119483825b71SWarner Losh 
119583825b71SWarner Losh 	/* Initialize interface name. */
119683825b71SWarner Losh 	ifp = sc->xl_ifp = if_alloc(IFT_ETHER);
119783825b71SWarner Losh 	if (ifp == NULL) {
119883825b71SWarner Losh 		device_printf(dev, "can not if_alloc()\n");
119983825b71SWarner Losh 		error = ENOSPC;
120083825b71SWarner Losh 		goto fail;
120183825b71SWarner Losh 	}
120283825b71SWarner Losh 	ifp->if_softc = sc;
120383825b71SWarner Losh 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
120483825b71SWarner Losh 
120583825b71SWarner Losh 	/* Reset the adapter. */
120683825b71SWarner Losh 	XL_LOCK(sc);
120783825b71SWarner Losh 	xl_reset(sc);
120883825b71SWarner Losh 	XL_UNLOCK(sc);
120983825b71SWarner Losh 
121083825b71SWarner Losh 	/*
121183825b71SWarner Losh 	 * Get station address from the EEPROM.
121283825b71SWarner Losh 	 */
121383825b71SWarner Losh 	if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) {
121483825b71SWarner Losh 		device_printf(dev, "failed to read station address\n");
121583825b71SWarner Losh 		error = ENXIO;
121683825b71SWarner Losh 		goto fail;
121783825b71SWarner Losh 	}
121883825b71SWarner Losh 
121948dcbc33SPyun YongHyeon 	callout_init_mtx(&sc->xl_tick_callout, &sc->xl_mtx, 0);
122083825b71SWarner Losh 	TASK_INIT(&sc->xl_task, 0, xl_rxeof_task, sc);
122183825b71SWarner Losh 
122283825b71SWarner Losh 	/*
122383825b71SWarner Losh 	 * Now allocate a tag for the DMA descriptor lists and a chunk
122483825b71SWarner Losh 	 * of DMA-able memory based on the tag.  Also obtain the DMA
122583825b71SWarner Losh 	 * addresses of the RX and TX ring, which we'll need later.
122683825b71SWarner Losh 	 * All of our lists are allocated as a contiguous block
122783825b71SWarner Losh 	 * of memory.
122883825b71SWarner Losh 	 */
122983825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
123083825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
123183825b71SWarner Losh 	    XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, NULL, NULL,
123283825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_tag);
123383825b71SWarner Losh 	if (error) {
123483825b71SWarner Losh 		device_printf(dev, "failed to allocate rx dma tag\n");
123583825b71SWarner Losh 		goto fail;
123683825b71SWarner Losh 	}
123783825b71SWarner Losh 
123883825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag,
1239006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT |
1240006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_rx_dmamap);
124183825b71SWarner Losh 	if (error) {
124283825b71SWarner Losh 		device_printf(dev, "no memory for rx list buffers!\n");
124383825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
124483825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
124583825b71SWarner Losh 		goto fail;
124683825b71SWarner Losh 	}
124783825b71SWarner Losh 
124883825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag,
124983825b71SWarner Losh 	    sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list,
125083825b71SWarner Losh 	    XL_RX_LIST_SZ, xl_dma_map_addr,
125183825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT);
125283825b71SWarner Losh 	if (error) {
125383825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the rx ring!\n");
125483825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
125583825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
125683825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
125783825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
125883825b71SWarner Losh 		goto fail;
125983825b71SWarner Losh 	}
126083825b71SWarner Losh 
126183825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
126283825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
126383825b71SWarner Losh 	    XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, NULL, NULL,
126483825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_tag);
126583825b71SWarner Losh 	if (error) {
126683825b71SWarner Losh 		device_printf(dev, "failed to allocate tx dma tag\n");
126783825b71SWarner Losh 		goto fail;
126883825b71SWarner Losh 	}
126983825b71SWarner Losh 
127083825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag,
1271006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT |
1272006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_tx_dmamap);
127383825b71SWarner Losh 	if (error) {
127483825b71SWarner Losh 		device_printf(dev, "no memory for list buffers!\n");
127583825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
127683825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
127783825b71SWarner Losh 		goto fail;
127883825b71SWarner Losh 	}
127983825b71SWarner Losh 
128083825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag,
128183825b71SWarner Losh 	    sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list,
128283825b71SWarner Losh 	    XL_TX_LIST_SZ, xl_dma_map_addr,
128383825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT);
128483825b71SWarner Losh 	if (error) {
128583825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the tx ring!\n");
128683825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
128783825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
128883825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
128983825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
129083825b71SWarner Losh 		goto fail;
129183825b71SWarner Losh 	}
129283825b71SWarner Losh 
129383825b71SWarner Losh 	/*
129483825b71SWarner Losh 	 * Allocate a DMA tag for the mapping of mbufs.
129583825b71SWarner Losh 	 */
129683825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
129783825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
129883825b71SWarner Losh 	    MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0, NULL,
129983825b71SWarner Losh 	    NULL, &sc->xl_mtag);
130083825b71SWarner Losh 	if (error) {
130183825b71SWarner Losh 		device_printf(dev, "failed to allocate mbuf dma tag\n");
130283825b71SWarner Losh 		goto fail;
130383825b71SWarner Losh 	}
130483825b71SWarner Losh 
130583825b71SWarner Losh 	/* We need a spare DMA map for the RX ring. */
130683825b71SWarner Losh 	error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap);
130783825b71SWarner Losh 	if (error)
130883825b71SWarner Losh 		goto fail;
130983825b71SWarner Losh 
131083825b71SWarner Losh 	/*
131183825b71SWarner Losh 	 * Figure out the card type. 3c905B adapters have the
131283825b71SWarner Losh 	 * 'supportsNoTxLength' bit set in the capabilities
131383825b71SWarner Losh 	 * word in the EEPROM.
1314f33a1c16SWarner Losh 	 * Note: my 3c575C CardBus card lies. It returns a value
131583825b71SWarner Losh 	 * of 0x1578 for its capabilities word, which is somewhat
131683825b71SWarner Losh 	 * nonsensical. Another way to distinguish a 3c90x chip
131783825b71SWarner Losh 	 * from a 3c90xB/C chip is to check for the 'supportsLargePackets'
131883825b71SWarner Losh 	 * bit. This will only be set for 3c90x boomerage chips.
131983825b71SWarner Losh 	 */
132083825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
132183825b71SWarner Losh 	if (sc->xl_caps & XL_CAPS_NO_TXLENGTH ||
132283825b71SWarner Losh 	    !(sc->xl_caps & XL_CAPS_LARGE_PKTS))
132383825b71SWarner Losh 		sc->xl_type = XL_TYPE_905B;
132483825b71SWarner Losh 	else
132583825b71SWarner Losh 		sc->xl_type = XL_TYPE_90X;
132683825b71SWarner Losh 
13279ae11bbaSPyun YongHyeon 	/* Check availability of WOL. */
13289ae11bbaSPyun YongHyeon 	if ((sc->xl_caps & XL_CAPS_PWRMGMT) != 0 &&
13293b0a4aefSJohn Baldwin 	    pci_find_cap(dev, PCIY_PMG, &pmcap) == 0) {
13309ae11bbaSPyun YongHyeon 		sc->xl_pmcap = pmcap;
13319ae11bbaSPyun YongHyeon 		sc->xl_flags |= XL_FLAG_WOL;
13329ae11bbaSPyun YongHyeon 		sinfo2 = 0;
13339ae11bbaSPyun YongHyeon 		xl_read_eeprom(sc, (caddr_t)&sinfo2, XL_EE_SOFTINFO2, 1, 0);
13349ae11bbaSPyun YongHyeon 		if ((sinfo2 & XL_SINFO2_AUX_WOL_CON) == 0 && bootverbose)
13359ae11bbaSPyun YongHyeon 			device_printf(dev,
13369ae11bbaSPyun YongHyeon 			    "No auxiliary remote wakeup connector!\n");
13379ae11bbaSPyun YongHyeon 	}
13389ae11bbaSPyun YongHyeon 
133983825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
134083825b71SWarner Losh 	sc->xl_tx_thresh = XL_MIN_FRAMELEN;
134183825b71SWarner Losh 
134283825b71SWarner Losh 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
134383825b71SWarner Losh 	ifp->if_ioctl = xl_ioctl;
134483825b71SWarner Losh 	ifp->if_capabilities = IFCAP_VLAN_MTU;
134583825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
134683825b71SWarner Losh 		ifp->if_hwassist = XL905B_CSUM_FEATURES;
134783825b71SWarner Losh #ifdef XL905B_TXCSUM_BROKEN
134883825b71SWarner Losh 		ifp->if_capabilities |= IFCAP_RXCSUM;
134983825b71SWarner Losh #else
135083825b71SWarner Losh 		ifp->if_capabilities |= IFCAP_HWCSUM;
135183825b71SWarner Losh #endif
135283825b71SWarner Losh 	}
13539ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0)
13549ae11bbaSPyun YongHyeon 		ifp->if_capabilities |= IFCAP_WOL_MAGIC;
135583825b71SWarner Losh 	ifp->if_capenable = ifp->if_capabilities;
135683825b71SWarner Losh #ifdef DEVICE_POLLING
135783825b71SWarner Losh 	ifp->if_capabilities |= IFCAP_POLLING;
135883825b71SWarner Losh #endif
135983825b71SWarner Losh 	ifp->if_start = xl_start;
136083825b71SWarner Losh 	ifp->if_init = xl_init;
136183825b71SWarner Losh 	IFQ_SET_MAXLEN(&ifp->if_snd, XL_TX_LIST_CNT - 1);
136283825b71SWarner Losh 	ifp->if_snd.ifq_drv_maxlen = XL_TX_LIST_CNT - 1;
136383825b71SWarner Losh 	IFQ_SET_READY(&ifp->if_snd);
136483825b71SWarner Losh 
136583825b71SWarner Losh 	/*
136683825b71SWarner Losh 	 * Now we have to see what sort of media we have.
136783825b71SWarner Losh 	 * This includes probing for an MII interace and a
136883825b71SWarner Losh 	 * possible PHY.
136983825b71SWarner Losh 	 */
137083825b71SWarner Losh 	XL_SEL_WIN(3);
137183825b71SWarner Losh 	sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
137283825b71SWarner Losh 	if (bootverbose)
137383825b71SWarner Losh 		device_printf(dev, "media options word: %x\n", sc->xl_media);
137483825b71SWarner Losh 
137583825b71SWarner Losh 	xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
137683825b71SWarner Losh 	sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
137783825b71SWarner Losh 	sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
137883825b71SWarner Losh 	sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
137983825b71SWarner Losh 
138083825b71SWarner Losh 	xl_mediacheck(sc);
138183825b71SWarner Losh 
138283825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
138383825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
138483825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
138583825b71SWarner Losh 		if (bootverbose)
138683825b71SWarner Losh 			device_printf(dev, "found MII/AUTO\n");
138783825b71SWarner Losh 		xl_setcfg(sc);
13888e5d93dbSMarius Strobl 		/*
13898e5d93dbSMarius Strobl 		 * Attach PHYs only at MII address 24 if !XL_FLAG_PHYOK.
13908e5d93dbSMarius Strobl 		 * This is to guard against problems with certain 3Com ASIC
13918e5d93dbSMarius Strobl 		 * revisions that incorrectly map the internal transceiver
13928e5d93dbSMarius Strobl 		 * control registers at all MII addresses.
13938e5d93dbSMarius Strobl 		 */
13948e5d93dbSMarius Strobl 		phy = MII_PHY_ANY;
13958edfedadSMarius Strobl 		if ((sc->xl_flags & XL_FLAG_PHYOK) == 0)
13968e5d93dbSMarius Strobl 			phy = 24;
13978e5d93dbSMarius Strobl 		error = mii_attach(dev, &sc->xl_miibus, ifp, xl_ifmedia_upd,
1398aee0e786SPyun YongHyeon 		    xl_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY,
1399aee0e786SPyun YongHyeon 		    sc->xl_type == XL_TYPE_905B ? MIIF_DOPAUSE : 0);
14008e5d93dbSMarius Strobl 		if (error != 0) {
14018e5d93dbSMarius Strobl 			device_printf(dev, "attaching PHYs failed\n");
140283825b71SWarner Losh 			goto fail;
140383825b71SWarner Losh 		}
140483825b71SWarner Losh 		goto done;
140583825b71SWarner Losh 	}
140683825b71SWarner Losh 
140783825b71SWarner Losh 	/*
140883825b71SWarner Losh 	 * Sanity check. If the user has selected "auto" and this isn't
140983825b71SWarner Losh 	 * a 10/100 card of some kind, we need to force the transceiver
141083825b71SWarner Losh 	 * type to something sane.
141183825b71SWarner Losh 	 */
141283825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_AUTO)
141383825b71SWarner Losh 		xl_choose_xcvr(sc, bootverbose);
141483825b71SWarner Losh 
141583825b71SWarner Losh 	/*
141683825b71SWarner Losh 	 * Do ifmedia setup.
141783825b71SWarner Losh 	 */
141883825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
141983825b71SWarner Losh 		if (bootverbose)
142083825b71SWarner Losh 			device_printf(dev, "found 10baseT\n");
142183825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
142283825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
142383825b71SWarner Losh 		if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
142483825b71SWarner Losh 			ifmedia_add(&sc->ifmedia,
142583825b71SWarner Losh 			    IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
142683825b71SWarner Losh 	}
142783825b71SWarner Losh 
142883825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
142983825b71SWarner Losh 		/*
143083825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
143183825b71SWarner Losh 		 */
143283825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
143383825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
143483825b71SWarner Losh 			if (bootverbose)
143583825b71SWarner Losh 				device_printf(dev, "found 10baseFL\n");
143683825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL);
143783825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX,
143883825b71SWarner Losh 			    0, NULL);
143983825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
144083825b71SWarner Losh 				ifmedia_add(&sc->ifmedia,
144183825b71SWarner Losh 				    IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL);
144283825b71SWarner Losh 		} else {
144383825b71SWarner Losh 			if (bootverbose)
144483825b71SWarner Losh 				device_printf(dev, "found AUI\n");
144583825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
144683825b71SWarner Losh 		}
144783825b71SWarner Losh 	}
144883825b71SWarner Losh 
144983825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
145083825b71SWarner Losh 		if (bootverbose)
145183825b71SWarner Losh 			device_printf(dev, "found BNC\n");
145283825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
145383825b71SWarner Losh 	}
145483825b71SWarner Losh 
145583825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
145683825b71SWarner Losh 		if (bootverbose)
145783825b71SWarner Losh 			device_printf(dev, "found 100baseFX\n");
145883825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL);
145983825b71SWarner Losh 	}
146083825b71SWarner Losh 
146183825b71SWarner Losh 	media = IFM_ETHER|IFM_100_TX|IFM_FDX;
146283825b71SWarner Losh 	xl_choose_media(sc, &media);
146383825b71SWarner Losh 
146483825b71SWarner Losh 	if (sc->xl_miibus == NULL)
146583825b71SWarner Losh 		ifmedia_set(&sc->ifmedia, media);
146683825b71SWarner Losh 
146783825b71SWarner Losh done:
146883825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
146983825b71SWarner Losh 		XL_SEL_WIN(0);
147083825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
147183825b71SWarner Losh 	}
147283825b71SWarner Losh 
147383825b71SWarner Losh 	/*
147483825b71SWarner Losh 	 * Call MI attach routine.
147583825b71SWarner Losh 	 */
147683825b71SWarner Losh 	ether_ifattach(ifp, eaddr);
147783825b71SWarner Losh 
147883825b71SWarner Losh 	error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET | INTR_MPSAFE,
147983825b71SWarner Losh 	    NULL, xl_intr, sc, &sc->xl_intrhand);
148083825b71SWarner Losh 	if (error) {
148183825b71SWarner Losh 		device_printf(dev, "couldn't set up irq\n");
148283825b71SWarner Losh 		ether_ifdetach(ifp);
148383825b71SWarner Losh 		goto fail;
148483825b71SWarner Losh 	}
148583825b71SWarner Losh 
148683825b71SWarner Losh fail:
148783825b71SWarner Losh 	if (error)
148883825b71SWarner Losh 		xl_detach(dev);
148983825b71SWarner Losh 
149083825b71SWarner Losh 	return (error);
149183825b71SWarner Losh }
149283825b71SWarner Losh 
149383825b71SWarner Losh /*
149483825b71SWarner Losh  * Choose a default media.
149583825b71SWarner Losh  * XXX This is a leaf function only called by xl_attach() and
149683825b71SWarner Losh  *     acquires/releases the non-recursible driver mutex to
149783825b71SWarner Losh  *     satisfy lock assertions.
149883825b71SWarner Losh  */
149983825b71SWarner Losh static void
150083825b71SWarner Losh xl_choose_media(struct xl_softc *sc, int *media)
150183825b71SWarner Losh {
150283825b71SWarner Losh 
150383825b71SWarner Losh 	XL_LOCK(sc);
150483825b71SWarner Losh 
150583825b71SWarner Losh 	switch (sc->xl_xcvr) {
150683825b71SWarner Losh 	case XL_XCVR_10BT:
150783825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
150883825b71SWarner Losh 		xl_setmode(sc, *media);
150983825b71SWarner Losh 		break;
151083825b71SWarner Losh 	case XL_XCVR_AUI:
151183825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
151283825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
151383825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_FL;
151483825b71SWarner Losh 			xl_setmode(sc, *media);
151583825b71SWarner Losh 		} else {
151683825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_5;
151783825b71SWarner Losh 			xl_setmode(sc, *media);
151883825b71SWarner Losh 		}
151983825b71SWarner Losh 		break;
152083825b71SWarner Losh 	case XL_XCVR_COAX:
152183825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_2;
152283825b71SWarner Losh 		xl_setmode(sc, *media);
152383825b71SWarner Losh 		break;
152483825b71SWarner Losh 	case XL_XCVR_AUTO:
152583825b71SWarner Losh 	case XL_XCVR_100BTX:
152683825b71SWarner Losh 	case XL_XCVR_MII:
152783825b71SWarner Losh 		/* Chosen by miibus */
152883825b71SWarner Losh 		break;
152983825b71SWarner Losh 	case XL_XCVR_100BFX:
153083825b71SWarner Losh 		*media = IFM_ETHER|IFM_100_FX;
153183825b71SWarner Losh 		break;
153283825b71SWarner Losh 	default:
153383825b71SWarner Losh 		device_printf(sc->xl_dev, "unknown XCVR type: %d\n",
153483825b71SWarner Losh 		    sc->xl_xcvr);
153583825b71SWarner Losh 		/*
153683825b71SWarner Losh 		 * This will probably be wrong, but it prevents
153783825b71SWarner Losh 		 * the ifmedia code from panicking.
153883825b71SWarner Losh 		 */
153983825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
154083825b71SWarner Losh 		break;
154183825b71SWarner Losh 	}
154283825b71SWarner Losh 
154383825b71SWarner Losh 	XL_UNLOCK(sc);
154483825b71SWarner Losh }
154583825b71SWarner Losh 
154683825b71SWarner Losh /*
154783825b71SWarner Losh  * Shutdown hardware and free up resources. This can be called any
154883825b71SWarner Losh  * time after the mutex has been initialized. It is called in both
154983825b71SWarner Losh  * the error case in attach and the normal detach case so it needs
155083825b71SWarner Losh  * to be careful about only freeing resources that have actually been
155183825b71SWarner Losh  * allocated.
155283825b71SWarner Losh  */
155383825b71SWarner Losh static int
155483825b71SWarner Losh xl_detach(device_t dev)
155583825b71SWarner Losh {
155683825b71SWarner Losh 	struct xl_softc		*sc;
155783825b71SWarner Losh 	struct ifnet		*ifp;
155883825b71SWarner Losh 	int			rid, res;
155983825b71SWarner Losh 
156083825b71SWarner Losh 	sc = device_get_softc(dev);
156183825b71SWarner Losh 	ifp = sc->xl_ifp;
156283825b71SWarner Losh 
156383825b71SWarner Losh 	KASSERT(mtx_initialized(&sc->xl_mtx), ("xl mutex not initialized"));
156483825b71SWarner Losh 
156583825b71SWarner Losh #ifdef DEVICE_POLLING
156683825b71SWarner Losh 	if (ifp && ifp->if_capenable & IFCAP_POLLING)
156783825b71SWarner Losh 		ether_poll_deregister(ifp);
156883825b71SWarner Losh #endif
156983825b71SWarner Losh 
157083825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO) {
157183825b71SWarner Losh 		rid = XL_PCI_LOMEM;
157283825b71SWarner Losh 		res = SYS_RES_MEMORY;
157383825b71SWarner Losh 	} else {
157483825b71SWarner Losh 		rid = XL_PCI_LOIO;
157583825b71SWarner Losh 		res = SYS_RES_IOPORT;
157683825b71SWarner Losh 	}
157783825b71SWarner Losh 
157883825b71SWarner Losh 	/* These should only be active if attach succeeded */
157983825b71SWarner Losh 	if (device_is_attached(dev)) {
158083825b71SWarner Losh 		XL_LOCK(sc);
158183825b71SWarner Losh 		xl_stop(sc);
158283825b71SWarner Losh 		XL_UNLOCK(sc);
158383825b71SWarner Losh 		taskqueue_drain(taskqueue_swi, &sc->xl_task);
158448dcbc33SPyun YongHyeon 		callout_drain(&sc->xl_tick_callout);
158583825b71SWarner Losh 		ether_ifdetach(ifp);
158683825b71SWarner Losh 	}
158783825b71SWarner Losh 	if (sc->xl_miibus)
158883825b71SWarner Losh 		device_delete_child(dev, sc->xl_miibus);
158983825b71SWarner Losh 	bus_generic_detach(dev);
159083825b71SWarner Losh 	ifmedia_removeall(&sc->ifmedia);
159183825b71SWarner Losh 
159283825b71SWarner Losh 	if (sc->xl_intrhand)
159383825b71SWarner Losh 		bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
159483825b71SWarner Losh 	if (sc->xl_irq)
159583825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
159683825b71SWarner Losh 	if (sc->xl_fres != NULL)
159783825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_MEMORY,
159883825b71SWarner Losh 		    XL_PCI_FUNCMEM, sc->xl_fres);
159983825b71SWarner Losh 	if (sc->xl_res)
160083825b71SWarner Losh 		bus_release_resource(dev, res, rid, sc->xl_res);
160183825b71SWarner Losh 
160283825b71SWarner Losh 	if (ifp)
160383825b71SWarner Losh 		if_free(ifp);
160483825b71SWarner Losh 
160583825b71SWarner Losh 	if (sc->xl_mtag) {
160683825b71SWarner Losh 		bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap);
160783825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_mtag);
160883825b71SWarner Losh 	}
160983825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_tag) {
161083825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_rx_tag,
161183825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
161283825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
161383825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
161483825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
161583825b71SWarner Losh 	}
161683825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_tag) {
161783825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_tx_tag,
161883825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
161983825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
162083825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
162183825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
162283825b71SWarner Losh 	}
162383825b71SWarner Losh 
162483825b71SWarner Losh 	mtx_destroy(&sc->xl_mtx);
162583825b71SWarner Losh 
162683825b71SWarner Losh 	return (0);
162783825b71SWarner Losh }
162883825b71SWarner Losh 
162983825b71SWarner Losh /*
163083825b71SWarner Losh  * Initialize the transmit descriptors.
163183825b71SWarner Losh  */
163283825b71SWarner Losh static int
163383825b71SWarner Losh xl_list_tx_init(struct xl_softc *sc)
163483825b71SWarner Losh {
163583825b71SWarner Losh 	struct xl_chain_data	*cd;
163683825b71SWarner Losh 	struct xl_list_data	*ld;
163783825b71SWarner Losh 	int			error, i;
163883825b71SWarner Losh 
163983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
164083825b71SWarner Losh 
164183825b71SWarner Losh 	cd = &sc->xl_cdata;
164283825b71SWarner Losh 	ld = &sc->xl_ldata;
164383825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
164483825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
164583825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
164683825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
164783825b71SWarner Losh 		if (error)
164883825b71SWarner Losh 			return (error);
164983825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
165083825b71SWarner Losh 		    i * sizeof(struct xl_list);
165183825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
165283825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = NULL;
165383825b71SWarner Losh 		else
165483825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
165583825b71SWarner Losh 	}
165683825b71SWarner Losh 
165783825b71SWarner Losh 	cd->xl_tx_free = &cd->xl_tx_chain[0];
165883825b71SWarner Losh 	cd->xl_tx_tail = cd->xl_tx_head = NULL;
165983825b71SWarner Losh 
166083825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
166183825b71SWarner Losh 	return (0);
166283825b71SWarner Losh }
166383825b71SWarner Losh 
166483825b71SWarner Losh /*
166583825b71SWarner Losh  * Initialize the transmit descriptors.
166683825b71SWarner Losh  */
166783825b71SWarner Losh static int
166883825b71SWarner Losh xl_list_tx_init_90xB(struct xl_softc *sc)
166983825b71SWarner Losh {
167083825b71SWarner Losh 	struct xl_chain_data	*cd;
167183825b71SWarner Losh 	struct xl_list_data	*ld;
167283825b71SWarner Losh 	int			error, i;
167383825b71SWarner Losh 
167483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
167583825b71SWarner Losh 
167683825b71SWarner Losh 	cd = &sc->xl_cdata;
167783825b71SWarner Losh 	ld = &sc->xl_ldata;
167883825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
167983825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
168083825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
168183825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
168283825b71SWarner Losh 		if (error)
168383825b71SWarner Losh 			return (error);
168483825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
168583825b71SWarner Losh 		    i * sizeof(struct xl_list);
168683825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
168783825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0];
168883825b71SWarner Losh 		else
168983825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
169083825b71SWarner Losh 		if (i == 0)
169183825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
169283825b71SWarner Losh 			    &cd->xl_tx_chain[XL_TX_LIST_CNT - 1];
169383825b71SWarner Losh 		else
169483825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
169583825b71SWarner Losh 			    &cd->xl_tx_chain[i - 1];
169683825b71SWarner Losh 	}
169783825b71SWarner Losh 
169883825b71SWarner Losh 	bzero(ld->xl_tx_list, XL_TX_LIST_SZ);
169983825b71SWarner Losh 	ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY);
170083825b71SWarner Losh 
170183825b71SWarner Losh 	cd->xl_tx_prod = 1;
170283825b71SWarner Losh 	cd->xl_tx_cons = 1;
170383825b71SWarner Losh 	cd->xl_tx_cnt = 0;
170483825b71SWarner Losh 
170583825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
170683825b71SWarner Losh 	return (0);
170783825b71SWarner Losh }
170883825b71SWarner Losh 
170983825b71SWarner Losh /*
171083825b71SWarner Losh  * Initialize the RX descriptors and allocate mbufs for them. Note that
171183825b71SWarner Losh  * we arrange the descriptors in a closed ring, so that the last descriptor
171283825b71SWarner Losh  * points back to the first.
171383825b71SWarner Losh  */
171483825b71SWarner Losh static int
171583825b71SWarner Losh xl_list_rx_init(struct xl_softc *sc)
171683825b71SWarner Losh {
171783825b71SWarner Losh 	struct xl_chain_data	*cd;
171883825b71SWarner Losh 	struct xl_list_data	*ld;
171983825b71SWarner Losh 	int			error, i, next;
172083825b71SWarner Losh 	u_int32_t		nextptr;
172183825b71SWarner Losh 
172283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
172383825b71SWarner Losh 
172483825b71SWarner Losh 	cd = &sc->xl_cdata;
172583825b71SWarner Losh 	ld = &sc->xl_ldata;
172683825b71SWarner Losh 
172783825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
172883825b71SWarner Losh 		cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i];
172983825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
173083825b71SWarner Losh 		    &cd->xl_rx_chain[i].xl_map);
173183825b71SWarner Losh 		if (error)
173283825b71SWarner Losh 			return (error);
173383825b71SWarner Losh 		error = xl_newbuf(sc, &cd->xl_rx_chain[i]);
173483825b71SWarner Losh 		if (error)
173583825b71SWarner Losh 			return (error);
173683825b71SWarner Losh 		if (i == (XL_RX_LIST_CNT - 1))
173783825b71SWarner Losh 			next = 0;
173883825b71SWarner Losh 		else
173983825b71SWarner Losh 			next = i + 1;
174083825b71SWarner Losh 		nextptr = ld->xl_rx_dmaaddr +
174183825b71SWarner Losh 		    next * sizeof(struct xl_list_onefrag);
174283825b71SWarner Losh 		cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next];
174383825b71SWarner Losh 		ld->xl_rx_list[i].xl_next = htole32(nextptr);
174483825b71SWarner Losh 	}
174583825b71SWarner Losh 
174683825b71SWarner Losh 	bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
174783825b71SWarner Losh 	cd->xl_rx_head = &cd->xl_rx_chain[0];
174883825b71SWarner Losh 
174983825b71SWarner Losh 	return (0);
175083825b71SWarner Losh }
175183825b71SWarner Losh 
175283825b71SWarner Losh /*
175383825b71SWarner Losh  * Initialize an RX descriptor and attach an MBUF cluster.
175483825b71SWarner Losh  * If we fail to do so, we need to leave the old mbuf and
175583825b71SWarner Losh  * the old DMA map untouched so that it can be reused.
175683825b71SWarner Losh  */
175783825b71SWarner Losh static int
175883825b71SWarner Losh xl_newbuf(struct xl_softc *sc, struct xl_chain_onefrag *c)
175983825b71SWarner Losh {
176083825b71SWarner Losh 	struct mbuf		*m_new = NULL;
176183825b71SWarner Losh 	bus_dmamap_t		map;
176283825b71SWarner Losh 	bus_dma_segment_t	segs[1];
176383825b71SWarner Losh 	int			error, nseg;
176483825b71SWarner Losh 
176583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
176683825b71SWarner Losh 
1767c6499eccSGleb Smirnoff 	m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
176883825b71SWarner Losh 	if (m_new == NULL)
176983825b71SWarner Losh 		return (ENOBUFS);
177083825b71SWarner Losh 
177183825b71SWarner Losh 	m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
177283825b71SWarner Losh 
177383825b71SWarner Losh 	/* Force longword alignment for packet payload. */
177483825b71SWarner Losh 	m_adj(m_new, ETHER_ALIGN);
177583825b71SWarner Losh 
177683825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, sc->xl_tmpmap, m_new,
177783825b71SWarner Losh 	    segs, &nseg, BUS_DMA_NOWAIT);
177883825b71SWarner Losh 	if (error) {
177983825b71SWarner Losh 		m_freem(m_new);
178083825b71SWarner Losh 		device_printf(sc->xl_dev, "can't map mbuf (error %d)\n",
178183825b71SWarner Losh 		    error);
178283825b71SWarner Losh 		return (error);
178383825b71SWarner Losh 	}
178483825b71SWarner Losh 	KASSERT(nseg == 1,
178583825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
178683825b71SWarner Losh 
178783825b71SWarner Losh 	bus_dmamap_unload(sc->xl_mtag, c->xl_map);
178883825b71SWarner Losh 	map = c->xl_map;
178983825b71SWarner Losh 	c->xl_map = sc->xl_tmpmap;
179083825b71SWarner Losh 	sc->xl_tmpmap = map;
179183825b71SWarner Losh 	c->xl_mbuf = m_new;
179283825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG);
179383825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_addr = htole32(segs->ds_addr);
1794f321edf9SPyun YongHyeon 	c->xl_ptr->xl_status = 0;
179583825b71SWarner Losh 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD);
179683825b71SWarner Losh 	return (0);
179783825b71SWarner Losh }
179883825b71SWarner Losh 
179983825b71SWarner Losh static int
180083825b71SWarner Losh xl_rx_resync(struct xl_softc *sc)
180183825b71SWarner Losh {
180283825b71SWarner Losh 	struct xl_chain_onefrag	*pos;
180383825b71SWarner Losh 	int			i;
180483825b71SWarner Losh 
180583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
180683825b71SWarner Losh 
180783825b71SWarner Losh 	pos = sc->xl_cdata.xl_rx_head;
180883825b71SWarner Losh 
180983825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
181083825b71SWarner Losh 		if (pos->xl_ptr->xl_status)
181183825b71SWarner Losh 			break;
181283825b71SWarner Losh 		pos = pos->xl_next;
181383825b71SWarner Losh 	}
181483825b71SWarner Losh 
181583825b71SWarner Losh 	if (i == XL_RX_LIST_CNT)
181683825b71SWarner Losh 		return (0);
181783825b71SWarner Losh 
181883825b71SWarner Losh 	sc->xl_cdata.xl_rx_head = pos;
181983825b71SWarner Losh 
182083825b71SWarner Losh 	return (EAGAIN);
182183825b71SWarner Losh }
182283825b71SWarner Losh 
182383825b71SWarner Losh /*
182483825b71SWarner Losh  * A frame has been uploaded: pass the resulting mbuf chain up to
182583825b71SWarner Losh  * the higher level protocols.
182683825b71SWarner Losh  */
18271abcdbd1SAttilio Rao static int
182883825b71SWarner Losh xl_rxeof(struct xl_softc *sc)
182983825b71SWarner Losh {
183083825b71SWarner Losh 	struct mbuf		*m;
183183825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
183283825b71SWarner Losh 	struct xl_chain_onefrag	*cur_rx;
18335a13764bSPyun YongHyeon 	int			total_len;
18341abcdbd1SAttilio Rao 	int			rx_npkts = 0;
183583825b71SWarner Losh 	u_int32_t		rxstat;
183683825b71SWarner Losh 
183783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
183883825b71SWarner Losh again:
183983825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap,
184083825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
184183825b71SWarner Losh 	while ((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
184283825b71SWarner Losh #ifdef DEVICE_POLLING
184383825b71SWarner Losh 		if (ifp->if_capenable & IFCAP_POLLING) {
184483825b71SWarner Losh 			if (sc->rxcycles <= 0)
184583825b71SWarner Losh 				break;
184683825b71SWarner Losh 			sc->rxcycles--;
184783825b71SWarner Losh 		}
184883825b71SWarner Losh #endif
184983825b71SWarner Losh 		cur_rx = sc->xl_cdata.xl_rx_head;
185083825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = cur_rx->xl_next;
185183825b71SWarner Losh 		total_len = rxstat & XL_RXSTAT_LENMASK;
18525a13764bSPyun YongHyeon 		rx_npkts++;
185383825b71SWarner Losh 
185483825b71SWarner Losh 		/*
185583825b71SWarner Losh 		 * Since we have told the chip to allow large frames,
185683825b71SWarner Losh 		 * we need to trap giant frame errors in software. We allow
185783825b71SWarner Losh 		 * a little more than the normal frame size to account for
185883825b71SWarner Losh 		 * frames with VLAN tags.
185983825b71SWarner Losh 		 */
186083825b71SWarner Losh 		if (total_len > XL_MAX_FRAMELEN)
186183825b71SWarner Losh 			rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE);
186283825b71SWarner Losh 
186383825b71SWarner Losh 		/*
186483825b71SWarner Losh 		 * If an error occurs, update stats, clear the
186583825b71SWarner Losh 		 * status word and leave the mbuf cluster in place:
186683825b71SWarner Losh 		 * it should simply get re-used next time this descriptor
186783825b71SWarner Losh 		 * comes up in the ring.
186883825b71SWarner Losh 		 */
186983825b71SWarner Losh 		if (rxstat & XL_RXSTAT_UP_ERROR) {
1870ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
187183825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
187283825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
187383825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
187483825b71SWarner Losh 			continue;
187583825b71SWarner Losh 		}
187683825b71SWarner Losh 
187783825b71SWarner Losh 		/*
187883825b71SWarner Losh 		 * If the error bit was not set, the upload complete
187983825b71SWarner Losh 		 * bit should be set which means we have a valid packet.
188083825b71SWarner Losh 		 * If not, something truly strange has happened.
188183825b71SWarner Losh 		 */
188283825b71SWarner Losh 		if (!(rxstat & XL_RXSTAT_UP_CMPLT)) {
188383825b71SWarner Losh 			device_printf(sc->xl_dev,
188483825b71SWarner Losh 			    "bad receive status -- packet dropped\n");
1885ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
188683825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
188783825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
188883825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
188983825b71SWarner Losh 			continue;
189083825b71SWarner Losh 		}
189183825b71SWarner Losh 
189283825b71SWarner Losh 		/* No errors; receive the packet. */
189383825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map,
189483825b71SWarner Losh 		    BUS_DMASYNC_POSTREAD);
189583825b71SWarner Losh 		m = cur_rx->xl_mbuf;
189683825b71SWarner Losh 
189783825b71SWarner Losh 		/*
189883825b71SWarner Losh 		 * Try to conjure up a new mbuf cluster. If that
189983825b71SWarner Losh 		 * fails, it means we have an out of memory condition and
190083825b71SWarner Losh 		 * should leave the buffer in place and continue. This will
190183825b71SWarner Losh 		 * result in a lost packet, but there's little else we
190283825b71SWarner Losh 		 * can do in this situation.
190383825b71SWarner Losh 		 */
190483825b71SWarner Losh 		if (xl_newbuf(sc, cur_rx)) {
1905ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
190683825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
190783825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
190883825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
190983825b71SWarner Losh 			continue;
191083825b71SWarner Losh 		}
191183825b71SWarner Losh 		bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
191283825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
191383825b71SWarner Losh 
1914ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
191583825b71SWarner Losh 		m->m_pkthdr.rcvif = ifp;
191683825b71SWarner Losh 		m->m_pkthdr.len = m->m_len = total_len;
191783825b71SWarner Losh 
191883825b71SWarner Losh 		if (ifp->if_capenable & IFCAP_RXCSUM) {
191983825b71SWarner Losh 			/* Do IP checksum checking. */
192083825b71SWarner Losh 			if (rxstat & XL_RXSTAT_IPCKOK)
192183825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
192283825b71SWarner Losh 			if (!(rxstat & XL_RXSTAT_IPCKERR))
192383825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
192483825b71SWarner Losh 			if ((rxstat & XL_RXSTAT_TCPCOK &&
192583825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_TCPCKERR)) ||
192683825b71SWarner Losh 			    (rxstat & XL_RXSTAT_UDPCKOK &&
192783825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_UDPCKERR))) {
192883825b71SWarner Losh 				m->m_pkthdr.csum_flags |=
192983825b71SWarner Losh 					CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
193083825b71SWarner Losh 				m->m_pkthdr.csum_data = 0xffff;
193183825b71SWarner Losh 			}
193283825b71SWarner Losh 		}
193383825b71SWarner Losh 
193483825b71SWarner Losh 		XL_UNLOCK(sc);
193583825b71SWarner Losh 		(*ifp->if_input)(ifp, m);
193683825b71SWarner Losh 		XL_LOCK(sc);
193783825b71SWarner Losh 
193883825b71SWarner Losh 		/*
193983825b71SWarner Losh 		 * If we are running from the taskqueue, the interface
194083825b71SWarner Losh 		 * might have been stopped while we were passing the last
194183825b71SWarner Losh 		 * packet up the network stack.
194283825b71SWarner Losh 		 */
194383825b71SWarner Losh 		if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
19441abcdbd1SAttilio Rao 			return (rx_npkts);
194583825b71SWarner Losh 	}
194683825b71SWarner Losh 
194783825b71SWarner Losh 	/*
194883825b71SWarner Losh 	 * Handle the 'end of channel' condition. When the upload
194983825b71SWarner Losh 	 * engine hits the end of the RX ring, it will stall. This
195083825b71SWarner Losh 	 * is our cue to flush the RX ring, reload the uplist pointer
195183825b71SWarner Losh 	 * register and unstall the engine.
195283825b71SWarner Losh 	 * XXX This is actually a little goofy. With the ThunderLAN
195383825b71SWarner Losh 	 * chip, you get an interrupt when the receiver hits the end
195483825b71SWarner Losh 	 * of the receive ring, which tells you exactly when you
195583825b71SWarner Losh 	 * you need to reload the ring pointer. Here we have to
195683825b71SWarner Losh 	 * fake it. I'm mad at myself for not being clever enough
195783825b71SWarner Losh 	 * to avoid the use of a goto here.
195883825b71SWarner Losh 	 */
195983825b71SWarner Losh 	if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 ||
196083825b71SWarner Losh 		CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) {
196183825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
196283825b71SWarner Losh 		xl_wait(sc);
196383825b71SWarner Losh 		CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
196483825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0];
196583825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
196683825b71SWarner Losh 		goto again;
196783825b71SWarner Losh 	}
19681abcdbd1SAttilio Rao 	return (rx_npkts);
196983825b71SWarner Losh }
197083825b71SWarner Losh 
197183825b71SWarner Losh /*
197283825b71SWarner Losh  * Taskqueue wrapper for xl_rxeof().
197383825b71SWarner Losh  */
197483825b71SWarner Losh static void
197583825b71SWarner Losh xl_rxeof_task(void *arg, int pending)
197683825b71SWarner Losh {
197783825b71SWarner Losh 	struct xl_softc *sc = (struct xl_softc *)arg;
197883825b71SWarner Losh 
197983825b71SWarner Losh 	XL_LOCK(sc);
198083825b71SWarner Losh 	if (sc->xl_ifp->if_drv_flags & IFF_DRV_RUNNING)
198183825b71SWarner Losh 		xl_rxeof(sc);
198283825b71SWarner Losh 	XL_UNLOCK(sc);
198383825b71SWarner Losh }
198483825b71SWarner Losh 
198583825b71SWarner Losh /*
198683825b71SWarner Losh  * A frame was downloaded to the chip. It's safe for us to clean up
198783825b71SWarner Losh  * the list buffers.
198883825b71SWarner Losh  */
198983825b71SWarner Losh static void
199083825b71SWarner Losh xl_txeof(struct xl_softc *sc)
199183825b71SWarner Losh {
199283825b71SWarner Losh 	struct xl_chain		*cur_tx;
199383825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
199483825b71SWarner Losh 
199583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
199683825b71SWarner Losh 
199783825b71SWarner Losh 	/*
199883825b71SWarner Losh 	 * Go through our tx list and free mbufs for those
199983825b71SWarner Losh 	 * frames that have been uploaded. Note: the 3c905B
200083825b71SWarner Losh 	 * sets a special bit in the status word to let us
200183825b71SWarner Losh 	 * know that a frame has been downloaded, but the
200283825b71SWarner Losh 	 * original 3c900/3c905 adapters don't do that.
200383825b71SWarner Losh 	 * Consequently, we have to use a different test if
200483825b71SWarner Losh 	 * xl_type != XL_TYPE_905B.
200583825b71SWarner Losh 	 */
200683825b71SWarner Losh 	while (sc->xl_cdata.xl_tx_head != NULL) {
200783825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_head;
200883825b71SWarner Losh 
200983825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DOWNLIST_PTR))
201083825b71SWarner Losh 			break;
201183825b71SWarner Losh 
201283825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = cur_tx->xl_next;
201383825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
201483825b71SWarner Losh 		    BUS_DMASYNC_POSTWRITE);
201583825b71SWarner Losh 		bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
201683825b71SWarner Losh 		m_freem(cur_tx->xl_mbuf);
201783825b71SWarner Losh 		cur_tx->xl_mbuf = NULL;
2018ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
2019ba65e0ccSPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
202083825b71SWarner Losh 
202183825b71SWarner Losh 		cur_tx->xl_next = sc->xl_cdata.xl_tx_free;
202283825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx;
202383825b71SWarner Losh 	}
202483825b71SWarner Losh 
202583825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head == NULL) {
202683825b71SWarner Losh 		sc->xl_wdog_timer = 0;
202783825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = NULL;
202883825b71SWarner Losh 	} else {
202983825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED ||
203083825b71SWarner Losh 			!CSR_READ_4(sc, XL_DOWNLIST_PTR)) {
203183825b71SWarner Losh 			CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
203283825b71SWarner Losh 				sc->xl_cdata.xl_tx_head->xl_phys);
203383825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
203483825b71SWarner Losh 		}
203583825b71SWarner Losh 	}
203683825b71SWarner Losh }
203783825b71SWarner Losh 
203883825b71SWarner Losh static void
203983825b71SWarner Losh xl_txeof_90xB(struct xl_softc *sc)
204083825b71SWarner Losh {
204183825b71SWarner Losh 	struct xl_chain		*cur_tx = NULL;
204283825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
204383825b71SWarner Losh 	int			idx;
204483825b71SWarner Losh 
204583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
204683825b71SWarner Losh 
204783825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
204883825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
204983825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_cons;
205083825b71SWarner Losh 	while (idx != sc->xl_cdata.xl_tx_prod) {
205183825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
205283825b71SWarner Losh 
205383825b71SWarner Losh 		if (!(le32toh(cur_tx->xl_ptr->xl_status) &
205483825b71SWarner Losh 		      XL_TXSTAT_DL_COMPLETE))
205583825b71SWarner Losh 			break;
205683825b71SWarner Losh 
205783825b71SWarner Losh 		if (cur_tx->xl_mbuf != NULL) {
205883825b71SWarner Losh 			bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
205983825b71SWarner Losh 			    BUS_DMASYNC_POSTWRITE);
206083825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
206183825b71SWarner Losh 			m_freem(cur_tx->xl_mbuf);
206283825b71SWarner Losh 			cur_tx->xl_mbuf = NULL;
206383825b71SWarner Losh 		}
206483825b71SWarner Losh 
2065ec4a8977SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
206683825b71SWarner Losh 
206783825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt--;
206883825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
206983825b71SWarner Losh 	}
207083825b71SWarner Losh 
207183825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_cnt == 0)
207283825b71SWarner Losh 		sc->xl_wdog_timer = 0;
207383825b71SWarner Losh 	sc->xl_cdata.xl_tx_cons = idx;
207483825b71SWarner Losh 
207583825b71SWarner Losh 	if (cur_tx != NULL)
207683825b71SWarner Losh 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
207783825b71SWarner Losh }
207883825b71SWarner Losh 
207983825b71SWarner Losh /*
208083825b71SWarner Losh  * TX 'end of channel' interrupt handler. Actually, we should
208183825b71SWarner Losh  * only get a 'TX complete' interrupt if there's a transmit error,
208283825b71SWarner Losh  * so this is really TX error handler.
208383825b71SWarner Losh  */
208483825b71SWarner Losh static void
208583825b71SWarner Losh xl_txeoc(struct xl_softc *sc)
208683825b71SWarner Losh {
208783825b71SWarner Losh 	u_int8_t		txstat;
208883825b71SWarner Losh 
208983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
209083825b71SWarner Losh 
209183825b71SWarner Losh 	while ((txstat = CSR_READ_1(sc, XL_TX_STATUS))) {
209283825b71SWarner Losh 		if (txstat & XL_TXSTATUS_UNDERRUN ||
209383825b71SWarner Losh 			txstat & XL_TXSTATUS_JABBER ||
209483825b71SWarner Losh 			txstat & XL_TXSTATUS_RECLAIM) {
209583825b71SWarner Losh 			device_printf(sc->xl_dev,
20967498e81aSPyun YongHyeon 			    "transmission error: 0x%02x\n", txstat);
209783825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
209883825b71SWarner Losh 			xl_wait(sc);
209983825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
210083825b71SWarner Losh 				if (sc->xl_cdata.xl_tx_cnt) {
210183825b71SWarner Losh 					int			i;
210283825b71SWarner Losh 					struct xl_chain		*c;
210383825b71SWarner Losh 
210483825b71SWarner Losh 					i = sc->xl_cdata.xl_tx_cons;
210583825b71SWarner Losh 					c = &sc->xl_cdata.xl_tx_chain[i];
210683825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
210783825b71SWarner Losh 					    c->xl_phys);
210883825b71SWarner Losh 					CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
21097498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
211083825b71SWarner Losh 				}
211183825b71SWarner Losh 			} else {
21127498e81aSPyun YongHyeon 				if (sc->xl_cdata.xl_tx_head != NULL) {
211383825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
211483825b71SWarner Losh 					    sc->xl_cdata.xl_tx_head->xl_phys);
21157498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
21167498e81aSPyun YongHyeon 				}
211783825b71SWarner Losh 			}
211883825b71SWarner Losh 			/*
211983825b71SWarner Losh 			 * Remember to set this for the
212083825b71SWarner Losh 			 * first generation 3c90X chips.
212183825b71SWarner Losh 			 */
212283825b71SWarner Losh 			CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
212383825b71SWarner Losh 			if (txstat & XL_TXSTATUS_UNDERRUN &&
212483825b71SWarner Losh 			    sc->xl_tx_thresh < XL_PACKET_SIZE) {
212583825b71SWarner Losh 				sc->xl_tx_thresh += XL_MIN_FRAMELEN;
212683825b71SWarner Losh 				device_printf(sc->xl_dev,
212783825b71SWarner Losh "tx underrun, increasing tx start threshold to %d bytes\n", sc->xl_tx_thresh);
212883825b71SWarner Losh 			}
212983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
213083825b71SWarner Losh 			    XL_CMD_TX_SET_START|sc->xl_tx_thresh);
213183825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
213283825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND,
213383825b71SWarner Losh 				XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
213483825b71SWarner Losh 			}
213583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
213683825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
213783825b71SWarner Losh 		} else {
213883825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
213983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
214083825b71SWarner Losh 		}
214183825b71SWarner Losh 		/*
214283825b71SWarner Losh 		 * Write an arbitrary byte to the TX_STATUS register
214383825b71SWarner Losh 		 * to clear this interrupt/error and advance to the next.
214483825b71SWarner Losh 		 */
214583825b71SWarner Losh 		CSR_WRITE_1(sc, XL_TX_STATUS, 0x01);
214683825b71SWarner Losh 	}
214783825b71SWarner Losh }
214883825b71SWarner Losh 
214983825b71SWarner Losh static void
215083825b71SWarner Losh xl_intr(void *arg)
215183825b71SWarner Losh {
215283825b71SWarner Losh 	struct xl_softc		*sc = arg;
215383825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
215483825b71SWarner Losh 	u_int16_t		status;
215583825b71SWarner Losh 
215683825b71SWarner Losh 	XL_LOCK(sc);
215783825b71SWarner Losh 
215883825b71SWarner Losh #ifdef DEVICE_POLLING
215983825b71SWarner Losh 	if (ifp->if_capenable & IFCAP_POLLING) {
216083825b71SWarner Losh 		XL_UNLOCK(sc);
216183825b71SWarner Losh 		return;
216283825b71SWarner Losh 	}
216383825b71SWarner Losh #endif
216483825b71SWarner Losh 
216574517b07SPyun YongHyeon 	for (;;) {
216674517b07SPyun YongHyeon 		status = CSR_READ_2(sc, XL_STATUS);
216774517b07SPyun YongHyeon 		if ((status & XL_INTRS) == 0 || status == 0xFFFF)
216874517b07SPyun YongHyeon 			break;
216983825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
217083825b71SWarner Losh 		    XL_CMD_INTR_ACK|(status & XL_INTRS));
217174517b07SPyun YongHyeon 		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
217274517b07SPyun YongHyeon 			break;
217383825b71SWarner Losh 
217483825b71SWarner Losh 		if (status & XL_STAT_UP_COMPLETE) {
21755a13764bSPyun YongHyeon 			if (xl_rxeof(sc) == 0) {
217683825b71SWarner Losh 				while (xl_rx_resync(sc))
217783825b71SWarner Losh 					xl_rxeof(sc);
217883825b71SWarner Losh 			}
217983825b71SWarner Losh 		}
218083825b71SWarner Losh 
218183825b71SWarner Losh 		if (status & XL_STAT_DOWN_COMPLETE) {
218283825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B)
218383825b71SWarner Losh 				xl_txeof_90xB(sc);
218483825b71SWarner Losh 			else
218583825b71SWarner Losh 				xl_txeof(sc);
218683825b71SWarner Losh 		}
218783825b71SWarner Losh 
218883825b71SWarner Losh 		if (status & XL_STAT_TX_COMPLETE) {
2189ec4a8977SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
219083825b71SWarner Losh 			xl_txeoc(sc);
219183825b71SWarner Losh 		}
219283825b71SWarner Losh 
219383825b71SWarner Losh 		if (status & XL_STAT_ADFAIL) {
219427b031a9SPyun YongHyeon 			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
219583825b71SWarner Losh 			xl_init_locked(sc);
219674517b07SPyun YongHyeon 			break;
219783825b71SWarner Losh 		}
219883825b71SWarner Losh 
219948dcbc33SPyun YongHyeon 		if (status & XL_STAT_STATSOFLOW)
220048dcbc33SPyun YongHyeon 			xl_stats_update(sc);
220183825b71SWarner Losh 	}
220283825b71SWarner Losh 
220374517b07SPyun YongHyeon 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
220474517b07SPyun YongHyeon 	    ifp->if_drv_flags & IFF_DRV_RUNNING) {
220583825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
220683825b71SWarner Losh 			xl_start_90xB_locked(ifp);
220783825b71SWarner Losh 		else
220883825b71SWarner Losh 			xl_start_locked(ifp);
220983825b71SWarner Losh 	}
221083825b71SWarner Losh 
221183825b71SWarner Losh 	XL_UNLOCK(sc);
221283825b71SWarner Losh }
221383825b71SWarner Losh 
221483825b71SWarner Losh #ifdef DEVICE_POLLING
22151abcdbd1SAttilio Rao static int
221683825b71SWarner Losh xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
221783825b71SWarner Losh {
221883825b71SWarner Losh 	struct xl_softc *sc = ifp->if_softc;
22191abcdbd1SAttilio Rao 	int rx_npkts = 0;
222083825b71SWarner Losh 
222183825b71SWarner Losh 	XL_LOCK(sc);
222283825b71SWarner Losh 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
22231abcdbd1SAttilio Rao 		rx_npkts = xl_poll_locked(ifp, cmd, count);
222483825b71SWarner Losh 	XL_UNLOCK(sc);
22251abcdbd1SAttilio Rao 	return (rx_npkts);
222683825b71SWarner Losh }
222783825b71SWarner Losh 
22281abcdbd1SAttilio Rao static int
222983825b71SWarner Losh xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
223083825b71SWarner Losh {
223183825b71SWarner Losh 	struct xl_softc *sc = ifp->if_softc;
22321abcdbd1SAttilio Rao 	int rx_npkts;
223383825b71SWarner Losh 
223483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
223583825b71SWarner Losh 
223683825b71SWarner Losh 	sc->rxcycles = count;
22371abcdbd1SAttilio Rao 	rx_npkts = xl_rxeof(sc);
223883825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
223983825b71SWarner Losh 		xl_txeof_90xB(sc);
224083825b71SWarner Losh 	else
224183825b71SWarner Losh 		xl_txeof(sc);
224283825b71SWarner Losh 
224383825b71SWarner Losh 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
224483825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
224583825b71SWarner Losh 			xl_start_90xB_locked(ifp);
224683825b71SWarner Losh 		else
224783825b71SWarner Losh 			xl_start_locked(ifp);
224883825b71SWarner Losh 	}
224983825b71SWarner Losh 
225083825b71SWarner Losh 	if (cmd == POLL_AND_CHECK_STATUS) {
225183825b71SWarner Losh 		u_int16_t status;
225283825b71SWarner Losh 
225383825b71SWarner Losh 		status = CSR_READ_2(sc, XL_STATUS);
225483825b71SWarner Losh 		if (status & XL_INTRS && status != 0xFFFF) {
225583825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
225683825b71SWarner Losh 			    XL_CMD_INTR_ACK|(status & XL_INTRS));
225783825b71SWarner Losh 
225883825b71SWarner Losh 			if (status & XL_STAT_TX_COMPLETE) {
2259ec4a8977SGleb Smirnoff 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
226083825b71SWarner Losh 				xl_txeoc(sc);
226183825b71SWarner Losh 			}
226283825b71SWarner Losh 
226383825b71SWarner Losh 			if (status & XL_STAT_ADFAIL) {
226427b031a9SPyun YongHyeon 				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
226583825b71SWarner Losh 				xl_init_locked(sc);
226683825b71SWarner Losh 			}
226783825b71SWarner Losh 
226848dcbc33SPyun YongHyeon 			if (status & XL_STAT_STATSOFLOW)
2269de53eba0SPyun YongHyeon 				xl_stats_update(sc);
227083825b71SWarner Losh 		}
227183825b71SWarner Losh 	}
22721abcdbd1SAttilio Rao 	return (rx_npkts);
227383825b71SWarner Losh }
227483825b71SWarner Losh #endif /* DEVICE_POLLING */
227583825b71SWarner Losh 
227683825b71SWarner Losh static void
227748dcbc33SPyun YongHyeon xl_tick(void *xsc)
227883825b71SWarner Losh {
227983825b71SWarner Losh 	struct xl_softc *sc = xsc;
228048dcbc33SPyun YongHyeon 	struct mii_data *mii;
228183825b71SWarner Losh 
228283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
228383825b71SWarner Losh 
228448dcbc33SPyun YongHyeon 	if (sc->xl_miibus != NULL) {
228548dcbc33SPyun YongHyeon 		mii = device_get_softc(sc->xl_miibus);
228648dcbc33SPyun YongHyeon 		mii_tick(mii);
228748dcbc33SPyun YongHyeon 	}
228848dcbc33SPyun YongHyeon 
228948dcbc33SPyun YongHyeon 	xl_stats_update(sc);
229083825b71SWarner Losh 	if (xl_watchdog(sc) == EJUSTRETURN)
229183825b71SWarner Losh 		return;
229283825b71SWarner Losh 
229348dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
229483825b71SWarner Losh }
229583825b71SWarner Losh 
229683825b71SWarner Losh static void
229748dcbc33SPyun YongHyeon xl_stats_update(struct xl_softc *sc)
229883825b71SWarner Losh {
229983825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
230083825b71SWarner Losh 	struct xl_stats		xl_stats;
230183825b71SWarner Losh 	u_int8_t		*p;
230283825b71SWarner Losh 	int			i;
230383825b71SWarner Losh 
230483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
230583825b71SWarner Losh 
230683825b71SWarner Losh 	bzero((char *)&xl_stats, sizeof(struct xl_stats));
230783825b71SWarner Losh 
230883825b71SWarner Losh 	p = (u_int8_t *)&xl_stats;
230983825b71SWarner Losh 
231083825b71SWarner Losh 	/* Read all the stats registers. */
231183825b71SWarner Losh 	XL_SEL_WIN(6);
231283825b71SWarner Losh 
231383825b71SWarner Losh 	for (i = 0; i < 16; i++)
231483825b71SWarner Losh 		*p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i);
231583825b71SWarner Losh 
2316ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IERRORS, xl_stats.xl_rx_overrun);
231783825b71SWarner Losh 
2318ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
2319ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_multi_collision +
2320ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_single_collision +
2321ec4a8977SGleb Smirnoff 	    xl_stats.xl_tx_late_collision);
232283825b71SWarner Losh 
232383825b71SWarner Losh 	/*
232483825b71SWarner Losh 	 * Boomerang and cyclone chips have an extra stats counter
232583825b71SWarner Losh 	 * in window 4 (BadSSD). We have to read this too in order
232683825b71SWarner Losh 	 * to clear out all the stats registers and avoid a statsoflow
232783825b71SWarner Losh 	 * interrupt.
232883825b71SWarner Losh 	 */
232983825b71SWarner Losh 	XL_SEL_WIN(4);
233083825b71SWarner Losh 	CSR_READ_1(sc, XL_W4_BADSSD);
233183825b71SWarner Losh 	XL_SEL_WIN(7);
233283825b71SWarner Losh }
233383825b71SWarner Losh 
233483825b71SWarner Losh /*
233583825b71SWarner Losh  * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
233683825b71SWarner Losh  * pointers to the fragment pointers.
233783825b71SWarner Losh  */
233883825b71SWarner Losh static int
233983825b71SWarner Losh xl_encap(struct xl_softc *sc, struct xl_chain *c, struct mbuf **m_head)
234083825b71SWarner Losh {
234183825b71SWarner Losh 	struct mbuf		*m_new;
234283825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
234383825b71SWarner Losh 	int			error, i, nseg, total_len;
234483825b71SWarner Losh 	u_int32_t		status;
234583825b71SWarner Losh 
234683825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
234783825b71SWarner Losh 
234883825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map, *m_head,
234983825b71SWarner Losh 	    sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
235083825b71SWarner Losh 
235183825b71SWarner Losh 	if (error && error != EFBIG) {
235283825b71SWarner Losh 		if_printf(ifp, "can't map mbuf (error %d)\n", error);
235383825b71SWarner Losh 		return (error);
235483825b71SWarner Losh 	}
235583825b71SWarner Losh 
235683825b71SWarner Losh 	/*
235783825b71SWarner Losh 	 * Handle special case: we used up all 63 fragments,
235883825b71SWarner Losh 	 * but we have more mbufs left in the chain. Copy the
235983825b71SWarner Losh 	 * data into an mbuf cluster. Note that we don't
236083825b71SWarner Losh 	 * bother clearing the values in the other fragment
236183825b71SWarner Losh 	 * pointers/counters; it wouldn't gain us anything,
236283825b71SWarner Losh 	 * and would waste cycles.
236383825b71SWarner Losh 	 */
236483825b71SWarner Losh 	if (error) {
2365c6499eccSGleb Smirnoff 		m_new = m_collapse(*m_head, M_NOWAIT, XL_MAXFRAGS);
236683825b71SWarner Losh 		if (m_new == NULL) {
236783825b71SWarner Losh 			m_freem(*m_head);
236883825b71SWarner Losh 			*m_head = NULL;
236983825b71SWarner Losh 			return (ENOBUFS);
237083825b71SWarner Losh 		}
237183825b71SWarner Losh 		*m_head = m_new;
237283825b71SWarner Losh 
237383825b71SWarner Losh 		error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map,
237483825b71SWarner Losh 		    *m_head, sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
237583825b71SWarner Losh 		if (error) {
237683825b71SWarner Losh 			m_freem(*m_head);
237783825b71SWarner Losh 			*m_head = NULL;
237883825b71SWarner Losh 			if_printf(ifp, "can't map mbuf (error %d)\n", error);
237983825b71SWarner Losh 			return (error);
238083825b71SWarner Losh 		}
238183825b71SWarner Losh 	}
238283825b71SWarner Losh 
238383825b71SWarner Losh 	KASSERT(nseg <= XL_MAXFRAGS,
238483825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
238583825b71SWarner Losh 	if (nseg == 0) {
238683825b71SWarner Losh 		m_freem(*m_head);
238783825b71SWarner Losh 		*m_head = NULL;
238883825b71SWarner Losh 		return (EIO);
238983825b71SWarner Losh 	}
23904f58a95cSPyun YongHyeon 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE);
239183825b71SWarner Losh 
239283825b71SWarner Losh 	total_len = 0;
239383825b71SWarner Losh 	for (i = 0; i < nseg; i++) {
239483825b71SWarner Losh 		KASSERT(sc->xl_cdata.xl_tx_segs[i].ds_len <= MCLBYTES,
239583825b71SWarner Losh 		    ("segment size too large"));
239683825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_addr =
239783825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_addr);
239883825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_len =
239983825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_len);
240083825b71SWarner Losh 		total_len += sc->xl_cdata.xl_tx_segs[i].ds_len;
240183825b71SWarner Losh 	}
240278564edaSPyun YongHyeon 	c->xl_ptr->xl_frag[nseg - 1].xl_len |= htole32(XL_LAST_FRAG);
240383825b71SWarner Losh 
240483825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
240583825b71SWarner Losh 		status = XL_TXSTAT_RND_DEFEAT;
240683825b71SWarner Losh 
240783825b71SWarner Losh #ifndef XL905B_TXCSUM_BROKEN
24088e95322aSPyun YongHyeon 		if ((*m_head)->m_pkthdr.csum_flags) {
24098e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP)
241083825b71SWarner Losh 				status |= XL_TXSTAT_IPCKSUM;
24118e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
241283825b71SWarner Losh 				status |= XL_TXSTAT_TCPCKSUM;
24138e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
241483825b71SWarner Losh 				status |= XL_TXSTAT_UDPCKSUM;
241583825b71SWarner Losh 		}
241683825b71SWarner Losh #endif
24174f58a95cSPyun YongHyeon 	} else
24184f58a95cSPyun YongHyeon 		status = total_len;
241983825b71SWarner Losh 	c->xl_ptr->xl_status = htole32(status);
24204f58a95cSPyun YongHyeon 	c->xl_ptr->xl_next = 0;
242183825b71SWarner Losh 
242283825b71SWarner Losh 	c->xl_mbuf = *m_head;
242383825b71SWarner Losh 	return (0);
242483825b71SWarner Losh }
242583825b71SWarner Losh 
242683825b71SWarner Losh /*
242783825b71SWarner Losh  * Main transmit routine. To avoid having to do mbuf copies, we put pointers
242883825b71SWarner Losh  * to the mbuf data regions directly in the transmit lists. We also save a
242983825b71SWarner Losh  * copy of the pointers since the transmit list fragment pointers are
243083825b71SWarner Losh  * physical addresses.
243183825b71SWarner Losh  */
243283825b71SWarner Losh 
243383825b71SWarner Losh static void
243483825b71SWarner Losh xl_start(struct ifnet *ifp)
243583825b71SWarner Losh {
243683825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
243783825b71SWarner Losh 
243883825b71SWarner Losh 	XL_LOCK(sc);
243983825b71SWarner Losh 
244083825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
244183825b71SWarner Losh 		xl_start_90xB_locked(ifp);
244283825b71SWarner Losh 	else
244383825b71SWarner Losh 		xl_start_locked(ifp);
244483825b71SWarner Losh 
244583825b71SWarner Losh 	XL_UNLOCK(sc);
244683825b71SWarner Losh }
244783825b71SWarner Losh 
244883825b71SWarner Losh static void
244983825b71SWarner Losh xl_start_locked(struct ifnet *ifp)
245083825b71SWarner Losh {
245183825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
24524a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
245383825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
24544a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
245583825b71SWarner Losh 	int			error;
245683825b71SWarner Losh 
245783825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
245883825b71SWarner Losh 
2459ba65e0ccSPyun YongHyeon 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2460ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
2461ba65e0ccSPyun YongHyeon 		return;
246283825b71SWarner Losh 	/*
246383825b71SWarner Losh 	 * Check for an available queue slot. If there are none,
246483825b71SWarner Losh 	 * punt.
246583825b71SWarner Losh 	 */
246683825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_free == NULL) {
246783825b71SWarner Losh 		xl_txeoc(sc);
246883825b71SWarner Losh 		xl_txeof(sc);
246983825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_free == NULL) {
247083825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
247183825b71SWarner Losh 			return;
247283825b71SWarner Losh 		}
247383825b71SWarner Losh 	}
247483825b71SWarner Losh 
247583825b71SWarner Losh 	start_tx = sc->xl_cdata.xl_tx_free;
247683825b71SWarner Losh 
247783825b71SWarner Losh 	for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
247883825b71SWarner Losh 	    sc->xl_cdata.xl_tx_free != NULL;) {
247983825b71SWarner Losh 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
248083825b71SWarner Losh 		if (m_head == NULL)
248183825b71SWarner Losh 			break;
248283825b71SWarner Losh 
248383825b71SWarner Losh 		/* Pick a descriptor off the free list. */
24844a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
248583825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_free;
248683825b71SWarner Losh 
248783825b71SWarner Losh 		/* Pack the data into the descriptor. */
248883825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
248983825b71SWarner Losh 		if (error) {
24904a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
249183825b71SWarner Losh 			if (m_head == NULL)
249283825b71SWarner Losh 				break;
249383825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
249483825b71SWarner Losh 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
249583825b71SWarner Losh 			break;
249683825b71SWarner Losh 		}
249783825b71SWarner Losh 
249883825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
249983825b71SWarner Losh 		cur_tx->xl_next = NULL;
250083825b71SWarner Losh 
250183825b71SWarner Losh 		/* Chain it together. */
250283825b71SWarner Losh 		if (prev != NULL) {
250383825b71SWarner Losh 			prev->xl_next = cur_tx;
250483825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
250583825b71SWarner Losh 		}
250683825b71SWarner Losh 		prev = cur_tx;
250783825b71SWarner Losh 
250883825b71SWarner Losh 		/*
250983825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
251083825b71SWarner Losh 		 * to him.
251183825b71SWarner Losh 		 */
251283825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
251383825b71SWarner Losh 	}
251483825b71SWarner Losh 
251583825b71SWarner Losh 	/*
251683825b71SWarner Losh 	 * If there are no packets queued, bail.
251783825b71SWarner Losh 	 */
251883825b71SWarner Losh 	if (cur_tx == NULL)
251983825b71SWarner Losh 		return;
252083825b71SWarner Losh 
252183825b71SWarner Losh 	/*
252283825b71SWarner Losh 	 * Place the request for the upload interrupt
252383825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
252483825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
252583825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
252683825b71SWarner Losh 	 * once for each packet.
252783825b71SWarner Losh 	 */
252878564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
252983825b71SWarner Losh 
253083825b71SWarner Losh 	/*
253183825b71SWarner Losh 	 * Queue the packets. If the TX channel is clear, update
253283825b71SWarner Losh 	 * the downlist pointer register.
253383825b71SWarner Losh 	 */
253483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
253583825b71SWarner Losh 	xl_wait(sc);
253683825b71SWarner Losh 
253783825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head != NULL) {
253883825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_next = start_tx;
253983825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next =
254083825b71SWarner Losh 		    htole32(start_tx->xl_phys);
254178564edaSPyun YongHyeon 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &=
254278564edaSPyun YongHyeon 		    htole32(~XL_TXSTAT_DL_INTR);
254383825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
254483825b71SWarner Losh 	} else {
254583825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = start_tx;
254683825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
254783825b71SWarner Losh 	}
25480ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
25490ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
255083825b71SWarner Losh 	if (!CSR_READ_4(sc, XL_DOWNLIST_PTR))
255183825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys);
255283825b71SWarner Losh 
255383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
255483825b71SWarner Losh 
255583825b71SWarner Losh 	XL_SEL_WIN(7);
255683825b71SWarner Losh 
255783825b71SWarner Losh 	/*
255883825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
255983825b71SWarner Losh 	 */
256083825b71SWarner Losh 	sc->xl_wdog_timer = 5;
256183825b71SWarner Losh 
256283825b71SWarner Losh 	/*
256383825b71SWarner Losh 	 * XXX Under certain conditions, usually on slower machines
256483825b71SWarner Losh 	 * where interrupts may be dropped, it's possible for the
256583825b71SWarner Losh 	 * adapter to chew up all the buffers in the receive ring
256683825b71SWarner Losh 	 * and stall, without us being able to do anything about it.
256783825b71SWarner Losh 	 * To guard against this, we need to make a pass over the
256883825b71SWarner Losh 	 * RX queue to make sure there aren't any packets pending.
256983825b71SWarner Losh 	 * Doing it here means we can flush the receive ring at the
257083825b71SWarner Losh 	 * same time the chip is DMAing the transmit descriptors we
257183825b71SWarner Losh 	 * just gave it.
257283825b71SWarner Losh 	 *
257383825b71SWarner Losh 	 * 3Com goes to some lengths to emphasize the Parallel Tasking (tm)
257483825b71SWarner Losh 	 * nature of their chips in all their marketing literature;
257583825b71SWarner Losh 	 * we may as well take advantage of it. :)
257683825b71SWarner Losh 	 */
257783825b71SWarner Losh 	taskqueue_enqueue(taskqueue_swi, &sc->xl_task);
257883825b71SWarner Losh }
257983825b71SWarner Losh 
258083825b71SWarner Losh static void
258183825b71SWarner Losh xl_start_90xB_locked(struct ifnet *ifp)
258283825b71SWarner Losh {
258383825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
25844a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
258583825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
25864a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
258783825b71SWarner Losh 	int			error, idx;
258883825b71SWarner Losh 
258983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
259083825b71SWarner Losh 
2591ba65e0ccSPyun YongHyeon 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2592ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
259383825b71SWarner Losh 		return;
259483825b71SWarner Losh 
259583825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_prod;
259683825b71SWarner Losh 	start_tx = &sc->xl_cdata.xl_tx_chain[idx];
259783825b71SWarner Losh 
259883825b71SWarner Losh 	for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
259983825b71SWarner Losh 	    sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL;) {
260083825b71SWarner Losh 		if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) {
260183825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
260283825b71SWarner Losh 			break;
260383825b71SWarner Losh 		}
260483825b71SWarner Losh 
260583825b71SWarner Losh 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
260683825b71SWarner Losh 		if (m_head == NULL)
260783825b71SWarner Losh 			break;
260883825b71SWarner Losh 
26094a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
261083825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
261183825b71SWarner Losh 
261283825b71SWarner Losh 		/* Pack the data into the descriptor. */
261383825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
261483825b71SWarner Losh 		if (error) {
26154a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
261683825b71SWarner Losh 			if (m_head == NULL)
261783825b71SWarner Losh 				break;
261883825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
261983825b71SWarner Losh 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
262083825b71SWarner Losh 			break;
262183825b71SWarner Losh 		}
262283825b71SWarner Losh 
262383825b71SWarner Losh 		/* Chain it together. */
262483825b71SWarner Losh 		if (prev != NULL)
262583825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
262683825b71SWarner Losh 		prev = cur_tx;
262783825b71SWarner Losh 
262883825b71SWarner Losh 		/*
262983825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
263083825b71SWarner Losh 		 * to him.
263183825b71SWarner Losh 		 */
263283825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
263383825b71SWarner Losh 
263483825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
263583825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt++;
263683825b71SWarner Losh 	}
263783825b71SWarner Losh 
263883825b71SWarner Losh 	/*
263983825b71SWarner Losh 	 * If there are no packets queued, bail.
264083825b71SWarner Losh 	 */
264183825b71SWarner Losh 	if (cur_tx == NULL)
264283825b71SWarner Losh 		return;
264383825b71SWarner Losh 
264483825b71SWarner Losh 	/*
264583825b71SWarner Losh 	 * Place the request for the upload interrupt
264683825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
264783825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
264883825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
264983825b71SWarner Losh 	 * once for each packet.
265083825b71SWarner Losh 	 */
265178564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
265283825b71SWarner Losh 
265383825b71SWarner Losh 	/* Start transmission */
265483825b71SWarner Losh 	sc->xl_cdata.xl_tx_prod = idx;
265583825b71SWarner Losh 	start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys);
26560ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
26570ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
265883825b71SWarner Losh 
265983825b71SWarner Losh 	/*
266083825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
266183825b71SWarner Losh 	 */
266283825b71SWarner Losh 	sc->xl_wdog_timer = 5;
266383825b71SWarner Losh }
266483825b71SWarner Losh 
266583825b71SWarner Losh static void
266683825b71SWarner Losh xl_init(void *xsc)
266783825b71SWarner Losh {
266883825b71SWarner Losh 	struct xl_softc		*sc = xsc;
266983825b71SWarner Losh 
267083825b71SWarner Losh 	XL_LOCK(sc);
267183825b71SWarner Losh 	xl_init_locked(sc);
267283825b71SWarner Losh 	XL_UNLOCK(sc);
267383825b71SWarner Losh }
267483825b71SWarner Losh 
267583825b71SWarner Losh static void
267683825b71SWarner Losh xl_init_locked(struct xl_softc *sc)
267783825b71SWarner Losh {
267883825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
267983825b71SWarner Losh 	int			error, i;
268083825b71SWarner Losh 	struct mii_data		*mii = NULL;
268183825b71SWarner Losh 
268283825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
268383825b71SWarner Losh 
268427b031a9SPyun YongHyeon 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
268527b031a9SPyun YongHyeon 		return;
268683825b71SWarner Losh 	/*
268783825b71SWarner Losh 	 * Cancel pending I/O and free all RX/TX buffers.
268883825b71SWarner Losh 	 */
268983825b71SWarner Losh 	xl_stop(sc);
269083825b71SWarner Losh 
2691ac681091SPyun YongHyeon 	/* Reset the chip to a known state. */
2692ac681091SPyun YongHyeon 	xl_reset(sc);
2693ac681091SPyun YongHyeon 
269483825b71SWarner Losh 	if (sc->xl_miibus == NULL) {
269583825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
269683825b71SWarner Losh 		xl_wait(sc);
269783825b71SWarner Losh 	}
269883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
269983825b71SWarner Losh 	xl_wait(sc);
270083825b71SWarner Losh 	DELAY(10000);
270183825b71SWarner Losh 
270283825b71SWarner Losh 	if (sc->xl_miibus != NULL)
270383825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
270483825b71SWarner Losh 
27059ae11bbaSPyun YongHyeon 	/*
27069ae11bbaSPyun YongHyeon 	 * Clear WOL status and disable all WOL feature as WOL
27079ae11bbaSPyun YongHyeon 	 * would interfere Rx operation under normal environments.
27089ae11bbaSPyun YongHyeon 	 */
27099ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0) {
27109ae11bbaSPyun YongHyeon 		XL_SEL_WIN(7);
27119ae11bbaSPyun YongHyeon 		CSR_READ_2(sc, XL_W7_BM_PME);
27129ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_W7_BM_PME, 0);
27139ae11bbaSPyun YongHyeon 	}
271483825b71SWarner Losh 	/* Init our MAC address */
271583825b71SWarner Losh 	XL_SEL_WIN(2);
271683825b71SWarner Losh 	for (i = 0; i < ETHER_ADDR_LEN; i++) {
271783825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i,
271883825b71SWarner Losh 				IF_LLADDR(sc->xl_ifp)[i]);
271983825b71SWarner Losh 	}
272083825b71SWarner Losh 
272183825b71SWarner Losh 	/* Clear the station mask. */
272283825b71SWarner Losh 	for (i = 0; i < 3; i++)
272383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
272483825b71SWarner Losh #ifdef notdef
272583825b71SWarner Losh 	/* Reset TX and RX. */
272683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
272783825b71SWarner Losh 	xl_wait(sc);
272883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
272983825b71SWarner Losh 	xl_wait(sc);
273083825b71SWarner Losh #endif
273183825b71SWarner Losh 	/* Init circular RX list. */
273283825b71SWarner Losh 	error = xl_list_rx_init(sc);
273383825b71SWarner Losh 	if (error) {
273483825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the rx ring failed (%d)\n",
273583825b71SWarner Losh 		    error);
273683825b71SWarner Losh 		xl_stop(sc);
273783825b71SWarner Losh 		return;
273883825b71SWarner Losh 	}
273983825b71SWarner Losh 
274083825b71SWarner Losh 	/* Init TX descriptors. */
274183825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
274283825b71SWarner Losh 		error = xl_list_tx_init_90xB(sc);
274383825b71SWarner Losh 	else
274483825b71SWarner Losh 		error = xl_list_tx_init(sc);
274583825b71SWarner Losh 	if (error) {
274683825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the tx ring failed (%d)\n",
274783825b71SWarner Losh 		    error);
274883825b71SWarner Losh 		xl_stop(sc);
274983825b71SWarner Losh 		return;
275083825b71SWarner Losh 	}
275183825b71SWarner Losh 
275283825b71SWarner Losh 	/*
275383825b71SWarner Losh 	 * Set the TX freethresh value.
275483825b71SWarner Losh 	 * Note that this has no effect on 3c905B "cyclone"
275583825b71SWarner Losh 	 * cards but is required for 3c900/3c905 "boomerang"
275683825b71SWarner Losh 	 * cards in order to enable the download engine.
275783825b71SWarner Losh 	 */
275883825b71SWarner Losh 	CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
275983825b71SWarner Losh 
276083825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
276183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
276283825b71SWarner Losh 
276383825b71SWarner Losh 	/*
276483825b71SWarner Losh 	 * If this is a 3c905B, also set the tx reclaim threshold.
276583825b71SWarner Losh 	 * This helps cut down on the number of tx reclaim errors
276683825b71SWarner Losh 	 * that could happen on a busy network. The chip multiplies
276783825b71SWarner Losh 	 * the register value by 16 to obtain the actual threshold
276883825b71SWarner Losh 	 * in bytes, so we divide by 16 when setting the value here.
276983825b71SWarner Losh 	 * The existing threshold value can be examined by reading
277083825b71SWarner Losh 	 * the register at offset 9 in window 5.
277183825b71SWarner Losh 	 */
277283825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
277383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
277483825b71SWarner Losh 		    XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
277583825b71SWarner Losh 	}
277683825b71SWarner Losh 
277783825b71SWarner Losh 	/* Set RX filter bits. */
2778dd0a7688SPyun YongHyeon 	xl_rxfilter(sc);
277983825b71SWarner Losh 
278083825b71SWarner Losh 	/*
278183825b71SWarner Losh 	 * Load the address of the RX list. We have to
278283825b71SWarner Losh 	 * stall the upload engine before we can manipulate
278383825b71SWarner Losh 	 * the uplist pointer register, then unstall it when
278483825b71SWarner Losh 	 * we're finished. We also have to wait for the
278583825b71SWarner Losh 	 * stall command to complete before proceeding.
278683825b71SWarner Losh 	 * Note that we have to do this after any RX resets
278783825b71SWarner Losh 	 * have completed since the uplist register is cleared
278883825b71SWarner Losh 	 * by a reset.
278983825b71SWarner Losh 	 */
279083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
279183825b71SWarner Losh 	xl_wait(sc);
279283825b71SWarner Losh 	CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
279383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
279483825b71SWarner Losh 	xl_wait(sc);
279583825b71SWarner Losh 
279683825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
279783825b71SWarner Losh 		/* Set polling interval */
279883825b71SWarner Losh 		CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
279983825b71SWarner Losh 		/* Load the address of the TX list */
280083825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
280183825b71SWarner Losh 		xl_wait(sc);
280283825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
280383825b71SWarner Losh 		    sc->xl_cdata.xl_tx_chain[0].xl_phys);
280483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
280583825b71SWarner Losh 		xl_wait(sc);
280683825b71SWarner Losh 	}
280783825b71SWarner Losh 
280883825b71SWarner Losh 	/*
280983825b71SWarner Losh 	 * If the coax transceiver is on, make sure to enable
281083825b71SWarner Losh 	 * the DC-DC converter.
281183825b71SWarner Losh 	 */
281283825b71SWarner Losh 	XL_SEL_WIN(3);
281383825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_COAX)
281483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
281583825b71SWarner Losh 	else
281683825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
281783825b71SWarner Losh 
281883825b71SWarner Losh 	/*
281983825b71SWarner Losh 	 * increase packet size to allow reception of 802.1q or ISL packets.
282083825b71SWarner Losh 	 * For the 3c90x chip, set the 'allow large packets' bit in the MAC
282183825b71SWarner Losh 	 * control register. For 3c90xB/C chips, use the RX packet size
282283825b71SWarner Losh 	 * register.
282383825b71SWarner Losh 	 */
282483825b71SWarner Losh 
282583825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
282683825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
282783825b71SWarner Losh 	else {
282883825b71SWarner Losh 		u_int8_t macctl;
282983825b71SWarner Losh 		macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
283083825b71SWarner Losh 		macctl |= XL_MACCTRL_ALLOW_LARGE_PACK;
283183825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
283283825b71SWarner Losh 	}
283383825b71SWarner Losh 
283483825b71SWarner Losh 	/* Clear out the stats counters. */
283583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
283648dcbc33SPyun YongHyeon 	xl_stats_update(sc);
283783825b71SWarner Losh 	XL_SEL_WIN(4);
283883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
283983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
284083825b71SWarner Losh 
284183825b71SWarner Losh 	/*
284283825b71SWarner Losh 	 * Enable interrupts.
284383825b71SWarner Losh 	 */
284483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
284583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
284683825b71SWarner Losh #ifdef DEVICE_POLLING
284783825b71SWarner Losh 	/* Disable interrupts if we are polling. */
284883825b71SWarner Losh 	if (ifp->if_capenable & IFCAP_POLLING)
284983825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
285083825b71SWarner Losh 	else
285183825b71SWarner Losh #endif
285283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
285383825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
285483825b71SWarner Losh 	    bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
285583825b71SWarner Losh 
285683825b71SWarner Losh 	/* Set the RX early threshold */
285783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
28580b96ba12SPyun YongHyeon 	CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY);
285983825b71SWarner Losh 
286083825b71SWarner Losh 	/* Enable receiver and transmitter. */
286183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
286283825b71SWarner Losh 	xl_wait(sc);
286383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
286483825b71SWarner Losh 	xl_wait(sc);
286583825b71SWarner Losh 
286683825b71SWarner Losh 	/* XXX Downcall to miibus. */
286783825b71SWarner Losh 	if (mii != NULL)
286883825b71SWarner Losh 		mii_mediachg(mii);
286983825b71SWarner Losh 
287083825b71SWarner Losh 	/* Select window 7 for normal operations. */
287183825b71SWarner Losh 	XL_SEL_WIN(7);
287283825b71SWarner Losh 
287383825b71SWarner Losh 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
287483825b71SWarner Losh 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
287583825b71SWarner Losh 
287683825b71SWarner Losh 	sc->xl_wdog_timer = 0;
287748dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
287883825b71SWarner Losh }
287983825b71SWarner Losh 
288083825b71SWarner Losh /*
288183825b71SWarner Losh  * Set media options.
288283825b71SWarner Losh  */
288383825b71SWarner Losh static int
288483825b71SWarner Losh xl_ifmedia_upd(struct ifnet *ifp)
288583825b71SWarner Losh {
288683825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
288783825b71SWarner Losh 	struct ifmedia		*ifm = NULL;
288883825b71SWarner Losh 	struct mii_data		*mii = NULL;
288983825b71SWarner Losh 
289083825b71SWarner Losh 	XL_LOCK(sc);
289183825b71SWarner Losh 
289283825b71SWarner Losh 	if (sc->xl_miibus != NULL)
289383825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
289483825b71SWarner Losh 	if (mii == NULL)
289583825b71SWarner Losh 		ifm = &sc->ifmedia;
289683825b71SWarner Losh 	else
289783825b71SWarner Losh 		ifm = &mii->mii_media;
289883825b71SWarner Losh 
289983825b71SWarner Losh 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
290083825b71SWarner Losh 	case IFM_100_FX:
290183825b71SWarner Losh 	case IFM_10_FL:
290283825b71SWarner Losh 	case IFM_10_2:
290383825b71SWarner Losh 	case IFM_10_5:
290483825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
290583825b71SWarner Losh 		XL_UNLOCK(sc);
290683825b71SWarner Losh 		return (0);
290783825b71SWarner Losh 	}
290883825b71SWarner Losh 
290983825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
291083825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
291183825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
291227b031a9SPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
291383825b71SWarner Losh 		xl_init_locked(sc);
291483825b71SWarner Losh 	} else {
291583825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
291683825b71SWarner Losh 	}
291783825b71SWarner Losh 
291883825b71SWarner Losh 	XL_UNLOCK(sc);
291983825b71SWarner Losh 
292083825b71SWarner Losh 	return (0);
292183825b71SWarner Losh }
292283825b71SWarner Losh 
292383825b71SWarner Losh /*
292483825b71SWarner Losh  * Report current media status.
292583825b71SWarner Losh  */
292683825b71SWarner Losh static void
292783825b71SWarner Losh xl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
292883825b71SWarner Losh {
292983825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
293083825b71SWarner Losh 	u_int32_t		icfg;
293183825b71SWarner Losh 	u_int16_t		status = 0;
293283825b71SWarner Losh 	struct mii_data		*mii = NULL;
293383825b71SWarner Losh 
293483825b71SWarner Losh 	XL_LOCK(sc);
293583825b71SWarner Losh 
293683825b71SWarner Losh 	if (sc->xl_miibus != NULL)
293783825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
293883825b71SWarner Losh 
293983825b71SWarner Losh 	XL_SEL_WIN(4);
294083825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
294183825b71SWarner Losh 
294283825b71SWarner Losh 	XL_SEL_WIN(3);
294383825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK;
294483825b71SWarner Losh 	icfg >>= XL_ICFG_CONNECTOR_BITS;
294583825b71SWarner Losh 
294683825b71SWarner Losh 	ifmr->ifm_active = IFM_ETHER;
294783825b71SWarner Losh 	ifmr->ifm_status = IFM_AVALID;
294883825b71SWarner Losh 
294983825b71SWarner Losh 	if ((status & XL_MEDIASTAT_CARRIER) == 0)
295083825b71SWarner Losh 		ifmr->ifm_status |= IFM_ACTIVE;
295183825b71SWarner Losh 
295283825b71SWarner Losh 	switch (icfg) {
295383825b71SWarner Losh 	case XL_XCVR_10BT:
295483825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_T;
295583825b71SWarner Losh 		if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
295683825b71SWarner Losh 			ifmr->ifm_active |= IFM_FDX;
295783825b71SWarner Losh 		else
295883825b71SWarner Losh 			ifmr->ifm_active |= IFM_HDX;
295983825b71SWarner Losh 		break;
296083825b71SWarner Losh 	case XL_XCVR_AUI:
296183825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
296283825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
296383825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_FL;
296483825b71SWarner Losh 			if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
296583825b71SWarner Losh 				ifmr->ifm_active |= IFM_FDX;
296683825b71SWarner Losh 			else
296783825b71SWarner Losh 				ifmr->ifm_active |= IFM_HDX;
296883825b71SWarner Losh 		} else
296983825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_5;
297083825b71SWarner Losh 		break;
297183825b71SWarner Losh 	case XL_XCVR_COAX:
297283825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_2;
297383825b71SWarner Losh 		break;
297483825b71SWarner Losh 	/*
297583825b71SWarner Losh 	 * XXX MII and BTX/AUTO should be separate cases.
297683825b71SWarner Losh 	 */
297783825b71SWarner Losh 
297883825b71SWarner Losh 	case XL_XCVR_100BTX:
297983825b71SWarner Losh 	case XL_XCVR_AUTO:
298083825b71SWarner Losh 	case XL_XCVR_MII:
298183825b71SWarner Losh 		if (mii != NULL) {
298283825b71SWarner Losh 			mii_pollstat(mii);
298383825b71SWarner Losh 			ifmr->ifm_active = mii->mii_media_active;
298483825b71SWarner Losh 			ifmr->ifm_status = mii->mii_media_status;
298583825b71SWarner Losh 		}
298683825b71SWarner Losh 		break;
298783825b71SWarner Losh 	case XL_XCVR_100BFX:
298883825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_100_FX;
298983825b71SWarner Losh 		break;
299083825b71SWarner Losh 	default:
299183825b71SWarner Losh 		if_printf(ifp, "unknown XCVR type: %d\n", icfg);
299283825b71SWarner Losh 		break;
299383825b71SWarner Losh 	}
299483825b71SWarner Losh 
299583825b71SWarner Losh 	XL_UNLOCK(sc);
299683825b71SWarner Losh }
299783825b71SWarner Losh 
299883825b71SWarner Losh static int
299983825b71SWarner Losh xl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
300083825b71SWarner Losh {
300183825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
300283825b71SWarner Losh 	struct ifreq		*ifr = (struct ifreq *) data;
3003a3835274SPyun YongHyeon 	int			error = 0, mask;
300483825b71SWarner Losh 	struct mii_data		*mii = NULL;
300583825b71SWarner Losh 
300683825b71SWarner Losh 	switch (command) {
300783825b71SWarner Losh 	case SIOCSIFFLAGS:
300883825b71SWarner Losh 		XL_LOCK(sc);
300983825b71SWarner Losh 		if (ifp->if_flags & IFF_UP) {
301083825b71SWarner Losh 			if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
3011dd0a7688SPyun YongHyeon 			    (ifp->if_flags ^ sc->xl_if_flags) &
3012dd0a7688SPyun YongHyeon 			    (IFF_PROMISC | IFF_ALLMULTI))
3013dd0a7688SPyun YongHyeon 				xl_rxfilter(sc);
3014dd0a7688SPyun YongHyeon 			else
301583825b71SWarner Losh 				xl_init_locked(sc);
301683825b71SWarner Losh 		} else {
301783825b71SWarner Losh 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
301883825b71SWarner Losh 				xl_stop(sc);
301983825b71SWarner Losh 		}
302083825b71SWarner Losh 		sc->xl_if_flags = ifp->if_flags;
302183825b71SWarner Losh 		XL_UNLOCK(sc);
302283825b71SWarner Losh 		break;
302383825b71SWarner Losh 	case SIOCADDMULTI:
302483825b71SWarner Losh 	case SIOCDELMULTI:
302583825b71SWarner Losh 		/* XXX Downcall from if_addmulti() possibly with locks held. */
302683825b71SWarner Losh 		XL_LOCK(sc);
3027dd0a7688SPyun YongHyeon 		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3028dd0a7688SPyun YongHyeon 			xl_rxfilter(sc);
302983825b71SWarner Losh 		XL_UNLOCK(sc);
303083825b71SWarner Losh 		break;
303183825b71SWarner Losh 	case SIOCGIFMEDIA:
303283825b71SWarner Losh 	case SIOCSIFMEDIA:
303383825b71SWarner Losh 		if (sc->xl_miibus != NULL)
303483825b71SWarner Losh 			mii = device_get_softc(sc->xl_miibus);
303583825b71SWarner Losh 		if (mii == NULL)
303683825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
303783825b71SWarner Losh 			    &sc->ifmedia, command);
303883825b71SWarner Losh 		else
303983825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
304083825b71SWarner Losh 			    &mii->mii_media, command);
304183825b71SWarner Losh 		break;
304283825b71SWarner Losh 	case SIOCSIFCAP:
3043a3835274SPyun YongHyeon 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
304483825b71SWarner Losh #ifdef DEVICE_POLLING
3045a3835274SPyun YongHyeon 		if ((mask & IFCAP_POLLING) != 0 &&
3046a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_POLLING) != 0) {
3047a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_POLLING;
3048a3835274SPyun YongHyeon 			if ((ifp->if_capenable & IFCAP_POLLING) != 0) {
304983825b71SWarner Losh 				error = ether_poll_register(xl_poll, ifp);
305083825b71SWarner Losh 				if (error)
3051a3835274SPyun YongHyeon 					break;
305283825b71SWarner Losh 				XL_LOCK(sc);
305383825b71SWarner Losh 				/* Disable interrupts */
305483825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
305583825b71SWarner Losh 				ifp->if_capenable |= IFCAP_POLLING;
305683825b71SWarner Losh 				XL_UNLOCK(sc);
3057a3835274SPyun YongHyeon 			} else {
305883825b71SWarner Losh 				error = ether_poll_deregister(ifp);
305983825b71SWarner Losh 				/* Enable interrupts. */
306083825b71SWarner Losh 				XL_LOCK(sc);
3061a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3062a3835274SPyun YongHyeon 				    XL_CMD_INTR_ACK | 0xFF);
3063a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3064a3835274SPyun YongHyeon 				    XL_CMD_INTR_ENB | XL_INTRS);
306583825b71SWarner Losh 				if (sc->xl_flags & XL_FLAG_FUNCREG)
3066a3835274SPyun YongHyeon 					bus_space_write_4(sc->xl_ftag,
3067a3835274SPyun YongHyeon 					    sc->xl_fhandle, 4, 0x8000);
306883825b71SWarner Losh 				XL_UNLOCK(sc);
3069a3835274SPyun YongHyeon 			}
307083825b71SWarner Losh 		}
307183825b71SWarner Losh #endif /* DEVICE_POLLING */
307283825b71SWarner Losh 		XL_LOCK(sc);
3073a3835274SPyun YongHyeon 		if ((mask & IFCAP_TXCSUM) != 0 &&
3074a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
3075a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_TXCSUM;
3076a3835274SPyun YongHyeon 			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
3077a3835274SPyun YongHyeon 				ifp->if_hwassist |= XL905B_CSUM_FEATURES;
307883825b71SWarner Losh 			else
3079a3835274SPyun YongHyeon 				ifp->if_hwassist &= ~XL905B_CSUM_FEATURES;
3080a3835274SPyun YongHyeon 		}
3081a3835274SPyun YongHyeon 		if ((mask & IFCAP_RXCSUM) != 0 &&
3082a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
3083a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_RXCSUM;
30849ae11bbaSPyun YongHyeon 		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
30859ae11bbaSPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0)
30869ae11bbaSPyun YongHyeon 			ifp->if_capenable ^= IFCAP_WOL_MAGIC;
308783825b71SWarner Losh 		XL_UNLOCK(sc);
308883825b71SWarner Losh 		break;
308983825b71SWarner Losh 	default:
309083825b71SWarner Losh 		error = ether_ioctl(ifp, command, data);
309183825b71SWarner Losh 		break;
309283825b71SWarner Losh 	}
309383825b71SWarner Losh 
309483825b71SWarner Losh 	return (error);
309583825b71SWarner Losh }
309683825b71SWarner Losh 
309783825b71SWarner Losh static int
309883825b71SWarner Losh xl_watchdog(struct xl_softc *sc)
309983825b71SWarner Losh {
310083825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
310183825b71SWarner Losh 	u_int16_t		status = 0;
31022b574f31SPyun YongHyeon 	int			misintr;
310383825b71SWarner Losh 
310483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
310583825b71SWarner Losh 
310683825b71SWarner Losh 	if (sc->xl_wdog_timer == 0 || --sc->xl_wdog_timer != 0)
310783825b71SWarner Losh 		return (0);
310883825b71SWarner Losh 
31092b574f31SPyun YongHyeon 	xl_rxeof(sc);
31102b574f31SPyun YongHyeon 	xl_txeoc(sc);
31112b574f31SPyun YongHyeon 	misintr = 0;
31122b574f31SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B) {
31132b574f31SPyun YongHyeon 		xl_txeof_90xB(sc);
31142b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_cnt == 0)
31152b574f31SPyun YongHyeon 			misintr++;
31162b574f31SPyun YongHyeon 	} else {
31172b574f31SPyun YongHyeon 		xl_txeof(sc);
31182b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_head == NULL)
31192b574f31SPyun YongHyeon 			misintr++;
31202b574f31SPyun YongHyeon 	}
31212b574f31SPyun YongHyeon 	if (misintr != 0) {
31222b574f31SPyun YongHyeon 		device_printf(sc->xl_dev,
31232b574f31SPyun YongHyeon 		    "watchdog timeout (missed Tx interrupts) -- recovering\n");
31242b574f31SPyun YongHyeon 		return (0);
31252b574f31SPyun YongHyeon 	}
31262b574f31SPyun YongHyeon 
3127ec4a8977SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
312883825b71SWarner Losh 	XL_SEL_WIN(4);
312983825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
313083825b71SWarner Losh 	device_printf(sc->xl_dev, "watchdog timeout\n");
313183825b71SWarner Losh 
313283825b71SWarner Losh 	if (status & XL_MEDIASTAT_CARRIER)
313383825b71SWarner Losh 		device_printf(sc->xl_dev,
313483825b71SWarner Losh 		    "no carrier - transceiver cable problem?\n");
313583825b71SWarner Losh 
313627b031a9SPyun YongHyeon 	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
313783825b71SWarner Losh 	xl_init_locked(sc);
313883825b71SWarner Losh 
313983825b71SWarner Losh 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
314083825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
314183825b71SWarner Losh 			xl_start_90xB_locked(ifp);
314283825b71SWarner Losh 		else
314383825b71SWarner Losh 			xl_start_locked(ifp);
314483825b71SWarner Losh 	}
314583825b71SWarner Losh 
314683825b71SWarner Losh 	return (EJUSTRETURN);
314783825b71SWarner Losh }
314883825b71SWarner Losh 
314983825b71SWarner Losh /*
315083825b71SWarner Losh  * Stop the adapter and free any mbufs allocated to the
315183825b71SWarner Losh  * RX and TX lists.
315283825b71SWarner Losh  */
315383825b71SWarner Losh static void
315483825b71SWarner Losh xl_stop(struct xl_softc *sc)
315583825b71SWarner Losh {
3156*3e85b721SEd Maste 	int			i;
315783825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
315883825b71SWarner Losh 
315983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
316083825b71SWarner Losh 
316183825b71SWarner Losh 	sc->xl_wdog_timer = 0;
316283825b71SWarner Losh 
316383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
316483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
316583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
316683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
316783825b71SWarner Losh 	xl_wait(sc);
316883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
316983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
317083825b71SWarner Losh 	DELAY(800);
317183825b71SWarner Losh 
317283825b71SWarner Losh #ifdef foo
317383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
317483825b71SWarner Losh 	xl_wait(sc);
317583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
317683825b71SWarner Losh 	xl_wait(sc);
317783825b71SWarner Losh #endif
317883825b71SWarner Losh 
317983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
318083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
318183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
318283825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
318383825b71SWarner Losh 		bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
318483825b71SWarner Losh 
318583825b71SWarner Losh 	/* Stop the stats updater. */
318648dcbc33SPyun YongHyeon 	callout_stop(&sc->xl_tick_callout);
318783825b71SWarner Losh 
318883825b71SWarner Losh 	/*
318983825b71SWarner Losh 	 * Free data in the RX lists.
319083825b71SWarner Losh 	 */
319183825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
319283825b71SWarner Losh 		if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) {
319383825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
319483825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
319583825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
319683825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
319783825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf);
319883825b71SWarner Losh 			sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL;
319983825b71SWarner Losh 		}
320083825b71SWarner Losh 	}
320183825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_list != NULL)
320283825b71SWarner Losh 		bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
320383825b71SWarner Losh 	/*
320483825b71SWarner Losh 	 * Free the TX list buffers.
320583825b71SWarner Losh 	 */
320683825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
320783825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) {
320883825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
320983825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
321083825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
321183825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
321283825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf);
321383825b71SWarner Losh 			sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL;
321483825b71SWarner Losh 		}
321583825b71SWarner Losh 	}
321683825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_list != NULL)
321783825b71SWarner Losh 		bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
321883825b71SWarner Losh 
321983825b71SWarner Losh 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
322083825b71SWarner Losh }
322183825b71SWarner Losh 
322283825b71SWarner Losh /*
322383825b71SWarner Losh  * Stop all chip I/O so that the kernel's probe routines don't
322483825b71SWarner Losh  * get confused by errant DMAs when rebooting.
322583825b71SWarner Losh  */
322683825b71SWarner Losh static int
322783825b71SWarner Losh xl_shutdown(device_t dev)
322883825b71SWarner Losh {
322983825b71SWarner Losh 
32309ae11bbaSPyun YongHyeon 	return (xl_suspend(dev));
323183825b71SWarner Losh }
323283825b71SWarner Losh 
323383825b71SWarner Losh static int
323483825b71SWarner Losh xl_suspend(device_t dev)
323583825b71SWarner Losh {
323683825b71SWarner Losh 	struct xl_softc		*sc;
323783825b71SWarner Losh 
323883825b71SWarner Losh 	sc = device_get_softc(dev);
323983825b71SWarner Losh 
324083825b71SWarner Losh 	XL_LOCK(sc);
324183825b71SWarner Losh 	xl_stop(sc);
32429ae11bbaSPyun YongHyeon 	xl_setwol(sc);
324383825b71SWarner Losh 	XL_UNLOCK(sc);
324483825b71SWarner Losh 
324583825b71SWarner Losh 	return (0);
324683825b71SWarner Losh }
324783825b71SWarner Losh 
324883825b71SWarner Losh static int
324983825b71SWarner Losh xl_resume(device_t dev)
325083825b71SWarner Losh {
325183825b71SWarner Losh 	struct xl_softc		*sc;
325283825b71SWarner Losh 	struct ifnet		*ifp;
325383825b71SWarner Losh 
325483825b71SWarner Losh 	sc = device_get_softc(dev);
325583825b71SWarner Losh 	ifp = sc->xl_ifp;
325683825b71SWarner Losh 
325783825b71SWarner Losh 	XL_LOCK(sc);
325883825b71SWarner Losh 
325927b031a9SPyun YongHyeon 	if (ifp->if_flags & IFF_UP) {
326027b031a9SPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
326183825b71SWarner Losh 		xl_init_locked(sc);
326227b031a9SPyun YongHyeon 	}
326383825b71SWarner Losh 
326483825b71SWarner Losh 	XL_UNLOCK(sc);
326583825b71SWarner Losh 
326683825b71SWarner Losh 	return (0);
326783825b71SWarner Losh }
32689ae11bbaSPyun YongHyeon 
32699ae11bbaSPyun YongHyeon static void
32709ae11bbaSPyun YongHyeon xl_setwol(struct xl_softc *sc)
32719ae11bbaSPyun YongHyeon {
32729ae11bbaSPyun YongHyeon 	struct ifnet		*ifp;
32739ae11bbaSPyun YongHyeon 	u_int16_t		cfg, pmstat;
32749ae11bbaSPyun YongHyeon 
32759ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) == 0)
32769ae11bbaSPyun YongHyeon 		return;
32779ae11bbaSPyun YongHyeon 
32789ae11bbaSPyun YongHyeon 	ifp = sc->xl_ifp;
32799ae11bbaSPyun YongHyeon 	XL_SEL_WIN(7);
32809ae11bbaSPyun YongHyeon 	/* Clear any pending PME events. */
32819ae11bbaSPyun YongHyeon 	CSR_READ_2(sc, XL_W7_BM_PME);
32829ae11bbaSPyun YongHyeon 	cfg = 0;
32839ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
32849ae11bbaSPyun YongHyeon 		cfg |= XL_BM_PME_MAGIC;
32859ae11bbaSPyun YongHyeon 	CSR_WRITE_2(sc, XL_W7_BM_PME, cfg);
32869ae11bbaSPyun YongHyeon 	/* Enable RX. */
32879ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
32889ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
32899ae11bbaSPyun YongHyeon 	/* Request PME. */
32909ae11bbaSPyun YongHyeon 	pmstat = pci_read_config(sc->xl_dev,
32919ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, 2);
32929ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
32939ae11bbaSPyun YongHyeon 		pmstat |= PCIM_PSTAT_PMEENABLE;
32949ae11bbaSPyun YongHyeon 	else
32959ae11bbaSPyun YongHyeon 		pmstat &= ~PCIM_PSTAT_PMEENABLE;
32969ae11bbaSPyun YongHyeon 	pci_write_config(sc->xl_dev,
32979ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, pmstat, 2);
32989ae11bbaSPyun YongHyeon }
3299