xref: /freebsd/sys/dev/xl/if_xl.c (revision de53eba037c9ea78a160d264450535ff27097a7c)
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 /*
7883825b71SWarner Losh  * The 3c90x series chips use a bus-master DMA interface for transfering
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/mbuf.h>
11083825b71SWarner Losh #include <sys/kernel.h>
11183825b71SWarner Losh #include <sys/module.h>
11283825b71SWarner Losh #include <sys/socket.h>
11383825b71SWarner Losh #include <sys/taskqueue.h>
11483825b71SWarner Losh 
11583825b71SWarner Losh #include <net/if.h>
11683825b71SWarner Losh #include <net/if_arp.h>
11783825b71SWarner Losh #include <net/ethernet.h>
11883825b71SWarner Losh #include <net/if_dl.h>
11983825b71SWarner Losh #include <net/if_media.h>
12083825b71SWarner Losh #include <net/if_types.h>
12183825b71SWarner Losh 
12283825b71SWarner Losh #include <net/bpf.h>
12383825b71SWarner Losh 
12483825b71SWarner Losh #include <machine/bus.h>
12583825b71SWarner Losh #include <machine/resource.h>
12683825b71SWarner Losh #include <sys/bus.h>
12783825b71SWarner Losh #include <sys/rman.h>
12883825b71SWarner Losh 
12983825b71SWarner Losh #include <dev/mii/mii.h>
13083825b71SWarner Losh #include <dev/mii/miivar.h>
13183825b71SWarner Losh 
13283825b71SWarner Losh #include <dev/pci/pcireg.h>
13383825b71SWarner Losh #include <dev/pci/pcivar.h>
13483825b71SWarner Losh 
13583825b71SWarner Losh MODULE_DEPEND(xl, pci, 1, 1, 1);
13683825b71SWarner Losh MODULE_DEPEND(xl, ether, 1, 1, 1);
13783825b71SWarner Losh MODULE_DEPEND(xl, miibus, 1, 1, 1);
13883825b71SWarner Losh 
13983825b71SWarner Losh /* "device miibus" required.  See GENERIC if you get errors here. */
14083825b71SWarner Losh #include "miibus_if.h"
14183825b71SWarner Losh 
14283825b71SWarner Losh #include <dev/xl/if_xlreg.h>
14383825b71SWarner Losh 
14483825b71SWarner Losh /*
14583825b71SWarner Losh  * TX Checksumming is disabled by default for two reasons:
14683825b71SWarner Losh  * - TX Checksumming will occasionally produce corrupt packets
14783825b71SWarner Losh  * - TX Checksumming seems to reduce performance
14883825b71SWarner Losh  *
14983825b71SWarner Losh  * Only 905B/C cards were reported to have this problem, it is possible
15083825b71SWarner Losh  * that later chips _may_ be immune.
15183825b71SWarner Losh  */
15283825b71SWarner Losh #define	XL905B_TXCSUM_BROKEN	1
15383825b71SWarner Losh 
15483825b71SWarner Losh #ifdef XL905B_TXCSUM_BROKEN
15583825b71SWarner Losh #define XL905B_CSUM_FEATURES	0
15683825b71SWarner Losh #else
15783825b71SWarner Losh #define XL905B_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
15883825b71SWarner Losh #endif
15983825b71SWarner Losh 
16083825b71SWarner Losh /*
16183825b71SWarner Losh  * Various supported device vendors/types and their names.
16283825b71SWarner Losh  */
16383825b71SWarner Losh static const struct xl_type xl_devs[] = {
16483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT,
16583825b71SWarner Losh 		"3Com 3c900-TPO Etherlink XL" },
16683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO,
16783825b71SWarner Losh 		"3Com 3c900-COMBO Etherlink XL" },
16883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT,
16983825b71SWarner Losh 		"3Com 3c905-TX Fast Etherlink XL" },
17083825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4,
17183825b71SWarner Losh 		"3Com 3c905-T4 Fast Etherlink XL" },
17283825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT,
17383825b71SWarner Losh 		"3Com 3c900B-TPO Etherlink XL" },
17483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO,
17583825b71SWarner Losh 		"3Com 3c900B-COMBO Etherlink XL" },
17683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC,
17783825b71SWarner Losh 		"3Com 3c900B-TPC Etherlink XL" },
17883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10FL,
17983825b71SWarner Losh 		"3Com 3c900B-FL Etherlink XL" },
18083825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT,
18183825b71SWarner Losh 		"3Com 3c905B-TX Fast Etherlink XL" },
18283825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4,
18383825b71SWarner Losh 		"3Com 3c905B-T4 Fast Etherlink XL" },
18483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX,
18583825b71SWarner Losh 		"3Com 3c905B-FX/SC Fast Etherlink XL" },
18683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO,
18783825b71SWarner Losh 		"3Com 3c905B-COMBO Fast Etherlink XL" },
18883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT,
18983825b71SWarner Losh 		"3Com 3c905C-TX Fast Etherlink XL" },
19083825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B,
19183825b71SWarner Losh 		"3Com 3c920B-EMB Integrated Fast Etherlink XL" },
19283825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B_WNM,
19383825b71SWarner Losh 		"3Com 3c920B-EMB-WNM Integrated Fast Etherlink XL" },
19483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV,
19583825b71SWarner Losh 		"3Com 3c980 Fast Etherlink XL" },
19683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV,
19783825b71SWarner Losh 		"3Com 3c980C Fast Etherlink XL" },
19883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX,
19983825b71SWarner Losh 		"3Com 3cSOHO100-TX OfficeConnect" },
20083825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT,
20183825b71SWarner Losh 		"3Com 3c450-TX HomeConnect" },
20283825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_555,
20383825b71SWarner Losh 		"3Com 3c555 Fast Etherlink XL" },
20483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_556,
20583825b71SWarner Losh 		"3Com 3c556 Fast Etherlink XL" },
20683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_556B,
20783825b71SWarner Losh 		"3Com 3c556B Fast Etherlink XL" },
20883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575A,
20983825b71SWarner Losh 		"3Com 3c575TX Fast Etherlink XL" },
21083825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575B,
21183825b71SWarner Losh 		"3Com 3c575B Fast Etherlink XL" },
21283825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_575C,
21383825b71SWarner Losh 		"3Com 3c575C Fast Etherlink XL" },
21483825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_656,
21583825b71SWarner Losh 		"3Com 3c656 Fast Etherlink XL" },
21683825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_HURRICANE_656B,
21783825b71SWarner Losh 		"3Com 3c656B Fast Etherlink XL" },
21883825b71SWarner Losh 	{ TC_VENDORID, TC_DEVICEID_TORNADO_656C,
21983825b71SWarner Losh 		"3Com 3c656C Fast Etherlink XL" },
22083825b71SWarner Losh 	{ 0, 0, NULL }
22183825b71SWarner Losh };
22283825b71SWarner Losh 
22383825b71SWarner Losh static int xl_probe(device_t);
22483825b71SWarner Losh static int xl_attach(device_t);
22583825b71SWarner Losh static int xl_detach(device_t);
22683825b71SWarner Losh 
22783825b71SWarner Losh static int xl_newbuf(struct xl_softc *, struct xl_chain_onefrag *);
22848dcbc33SPyun YongHyeon static void xl_tick(void *);
22948dcbc33SPyun YongHyeon static void xl_stats_update(struct xl_softc *);
23083825b71SWarner Losh static int xl_encap(struct xl_softc *, struct xl_chain *, struct mbuf **);
2311abcdbd1SAttilio Rao static int xl_rxeof(struct xl_softc *);
23283825b71SWarner Losh static void xl_rxeof_task(void *, int);
23383825b71SWarner Losh static int xl_rx_resync(struct xl_softc *);
23483825b71SWarner Losh static void xl_txeof(struct xl_softc *);
23583825b71SWarner Losh static void xl_txeof_90xB(struct xl_softc *);
23683825b71SWarner Losh static void xl_txeoc(struct xl_softc *);
23783825b71SWarner Losh static void xl_intr(void *);
23883825b71SWarner Losh static void xl_start(struct ifnet *);
23983825b71SWarner Losh static void xl_start_locked(struct ifnet *);
24083825b71SWarner Losh static void xl_start_90xB_locked(struct ifnet *);
24183825b71SWarner Losh static int xl_ioctl(struct ifnet *, u_long, caddr_t);
24283825b71SWarner Losh static void xl_init(void *);
24383825b71SWarner Losh static void xl_init_locked(struct xl_softc *);
24483825b71SWarner Losh static void xl_stop(struct xl_softc *);
24583825b71SWarner Losh static int xl_watchdog(struct xl_softc *);
24683825b71SWarner Losh static int xl_shutdown(device_t);
24783825b71SWarner Losh static int xl_suspend(device_t);
24883825b71SWarner Losh static int xl_resume(device_t);
2499ae11bbaSPyun YongHyeon static void xl_setwol(struct xl_softc *);
25083825b71SWarner Losh 
25183825b71SWarner Losh #ifdef DEVICE_POLLING
2521abcdbd1SAttilio Rao static int xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count);
2531abcdbd1SAttilio Rao static int xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count);
25483825b71SWarner Losh #endif
25583825b71SWarner Losh 
25683825b71SWarner Losh static int xl_ifmedia_upd(struct ifnet *);
25783825b71SWarner Losh static void xl_ifmedia_sts(struct ifnet *, struct ifmediareq *);
25883825b71SWarner Losh 
25983825b71SWarner Losh static int xl_eeprom_wait(struct xl_softc *);
26083825b71SWarner Losh static int xl_read_eeprom(struct xl_softc *, caddr_t, int, int, int);
26183825b71SWarner Losh static void xl_mii_sync(struct xl_softc *);
26283825b71SWarner Losh static void xl_mii_send(struct xl_softc *, u_int32_t, int);
26383825b71SWarner Losh static int xl_mii_readreg(struct xl_softc *, struct xl_mii_frame *);
26483825b71SWarner Losh static int xl_mii_writereg(struct xl_softc *, struct xl_mii_frame *);
26583825b71SWarner Losh 
266dd0a7688SPyun YongHyeon static void xl_rxfilter(struct xl_softc *);
267dd0a7688SPyun YongHyeon static void xl_rxfilter_90x(struct xl_softc *);
268dd0a7688SPyun YongHyeon static void xl_rxfilter_90xB(struct xl_softc *);
26983825b71SWarner Losh static void xl_setcfg(struct xl_softc *);
27083825b71SWarner Losh static void xl_setmode(struct xl_softc *, int);
27183825b71SWarner Losh static void xl_reset(struct xl_softc *);
27283825b71SWarner Losh static int xl_list_rx_init(struct xl_softc *);
27383825b71SWarner Losh static int xl_list_tx_init(struct xl_softc *);
27483825b71SWarner Losh static int xl_list_tx_init_90xB(struct xl_softc *);
27583825b71SWarner Losh static void xl_wait(struct xl_softc *);
27683825b71SWarner Losh static void xl_mediacheck(struct xl_softc *);
27783825b71SWarner Losh static void xl_choose_media(struct xl_softc *sc, int *media);
27883825b71SWarner Losh static void xl_choose_xcvr(struct xl_softc *, int);
27983825b71SWarner Losh static void xl_dma_map_addr(void *, bus_dma_segment_t *, int, int);
28083825b71SWarner Losh #ifdef notdef
28183825b71SWarner Losh static void xl_testpacket(struct xl_softc *);
28283825b71SWarner Losh #endif
28383825b71SWarner Losh 
28483825b71SWarner Losh static int xl_miibus_readreg(device_t, int, int);
28583825b71SWarner Losh static int xl_miibus_writereg(device_t, int, int, int);
28683825b71SWarner Losh static void xl_miibus_statchg(device_t);
28783825b71SWarner Losh static void xl_miibus_mediainit(device_t);
28883825b71SWarner Losh 
28983825b71SWarner Losh static device_method_t xl_methods[] = {
29083825b71SWarner Losh 	/* Device interface */
29183825b71SWarner Losh 	DEVMETHOD(device_probe,		xl_probe),
29283825b71SWarner Losh 	DEVMETHOD(device_attach,	xl_attach),
29383825b71SWarner Losh 	DEVMETHOD(device_detach,	xl_detach),
29483825b71SWarner Losh 	DEVMETHOD(device_shutdown,	xl_shutdown),
29583825b71SWarner Losh 	DEVMETHOD(device_suspend,	xl_suspend),
29683825b71SWarner Losh 	DEVMETHOD(device_resume,	xl_resume),
29783825b71SWarner Losh 
29883825b71SWarner Losh 	/* bus interface */
29983825b71SWarner Losh 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
30083825b71SWarner Losh 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
30183825b71SWarner Losh 
30283825b71SWarner Losh 	/* MII interface */
30383825b71SWarner Losh 	DEVMETHOD(miibus_readreg,	xl_miibus_readreg),
30483825b71SWarner Losh 	DEVMETHOD(miibus_writereg,	xl_miibus_writereg),
30583825b71SWarner Losh 	DEVMETHOD(miibus_statchg,	xl_miibus_statchg),
30683825b71SWarner Losh 	DEVMETHOD(miibus_mediainit,	xl_miibus_mediainit),
30783825b71SWarner Losh 
30883825b71SWarner Losh 	{ 0, 0 }
30983825b71SWarner Losh };
31083825b71SWarner Losh 
31183825b71SWarner Losh static driver_t xl_driver = {
31283825b71SWarner Losh 	"xl",
31383825b71SWarner Losh 	xl_methods,
31483825b71SWarner Losh 	sizeof(struct xl_softc)
31583825b71SWarner Losh };
31683825b71SWarner Losh 
31783825b71SWarner Losh static devclass_t xl_devclass;
31883825b71SWarner Losh 
31983825b71SWarner Losh DRIVER_MODULE(xl, pci, xl_driver, xl_devclass, 0, 0);
32083825b71SWarner Losh DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, 0, 0);
32183825b71SWarner Losh 
32283825b71SWarner Losh static void
32383825b71SWarner Losh xl_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
32483825b71SWarner Losh {
32583825b71SWarner Losh 	u_int32_t *paddr;
32683825b71SWarner Losh 
32783825b71SWarner Losh 	paddr = arg;
32883825b71SWarner Losh 	*paddr = segs->ds_addr;
32983825b71SWarner Losh }
33083825b71SWarner Losh 
33183825b71SWarner Losh /*
33283825b71SWarner Losh  * Murphy's law says that it's possible the chip can wedge and
33383825b71SWarner Losh  * the 'command in progress' bit may never clear. Hence, we wait
33483825b71SWarner Losh  * only a finite amount of time to avoid getting caught in an
33583825b71SWarner Losh  * infinite loop. Normally this delay routine would be a macro,
33683825b71SWarner Losh  * but it isn't called during normal operation so we can afford
33783825b71SWarner Losh  * to make it a function.
33883825b71SWarner Losh  */
33983825b71SWarner Losh static void
34083825b71SWarner Losh xl_wait(struct xl_softc *sc)
34183825b71SWarner Losh {
34283825b71SWarner Losh 	register int		i;
34383825b71SWarner Losh 
34483825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
34583825b71SWarner Losh 		if ((CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY) == 0)
34683825b71SWarner Losh 			break;
34783825b71SWarner Losh 	}
34883825b71SWarner Losh 
34983825b71SWarner Losh 	if (i == XL_TIMEOUT)
35083825b71SWarner Losh 		device_printf(sc->xl_dev, "command never completed!\n");
35183825b71SWarner Losh }
35283825b71SWarner Losh 
35383825b71SWarner Losh /*
35483825b71SWarner Losh  * MII access routines are provided for adapters with external
35583825b71SWarner Losh  * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
35683825b71SWarner Losh  * autoneg logic that's faked up to look like a PHY (3c905B-TX).
35783825b71SWarner Losh  * Note: if you don't perform the MDIO operations just right,
35883825b71SWarner Losh  * it's possible to end up with code that works correctly with
35983825b71SWarner Losh  * some chips/CPUs/processor speeds/bus speeds/etc but not
36083825b71SWarner Losh  * with others.
36183825b71SWarner Losh  */
36283825b71SWarner Losh #define MII_SET(x)					\
36383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_PHY_MGMT,			\
36483825b71SWarner Losh 		CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x))
36583825b71SWarner Losh 
36683825b71SWarner Losh #define MII_CLR(x)					\
36783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_PHY_MGMT,			\
36883825b71SWarner Losh 		CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x))
36983825b71SWarner Losh 
37083825b71SWarner Losh /*
37183825b71SWarner Losh  * Sync the PHYs by setting data bit and strobing the clock 32 times.
37283825b71SWarner Losh  */
37383825b71SWarner Losh static void
37483825b71SWarner Losh xl_mii_sync(struct xl_softc *sc)
37583825b71SWarner Losh {
37683825b71SWarner Losh 	register int		i;
37783825b71SWarner Losh 
37883825b71SWarner Losh 	XL_SEL_WIN(4);
37983825b71SWarner Losh 	MII_SET(XL_MII_DIR|XL_MII_DATA);
38083825b71SWarner Losh 
38183825b71SWarner Losh 	for (i = 0; i < 32; i++) {
38283825b71SWarner Losh 		MII_SET(XL_MII_CLK);
38383825b71SWarner Losh 		MII_SET(XL_MII_DATA);
38483825b71SWarner Losh 		MII_SET(XL_MII_DATA);
38583825b71SWarner Losh 		MII_CLR(XL_MII_CLK);
38683825b71SWarner Losh 		MII_SET(XL_MII_DATA);
38783825b71SWarner Losh 		MII_SET(XL_MII_DATA);
38883825b71SWarner Losh 	}
38983825b71SWarner Losh }
39083825b71SWarner Losh 
39183825b71SWarner Losh /*
39283825b71SWarner Losh  * Clock a series of bits through the MII.
39383825b71SWarner Losh  */
39483825b71SWarner Losh static void
39583825b71SWarner Losh xl_mii_send(struct xl_softc *sc, u_int32_t bits, int cnt)
39683825b71SWarner Losh {
39783825b71SWarner Losh 	int			i;
39883825b71SWarner Losh 
39983825b71SWarner Losh 	XL_SEL_WIN(4);
40083825b71SWarner Losh 	MII_CLR(XL_MII_CLK);
40183825b71SWarner Losh 
40283825b71SWarner Losh 	for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
40383825b71SWarner Losh 		if (bits & i) {
40483825b71SWarner Losh 			MII_SET(XL_MII_DATA);
40583825b71SWarner Losh 		} else {
40683825b71SWarner Losh 			MII_CLR(XL_MII_DATA);
40783825b71SWarner Losh 		}
40883825b71SWarner Losh 		MII_CLR(XL_MII_CLK);
40983825b71SWarner Losh 		MII_SET(XL_MII_CLK);
41083825b71SWarner Losh 	}
41183825b71SWarner Losh }
41283825b71SWarner Losh 
41383825b71SWarner Losh /*
41483825b71SWarner Losh  * Read an PHY register through the MII.
41583825b71SWarner Losh  */
41683825b71SWarner Losh static int
41783825b71SWarner Losh xl_mii_readreg(struct xl_softc *sc, struct xl_mii_frame *frame)
41883825b71SWarner Losh {
41983825b71SWarner Losh 	int			i, ack;
42083825b71SWarner Losh 
42183825b71SWarner Losh 	/* Set up frame for RX. */
42283825b71SWarner Losh 	frame->mii_stdelim = XL_MII_STARTDELIM;
42383825b71SWarner Losh 	frame->mii_opcode = XL_MII_READOP;
42483825b71SWarner Losh 	frame->mii_turnaround = 0;
42583825b71SWarner Losh 	frame->mii_data = 0;
42683825b71SWarner Losh 
42783825b71SWarner Losh 	/* Select register window 4. */
42883825b71SWarner Losh 	XL_SEL_WIN(4);
42983825b71SWarner Losh 
43083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0);
43183825b71SWarner Losh 	/* Turn on data xmit. */
43283825b71SWarner Losh 	MII_SET(XL_MII_DIR);
43383825b71SWarner Losh 
43483825b71SWarner Losh 	xl_mii_sync(sc);
43583825b71SWarner Losh 
43683825b71SWarner Losh 	/* Send command/address info. */
43783825b71SWarner Losh 	xl_mii_send(sc, frame->mii_stdelim, 2);
43883825b71SWarner Losh 	xl_mii_send(sc, frame->mii_opcode, 2);
43983825b71SWarner Losh 	xl_mii_send(sc, frame->mii_phyaddr, 5);
44083825b71SWarner Losh 	xl_mii_send(sc, frame->mii_regaddr, 5);
44183825b71SWarner Losh 
44283825b71SWarner Losh 	/* Idle bit */
44383825b71SWarner Losh 	MII_CLR((XL_MII_CLK|XL_MII_DATA));
44483825b71SWarner Losh 	MII_SET(XL_MII_CLK);
44583825b71SWarner Losh 
44683825b71SWarner Losh 	/* Turn off xmit. */
44783825b71SWarner Losh 	MII_CLR(XL_MII_DIR);
44883825b71SWarner Losh 
44983825b71SWarner Losh 	/* Check for ack */
45083825b71SWarner Losh 	MII_CLR(XL_MII_CLK);
45183825b71SWarner Losh 	ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA;
45283825b71SWarner Losh 	MII_SET(XL_MII_CLK);
45383825b71SWarner Losh 
45483825b71SWarner Losh 	/*
45583825b71SWarner Losh 	 * Now try reading data bits. If the ack failed, we still
45683825b71SWarner Losh 	 * need to clock through 16 cycles to keep the PHY(s) in sync.
45783825b71SWarner Losh 	 */
45883825b71SWarner Losh 	if (ack) {
45983825b71SWarner Losh 		for (i = 0; i < 16; i++) {
46083825b71SWarner Losh 			MII_CLR(XL_MII_CLK);
46183825b71SWarner Losh 			MII_SET(XL_MII_CLK);
46283825b71SWarner Losh 		}
46383825b71SWarner Losh 		goto fail;
46483825b71SWarner Losh 	}
46583825b71SWarner Losh 
46683825b71SWarner Losh 	for (i = 0x8000; i; i >>= 1) {
46783825b71SWarner Losh 		MII_CLR(XL_MII_CLK);
46883825b71SWarner Losh 		if (!ack) {
46983825b71SWarner Losh 			if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA)
47083825b71SWarner Losh 				frame->mii_data |= i;
47183825b71SWarner Losh 		}
47283825b71SWarner Losh 		MII_SET(XL_MII_CLK);
47383825b71SWarner Losh 	}
47483825b71SWarner Losh 
47583825b71SWarner Losh fail:
47683825b71SWarner Losh 	MII_CLR(XL_MII_CLK);
47783825b71SWarner Losh 	MII_SET(XL_MII_CLK);
47883825b71SWarner Losh 
47983825b71SWarner Losh 	return (ack ? 1 : 0);
48083825b71SWarner Losh }
48183825b71SWarner Losh 
48283825b71SWarner Losh /*
48383825b71SWarner Losh  * Write to a PHY register through the MII.
48483825b71SWarner Losh  */
48583825b71SWarner Losh static int
48683825b71SWarner Losh xl_mii_writereg(struct xl_softc *sc, struct xl_mii_frame *frame)
48783825b71SWarner Losh {
48883825b71SWarner Losh 
48983825b71SWarner Losh 	/* Set up frame for TX. */
49083825b71SWarner Losh 	frame->mii_stdelim = XL_MII_STARTDELIM;
49183825b71SWarner Losh 	frame->mii_opcode = XL_MII_WRITEOP;
49283825b71SWarner Losh 	frame->mii_turnaround = XL_MII_TURNAROUND;
49383825b71SWarner Losh 
49483825b71SWarner Losh 	/* Select the window 4. */
49583825b71SWarner Losh 	XL_SEL_WIN(4);
49683825b71SWarner Losh 
49783825b71SWarner Losh 	/* Turn on data output. */
49883825b71SWarner Losh 	MII_SET(XL_MII_DIR);
49983825b71SWarner Losh 
50083825b71SWarner Losh 	xl_mii_sync(sc);
50183825b71SWarner Losh 
50283825b71SWarner Losh 	xl_mii_send(sc, frame->mii_stdelim, 2);
50383825b71SWarner Losh 	xl_mii_send(sc, frame->mii_opcode, 2);
50483825b71SWarner Losh 	xl_mii_send(sc, frame->mii_phyaddr, 5);
50583825b71SWarner Losh 	xl_mii_send(sc, frame->mii_regaddr, 5);
50683825b71SWarner Losh 	xl_mii_send(sc, frame->mii_turnaround, 2);
50783825b71SWarner Losh 	xl_mii_send(sc, frame->mii_data, 16);
50883825b71SWarner Losh 
50983825b71SWarner Losh 	/* Idle bit. */
51083825b71SWarner Losh 	MII_SET(XL_MII_CLK);
51183825b71SWarner Losh 	MII_CLR(XL_MII_CLK);
51283825b71SWarner Losh 
51383825b71SWarner Losh 	/* Turn off xmit. */
51483825b71SWarner Losh 	MII_CLR(XL_MII_DIR);
51583825b71SWarner Losh 
51683825b71SWarner Losh 	return (0);
51783825b71SWarner Losh }
51883825b71SWarner Losh 
51983825b71SWarner Losh static int
52083825b71SWarner Losh xl_miibus_readreg(device_t dev, int phy, int reg)
52183825b71SWarner Losh {
52283825b71SWarner Losh 	struct xl_softc		*sc;
52383825b71SWarner Losh 	struct xl_mii_frame	frame;
52483825b71SWarner Losh 
52583825b71SWarner Losh 	sc = device_get_softc(dev);
52683825b71SWarner Losh 
52783825b71SWarner Losh 	bzero((char *)&frame, sizeof(frame));
52883825b71SWarner Losh 	frame.mii_phyaddr = phy;
52983825b71SWarner Losh 	frame.mii_regaddr = reg;
53083825b71SWarner Losh 
53183825b71SWarner Losh 	xl_mii_readreg(sc, &frame);
53283825b71SWarner Losh 
53383825b71SWarner Losh 	return (frame.mii_data);
53483825b71SWarner Losh }
53583825b71SWarner Losh 
53683825b71SWarner Losh static int
53783825b71SWarner Losh xl_miibus_writereg(device_t dev, int phy, int reg, int data)
53883825b71SWarner Losh {
53983825b71SWarner Losh 	struct xl_softc		*sc;
54083825b71SWarner Losh 	struct xl_mii_frame	frame;
54183825b71SWarner Losh 
54283825b71SWarner Losh 	sc = device_get_softc(dev);
54383825b71SWarner Losh 
54483825b71SWarner Losh 	bzero((char *)&frame, sizeof(frame));
54583825b71SWarner Losh 	frame.mii_phyaddr = phy;
54683825b71SWarner Losh 	frame.mii_regaddr = reg;
54783825b71SWarner Losh 	frame.mii_data = data;
54883825b71SWarner Losh 
54983825b71SWarner Losh 	xl_mii_writereg(sc, &frame);
55083825b71SWarner Losh 
55183825b71SWarner Losh 	return (0);
55283825b71SWarner Losh }
55383825b71SWarner Losh 
55483825b71SWarner Losh static void
55583825b71SWarner Losh xl_miibus_statchg(device_t dev)
55683825b71SWarner Losh {
55783825b71SWarner Losh 	struct xl_softc		*sc;
55883825b71SWarner Losh 	struct mii_data		*mii;
559aee0e786SPyun YongHyeon 	uint8_t			macctl;
56083825b71SWarner Losh 
56183825b71SWarner Losh 	sc = device_get_softc(dev);
56283825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
56383825b71SWarner Losh 
56483825b71SWarner Losh 	xl_setcfg(sc);
56583825b71SWarner Losh 
56683825b71SWarner Losh 	/* Set ASIC's duplex mode to match the PHY. */
56783825b71SWarner Losh 	XL_SEL_WIN(3);
568aee0e786SPyun YongHyeon 	macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
569aee0e786SPyun YongHyeon 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
570aee0e786SPyun YongHyeon 		macctl |= XL_MACCTRL_DUPLEX;
571aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B) {
572aee0e786SPyun YongHyeon 			if ((IFM_OPTIONS(mii->mii_media_active) &
573aee0e786SPyun YongHyeon 			    IFM_ETH_RXPAUSE) != 0)
574aee0e786SPyun YongHyeon 				macctl |= XL_MACCTRL_FLOW_CONTROL_ENB;
57583825b71SWarner Losh 			else
576aee0e786SPyun YongHyeon 				macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
577aee0e786SPyun YongHyeon 		}
578aee0e786SPyun YongHyeon 	} else {
579aee0e786SPyun YongHyeon 		macctl &= ~XL_MACCTRL_DUPLEX;
580aee0e786SPyun YongHyeon 		if (sc->xl_type == XL_TYPE_905B)
581aee0e786SPyun YongHyeon 			macctl &= ~XL_MACCTRL_FLOW_CONTROL_ENB;
582aee0e786SPyun YongHyeon 	}
583aee0e786SPyun YongHyeon 	CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
58483825b71SWarner Losh }
58583825b71SWarner Losh 
58683825b71SWarner Losh /*
58783825b71SWarner Losh  * Special support for the 3c905B-COMBO. This card has 10/100 support
58883825b71SWarner Losh  * plus BNC and AUI ports. This means we will have both an miibus attached
58983825b71SWarner Losh  * plus some non-MII media settings. In order to allow this, we have to
59083825b71SWarner Losh  * add the extra media to the miibus's ifmedia struct, but we can't do
59183825b71SWarner Losh  * that during xl_attach() because the miibus hasn't been attached yet.
59283825b71SWarner Losh  * So instead, we wait until the miibus probe/attach is done, at which
59383825b71SWarner Losh  * point we will get a callback telling is that it's safe to add our
59483825b71SWarner Losh  * extra media.
59583825b71SWarner Losh  */
59683825b71SWarner Losh static void
59783825b71SWarner Losh xl_miibus_mediainit(device_t dev)
59883825b71SWarner Losh {
59983825b71SWarner Losh 	struct xl_softc		*sc;
60083825b71SWarner Losh 	struct mii_data		*mii;
60183825b71SWarner Losh 	struct ifmedia		*ifm;
60283825b71SWarner Losh 
60383825b71SWarner Losh 	sc = device_get_softc(dev);
60483825b71SWarner Losh 	mii = device_get_softc(sc->xl_miibus);
60583825b71SWarner Losh 	ifm = &mii->mii_media;
60683825b71SWarner Losh 
60783825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI | XL_MEDIAOPT_10FL)) {
60883825b71SWarner Losh 		/*
60983825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
61083825b71SWarner Losh 		 */
61183825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
61283825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
61383825b71SWarner Losh 			if (bootverbose)
61483825b71SWarner Losh 				device_printf(sc->xl_dev, "found 10baseFL\n");
61583825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL, 0, NULL);
61683825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_FL|IFM_HDX, 0,
61783825b71SWarner Losh 			    NULL);
61883825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
61983825b71SWarner Losh 				ifmedia_add(ifm,
62083825b71SWarner Losh 				    IFM_ETHER | IFM_10_FL | IFM_FDX, 0, NULL);
62183825b71SWarner Losh 		} else {
62283825b71SWarner Losh 			if (bootverbose)
62383825b71SWarner Losh 				device_printf(sc->xl_dev, "found AUI\n");
62483825b71SWarner Losh 			ifmedia_add(ifm, IFM_ETHER | IFM_10_5, 0, NULL);
62583825b71SWarner Losh 		}
62683825b71SWarner Losh 	}
62783825b71SWarner Losh 
62883825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
62983825b71SWarner Losh 		if (bootverbose)
63083825b71SWarner Losh 			device_printf(sc->xl_dev, "found BNC\n");
63183825b71SWarner Losh 		ifmedia_add(ifm, IFM_ETHER | IFM_10_2, 0, NULL);
63283825b71SWarner Losh 	}
63383825b71SWarner Losh }
63483825b71SWarner Losh 
63583825b71SWarner Losh /*
63683825b71SWarner Losh  * The EEPROM is slow: give it time to come ready after issuing
63783825b71SWarner Losh  * it a command.
63883825b71SWarner Losh  */
63983825b71SWarner Losh static int
64083825b71SWarner Losh xl_eeprom_wait(struct xl_softc *sc)
64183825b71SWarner Losh {
64283825b71SWarner Losh 	int			i;
64383825b71SWarner Losh 
64483825b71SWarner Losh 	for (i = 0; i < 100; i++) {
64583825b71SWarner Losh 		if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
64683825b71SWarner Losh 			DELAY(162);
64783825b71SWarner Losh 		else
64883825b71SWarner Losh 			break;
64983825b71SWarner Losh 	}
65083825b71SWarner Losh 
65183825b71SWarner Losh 	if (i == 100) {
65283825b71SWarner Losh 		device_printf(sc->xl_dev, "eeprom failed to come ready\n");
65383825b71SWarner Losh 		return (1);
65483825b71SWarner Losh 	}
65583825b71SWarner Losh 
65683825b71SWarner Losh 	return (0);
65783825b71SWarner Losh }
65883825b71SWarner Losh 
65983825b71SWarner Losh /*
66083825b71SWarner Losh  * Read a sequence of words from the EEPROM. Note that ethernet address
66183825b71SWarner Losh  * data is stored in the EEPROM in network byte order.
66283825b71SWarner Losh  */
66383825b71SWarner Losh static int
66483825b71SWarner Losh xl_read_eeprom(struct xl_softc *sc, caddr_t dest, int off, int cnt, int swap)
66583825b71SWarner Losh {
66683825b71SWarner Losh 	int			err = 0, i;
66783825b71SWarner Losh 	u_int16_t		word = 0, *ptr;
66883825b71SWarner Losh 
66983825b71SWarner Losh #define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
67083825b71SWarner Losh #define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
67183825b71SWarner Losh 	/*
67283825b71SWarner Losh 	 * XXX: WARNING! DANGER!
67383825b71SWarner Losh 	 * It's easy to accidentally overwrite the rom content!
67483825b71SWarner Losh 	 * Note: the 3c575 uses 8bit EEPROM offsets.
67583825b71SWarner Losh 	 */
67683825b71SWarner Losh 	XL_SEL_WIN(0);
67783825b71SWarner Losh 
67883825b71SWarner Losh 	if (xl_eeprom_wait(sc))
67983825b71SWarner Losh 		return (1);
68083825b71SWarner Losh 
68183825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
68283825b71SWarner Losh 		off += 0x30;
68383825b71SWarner Losh 
68483825b71SWarner Losh 	for (i = 0; i < cnt; i++) {
68583825b71SWarner Losh 		if (sc->xl_flags & XL_FLAG_8BITROM)
68683825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
68783825b71SWarner Losh 			    XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
68883825b71SWarner Losh 		else
68983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_W0_EE_CMD,
69083825b71SWarner Losh 			    XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
69183825b71SWarner Losh 		err = xl_eeprom_wait(sc);
69283825b71SWarner Losh 		if (err)
69383825b71SWarner Losh 			break;
69483825b71SWarner Losh 		word = CSR_READ_2(sc, XL_W0_EE_DATA);
69583825b71SWarner Losh 		ptr = (u_int16_t *)(dest + (i * 2));
69683825b71SWarner Losh 		if (swap)
69783825b71SWarner Losh 			*ptr = ntohs(word);
69883825b71SWarner Losh 		else
69983825b71SWarner Losh 			*ptr = word;
70083825b71SWarner Losh 	}
70183825b71SWarner Losh 
70283825b71SWarner Losh 	return (err ? 1 : 0);
70383825b71SWarner Losh }
70483825b71SWarner Losh 
705dd0a7688SPyun YongHyeon static void
706dd0a7688SPyun YongHyeon xl_rxfilter(struct xl_softc *sc)
707dd0a7688SPyun YongHyeon {
708dd0a7688SPyun YongHyeon 
709dd0a7688SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B)
710dd0a7688SPyun YongHyeon 		xl_rxfilter_90xB(sc);
711dd0a7688SPyun YongHyeon 	else
712dd0a7688SPyun YongHyeon 		xl_rxfilter_90x(sc);
713dd0a7688SPyun YongHyeon }
714dd0a7688SPyun YongHyeon 
71583825b71SWarner Losh /*
71683825b71SWarner Losh  * NICs older than the 3c905B have only one multicast option, which
71783825b71SWarner Losh  * is to enable reception of all multicast frames.
71883825b71SWarner Losh  */
71983825b71SWarner Losh static void
720dd0a7688SPyun YongHyeon xl_rxfilter_90x(struct xl_softc *sc)
72183825b71SWarner Losh {
722dd0a7688SPyun YongHyeon 	struct ifnet		*ifp;
72383825b71SWarner Losh 	struct ifmultiaddr	*ifma;
72483825b71SWarner Losh 	u_int8_t		rxfilt;
72583825b71SWarner Losh 
72683825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
72783825b71SWarner Losh 
728dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
729dd0a7688SPyun YongHyeon 
73083825b71SWarner Losh 	XL_SEL_WIN(5);
73183825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
732dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
733dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL);
73483825b71SWarner Losh 
735dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
736dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
737dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
738dd0a7688SPyun YongHyeon 	if (ifp->if_flags & IFF_BROADCAST)
739dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
740dd0a7688SPyun YongHyeon 
741dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
742dd0a7688SPyun YongHyeon 	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
743dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_PROMISC)
744dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
745dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_ALLMULTI)
74683825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
747dd0a7688SPyun YongHyeon 	} else {
748dd0a7688SPyun YongHyeon 		if_maddr_rlock(ifp);
749dd0a7688SPyun YongHyeon 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
750dd0a7688SPyun YongHyeon 			if (ifma->ifma_addr->sa_family != AF_LINK)
751dd0a7688SPyun YongHyeon 				continue;
752dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLMULTI;
753dd0a7688SPyun YongHyeon 			break;
754dd0a7688SPyun YongHyeon 		}
755dd0a7688SPyun YongHyeon 		if_maddr_runlock(ifp);
75683825b71SWarner Losh 	}
75783825b71SWarner Losh 
758dd0a7688SPyun YongHyeon 	CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
759dd0a7688SPyun YongHyeon 	XL_SEL_WIN(7);
76083825b71SWarner Losh }
76183825b71SWarner Losh 
76283825b71SWarner Losh /*
76383825b71SWarner Losh  * 3c905B adapters have a hash filter that we can program.
76483825b71SWarner Losh  */
76583825b71SWarner Losh static void
766dd0a7688SPyun YongHyeon xl_rxfilter_90xB(struct xl_softc *sc)
76783825b71SWarner Losh {
768dd0a7688SPyun YongHyeon 	struct ifnet		*ifp;
76983825b71SWarner Losh 	struct ifmultiaddr	*ifma;
770dd0a7688SPyun YongHyeon 	int			i, mcnt;
771dd0a7688SPyun YongHyeon 	u_int16_t		h;
77283825b71SWarner Losh 	u_int8_t		rxfilt;
77383825b71SWarner Losh 
77483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
77583825b71SWarner Losh 
776dd0a7688SPyun YongHyeon 	ifp = sc->xl_ifp;
777dd0a7688SPyun YongHyeon 
77883825b71SWarner Losh 	XL_SEL_WIN(5);
77983825b71SWarner Losh 	rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
780dd0a7688SPyun YongHyeon 	rxfilt &= ~(XL_RXFILTER_ALLFRAMES | XL_RXFILTER_ALLMULTI |
781dd0a7688SPyun YongHyeon 	    XL_RXFILTER_BROADCAST | XL_RXFILTER_INDIVIDUAL |
782dd0a7688SPyun YongHyeon 	    XL_RXFILTER_MULTIHASH);
78383825b71SWarner Losh 
784dd0a7688SPyun YongHyeon 	/* Set the individual bit to receive frames for this host only. */
785dd0a7688SPyun YongHyeon 	rxfilt |= XL_RXFILTER_INDIVIDUAL;
786dd0a7688SPyun YongHyeon 	/* Set capture broadcast bit to capture broadcast frames. */
787dd0a7688SPyun YongHyeon 	if (ifp->if_flags & IFF_BROADCAST)
788dd0a7688SPyun YongHyeon 		rxfilt |= XL_RXFILTER_BROADCAST;
789dd0a7688SPyun YongHyeon 
790dd0a7688SPyun YongHyeon 	/* If we want promiscuous mode, set the allframes bit. */
791dd0a7688SPyun YongHyeon 	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
792dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_PROMISC)
793dd0a7688SPyun YongHyeon 			rxfilt |= XL_RXFILTER_ALLFRAMES;
794dd0a7688SPyun YongHyeon 		if (ifp->if_flags & IFF_ALLMULTI)
79583825b71SWarner Losh 			rxfilt |= XL_RXFILTER_ALLMULTI;
796dd0a7688SPyun YongHyeon 	} else {
797dd0a7688SPyun YongHyeon 		/* First, zot all the existing hash bits. */
79883825b71SWarner Losh 		for (i = 0; i < XL_HASHFILT_SIZE; i++)
79983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH | i);
80083825b71SWarner Losh 
801dd0a7688SPyun YongHyeon 		/* Now program new ones. */
802dd0a7688SPyun YongHyeon 		mcnt = 0;
803eb956cd0SRobert Watson 		if_maddr_rlock(ifp);
80483825b71SWarner Losh 		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
80583825b71SWarner Losh 			if (ifma->ifma_addr->sa_family != AF_LINK)
80683825b71SWarner Losh 				continue;
80783825b71SWarner Losh 			/*
808dd0a7688SPyun YongHyeon 			 * Note: the 3c905B currently only supports a 64-bit
809dd0a7688SPyun YongHyeon 			 * hash table, which means we really only need 6 bits,
810dd0a7688SPyun YongHyeon 			 * but the manual indicates that future chip revisions
811dd0a7688SPyun YongHyeon 			 * will have a 256-bit hash table, hence the routine
812dd0a7688SPyun YongHyeon 			 * is set up to calculate 8 bits of position info in
813dd0a7688SPyun YongHyeon 			 * case we need it some day.
814dd0a7688SPyun YongHyeon 			 * Note II, The Sequel: _CURRENT_ versions of the
815dd0a7688SPyun YongHyeon 			 * 3c905B have a 256 bit hash table. This means we have
816dd0a7688SPyun YongHyeon 			 * to use all 8 bits regardless.  On older cards, the
817dd0a7688SPyun YongHyeon 			 * upper 2 bits will be ignored. Grrrr....
81883825b71SWarner Losh 			 */
81983825b71SWarner Losh 			h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
82083825b71SWarner Losh 			    ifma->ifma_addr), ETHER_ADDR_LEN) & 0xFF;
82183825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
82283825b71SWarner Losh 			    h | XL_CMD_RX_SET_HASH | XL_HASH_SET);
82383825b71SWarner Losh 			mcnt++;
82483825b71SWarner Losh 		}
825eb956cd0SRobert Watson 		if_maddr_runlock(ifp);
826dd0a7688SPyun YongHyeon 		if (mcnt > 0)
82783825b71SWarner Losh 			rxfilt |= XL_RXFILTER_MULTIHASH;
828dd0a7688SPyun YongHyeon 	}
82983825b71SWarner Losh 
83083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
831dd0a7688SPyun YongHyeon 	XL_SEL_WIN(7);
83283825b71SWarner Losh }
83383825b71SWarner Losh 
83483825b71SWarner Losh static void
83583825b71SWarner Losh xl_setcfg(struct xl_softc *sc)
83683825b71SWarner Losh {
83783825b71SWarner Losh 	u_int32_t		icfg;
83883825b71SWarner Losh 
83983825b71SWarner Losh 	/*XL_LOCK_ASSERT(sc);*/
84083825b71SWarner Losh 
84183825b71SWarner Losh 	XL_SEL_WIN(3);
84283825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
84383825b71SWarner Losh 	icfg &= ~XL_ICFG_CONNECTOR_MASK;
84483825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
84583825b71SWarner Losh 		sc->xl_media & XL_MEDIAOPT_BT4)
84683825b71SWarner Losh 		icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
84783825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BTX)
84883825b71SWarner Losh 		icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
84983825b71SWarner Losh 
85083825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
85183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
85283825b71SWarner Losh }
85383825b71SWarner Losh 
85483825b71SWarner Losh static void
85583825b71SWarner Losh xl_setmode(struct xl_softc *sc, int media)
85683825b71SWarner Losh {
85783825b71SWarner Losh 	u_int32_t		icfg;
85883825b71SWarner Losh 	u_int16_t		mediastat;
85983825b71SWarner Losh 	char			*pmsg = "", *dmsg = "";
86083825b71SWarner Losh 
86183825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
86283825b71SWarner Losh 
86383825b71SWarner Losh 	XL_SEL_WIN(4);
86483825b71SWarner Losh 	mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
86583825b71SWarner Losh 	XL_SEL_WIN(3);
86683825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
86783825b71SWarner Losh 
86883825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
86983825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_T) {
87083825b71SWarner Losh 			pmsg = "10baseT transceiver";
87183825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_10BT;
87283825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
87383825b71SWarner Losh 			icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
87483825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT |
87583825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD;
87683825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
87783825b71SWarner Losh 		}
87883825b71SWarner Losh 	}
87983825b71SWarner Losh 
88083825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
88183825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_100_FX) {
88283825b71SWarner Losh 			pmsg = "100baseFX port";
88383825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_100BFX;
88483825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
88583825b71SWarner Losh 			icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
88683825b71SWarner Losh 			mediastat |= XL_MEDIASTAT_LINKBEAT;
88783825b71SWarner Losh 			mediastat &= ~XL_MEDIASTAT_SQEENB;
88883825b71SWarner Losh 		}
88983825b71SWarner Losh 	}
89083825b71SWarner Losh 
89183825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
89283825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_5) {
89383825b71SWarner Losh 			pmsg = "AUI port";
89483825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
89583825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
89683825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
89783825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
89883825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
89983825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
90083825b71SWarner Losh 		}
90183825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_FL) {
90283825b71SWarner Losh 			pmsg = "10baseFL transceiver";
90383825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_AUI;
90483825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
90583825b71SWarner Losh 			icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
90683825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
90783825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD);
90883825b71SWarner Losh 			mediastat |= ~XL_MEDIASTAT_SQEENB;
90983825b71SWarner Losh 		}
91083825b71SWarner Losh 	}
91183825b71SWarner Losh 
91283825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
91383825b71SWarner Losh 		if (IFM_SUBTYPE(media) == IFM_10_2) {
91483825b71SWarner Losh 			pmsg = "AUI port";
91583825b71SWarner Losh 			sc->xl_xcvr = XL_XCVR_COAX;
91683825b71SWarner Losh 			icfg &= ~XL_ICFG_CONNECTOR_MASK;
91783825b71SWarner Losh 			icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
91883825b71SWarner Losh 			mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
91983825b71SWarner Losh 			    XL_MEDIASTAT_JABGUARD | XL_MEDIASTAT_SQEENB);
92083825b71SWarner Losh 		}
92183825b71SWarner Losh 	}
92283825b71SWarner Losh 
92383825b71SWarner Losh 	if ((media & IFM_GMASK) == IFM_FDX ||
92483825b71SWarner Losh 			IFM_SUBTYPE(media) == IFM_100_FX) {
92583825b71SWarner Losh 		dmsg = "full";
92683825b71SWarner Losh 		XL_SEL_WIN(3);
92783825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
92883825b71SWarner Losh 	} else {
92983825b71SWarner Losh 		dmsg = "half";
93083825b71SWarner Losh 		XL_SEL_WIN(3);
93183825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
93283825b71SWarner Losh 			(CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
93383825b71SWarner Losh 	}
93483825b71SWarner Losh 
93583825b71SWarner Losh 	if (IFM_SUBTYPE(media) == IFM_10_2)
93683825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
93783825b71SWarner Losh 	else
93883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
93983825b71SWarner Losh 
94083825b71SWarner Losh 	CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
94183825b71SWarner Losh 	XL_SEL_WIN(4);
94283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
94383825b71SWarner Losh 
94483825b71SWarner Losh 	DELAY(800);
94583825b71SWarner Losh 	XL_SEL_WIN(7);
94683825b71SWarner Losh 
94783825b71SWarner Losh 	device_printf(sc->xl_dev, "selecting %s, %s duplex\n", pmsg, dmsg);
94883825b71SWarner Losh }
94983825b71SWarner Losh 
95083825b71SWarner Losh static void
95183825b71SWarner Losh xl_reset(struct xl_softc *sc)
95283825b71SWarner Losh {
95383825b71SWarner Losh 	register int		i;
95483825b71SWarner Losh 
95583825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
95683825b71SWarner Losh 
95783825b71SWarner Losh 	XL_SEL_WIN(0);
95883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
95983825b71SWarner Losh 	    ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
96083825b71SWarner Losh 	     XL_RESETOPT_DISADVFD:0));
96183825b71SWarner Losh 
96283825b71SWarner Losh 	/*
96383825b71SWarner Losh 	 * If we're using memory mapped register mode, pause briefly
96483825b71SWarner Losh 	 * after issuing the reset command before trying to access any
965f33a1c16SWarner Losh 	 * other registers. With my 3c575C CardBus card, failing to do
96683825b71SWarner Losh 	 * this results in the system locking up while trying to poll
96783825b71SWarner Losh 	 * the command busy bit in the status register.
96883825b71SWarner Losh 	 */
96983825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO)
97083825b71SWarner Losh 		DELAY(100000);
97183825b71SWarner Losh 
97283825b71SWarner Losh 	for (i = 0; i < XL_TIMEOUT; i++) {
97383825b71SWarner Losh 		DELAY(10);
97483825b71SWarner Losh 		if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
97583825b71SWarner Losh 			break;
97683825b71SWarner Losh 	}
97783825b71SWarner Losh 
97883825b71SWarner Losh 	if (i == XL_TIMEOUT)
97983825b71SWarner Losh 		device_printf(sc->xl_dev, "reset didn't complete\n");
98083825b71SWarner Losh 
98183825b71SWarner Losh 	/* Reset TX and RX. */
98283825b71SWarner Losh 	/* Note: the RX reset takes an absurd amount of time
98383825b71SWarner Losh 	 * on newer versions of the Tornado chips such as those
98483825b71SWarner Losh 	 * on the 3c905CX and newer 3c908C cards. We wait an
98583825b71SWarner Losh 	 * extra amount of time so that xl_wait() doesn't complain
98683825b71SWarner Losh 	 * and annoy the users.
98783825b71SWarner Losh 	 */
98883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
98983825b71SWarner Losh 	DELAY(100000);
99083825b71SWarner Losh 	xl_wait(sc);
99183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
99283825b71SWarner Losh 	xl_wait(sc);
99383825b71SWarner Losh 
99483825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
99583825b71SWarner Losh 	    sc->xl_flags & XL_FLAG_INVERT_MII_PWR) {
99683825b71SWarner Losh 		XL_SEL_WIN(2);
99783825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS,
99883825b71SWarner Losh 		    CSR_READ_2(sc, XL_W2_RESET_OPTIONS) |
99983825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR) ?
100083825b71SWarner Losh 		    XL_RESETOPT_INVERT_LED : 0) |
100183825b71SWarner Losh 		    ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR) ?
100283825b71SWarner Losh 		    XL_RESETOPT_INVERT_MII : 0));
100383825b71SWarner Losh 	}
100483825b71SWarner Losh 
100583825b71SWarner Losh 	/* Wait a little while for the chip to get its brains in order. */
100683825b71SWarner Losh 	DELAY(100000);
100783825b71SWarner Losh }
100883825b71SWarner Losh 
100983825b71SWarner Losh /*
101083825b71SWarner Losh  * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device
101183825b71SWarner Losh  * IDs against our list and return a device name if we find a match.
101283825b71SWarner Losh  */
101383825b71SWarner Losh static int
101483825b71SWarner Losh xl_probe(device_t dev)
101583825b71SWarner Losh {
101683825b71SWarner Losh 	const struct xl_type	*t;
101783825b71SWarner Losh 
101883825b71SWarner Losh 	t = xl_devs;
101983825b71SWarner Losh 
102083825b71SWarner Losh 	while (t->xl_name != NULL) {
102183825b71SWarner Losh 		if ((pci_get_vendor(dev) == t->xl_vid) &&
102283825b71SWarner Losh 		    (pci_get_device(dev) == t->xl_did)) {
102383825b71SWarner Losh 			device_set_desc(dev, t->xl_name);
102483825b71SWarner Losh 			return (BUS_PROBE_DEFAULT);
102583825b71SWarner Losh 		}
102683825b71SWarner Losh 		t++;
102783825b71SWarner Losh 	}
102883825b71SWarner Losh 
102983825b71SWarner Losh 	return (ENXIO);
103083825b71SWarner Losh }
103183825b71SWarner Losh 
103283825b71SWarner Losh /*
103383825b71SWarner Losh  * This routine is a kludge to work around possible hardware faults
103483825b71SWarner Losh  * or manufacturing defects that can cause the media options register
103583825b71SWarner Losh  * (or reset options register, as it's called for the first generation
103683825b71SWarner Losh  * 3c90x adapters) to return an incorrect result. I have encountered
103783825b71SWarner Losh  * one Dell Latitude laptop docking station with an integrated 3c905-TX
103883825b71SWarner Losh  * which doesn't have any of the 'mediaopt' bits set. This screws up
103983825b71SWarner Losh  * the attach routine pretty badly because it doesn't know what media
104083825b71SWarner Losh  * to look for. If we find ourselves in this predicament, this routine
104183825b71SWarner Losh  * will try to guess the media options values and warn the user of a
104283825b71SWarner Losh  * possible manufacturing defect with his adapter/system/whatever.
104383825b71SWarner Losh  */
104483825b71SWarner Losh static void
104583825b71SWarner Losh xl_mediacheck(struct xl_softc *sc)
104683825b71SWarner Losh {
104783825b71SWarner Losh 
104883825b71SWarner Losh 	/*
104983825b71SWarner Losh 	 * If some of the media options bits are set, assume they are
105083825b71SWarner Losh 	 * correct. If not, try to figure it out down below.
105183825b71SWarner Losh 	 * XXX I should check for 10baseFL, but I don't have an adapter
105283825b71SWarner Losh 	 * to test with.
105383825b71SWarner Losh 	 */
105483825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
105583825b71SWarner Losh 		/*
105683825b71SWarner Losh 		 * Check the XCVR value. If it's not in the normal range
105783825b71SWarner Losh 		 * of values, we need to fake it up here.
105883825b71SWarner Losh 		 */
105983825b71SWarner Losh 		if (sc->xl_xcvr <= XL_XCVR_AUTO)
106083825b71SWarner Losh 			return;
106183825b71SWarner Losh 		else {
106283825b71SWarner Losh 			device_printf(sc->xl_dev,
106383825b71SWarner Losh 			    "bogus xcvr value in EEPROM (%x)\n", sc->xl_xcvr);
106483825b71SWarner Losh 			device_printf(sc->xl_dev,
106583825b71SWarner Losh 			    "choosing new default based on card type\n");
106683825b71SWarner Losh 		}
106783825b71SWarner Losh 	} else {
106883825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
106983825b71SWarner Losh 		    sc->xl_media & XL_MEDIAOPT_10FL)
107083825b71SWarner Losh 			return;
107183825b71SWarner Losh 		device_printf(sc->xl_dev,
107283825b71SWarner Losh "WARNING: no media options bits set in the media options register!!\n");
107383825b71SWarner Losh 		device_printf(sc->xl_dev,
107483825b71SWarner Losh "this could be a manufacturing defect in your adapter or system\n");
107583825b71SWarner Losh 		device_printf(sc->xl_dev,
107683825b71SWarner Losh "attempting to guess media type; you should probably consult your vendor\n");
107783825b71SWarner Losh 	}
107883825b71SWarner Losh 
107983825b71SWarner Losh 	xl_choose_xcvr(sc, 1);
108083825b71SWarner Losh }
108183825b71SWarner Losh 
108283825b71SWarner Losh static void
108383825b71SWarner Losh xl_choose_xcvr(struct xl_softc *sc, int verbose)
108483825b71SWarner Losh {
108583825b71SWarner Losh 	u_int16_t		devid;
108683825b71SWarner Losh 
108783825b71SWarner Losh 	/*
108883825b71SWarner Losh 	 * Read the device ID from the EEPROM.
108983825b71SWarner Losh 	 * This is what's loaded into the PCI device ID register, so it has
109083825b71SWarner Losh 	 * to be correct otherwise we wouldn't have gotten this far.
109183825b71SWarner Losh 	 */
109283825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
109383825b71SWarner Losh 
109483825b71SWarner Losh 	switch (devid) {
109583825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT:	/* 3c900-TPO */
109683825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT:		/* 3c900B-TPO */
109783825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
109883825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
109983825b71SWarner Losh 		if (verbose)
110083825b71SWarner Losh 			device_printf(sc->xl_dev,
110183825b71SWarner Losh 			    "guessing 10BaseT transceiver\n");
110283825b71SWarner Losh 		break;
110383825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10BT_COMBO:	/* 3c900-COMBO */
110483825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_COMBO:	/* 3c900B-COMBO */
110583825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
110683825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
110783825b71SWarner Losh 		if (verbose)
110883825b71SWarner Losh 			device_printf(sc->xl_dev,
110983825b71SWarner Losh 			    "guessing COMBO (AUI/BNC/TP)\n");
111083825b71SWarner Losh 		break;
111183825b71SWarner Losh 	case TC_DEVICEID_KRAKATOA_10BT_TPC:	/* 3c900B-TPC */
111283825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
111383825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_10BT;
111483825b71SWarner Losh 		if (verbose)
111583825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing TPC (BNC/TP)\n");
111683825b71SWarner Losh 		break;
111783825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10FL:		/* 3c900B-FL */
111883825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_10FL;
111983825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUI;
112083825b71SWarner Losh 		if (verbose)
112183825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10baseFL\n");
112283825b71SWarner Losh 		break;
112383825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
112483825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_555:		/* 3c555 */
112583825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556:		/* 3c556 */
112683825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_556B:	/* 3c556B */
112783825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:	/* 3c575TX */
112883825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:	/* 3c575B */
112983825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:	/* 3c575C */
113083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656:		/* 3c656 */
113183825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_656B:	/* 3c656B */
113283825b71SWarner Losh 	case TC_DEVICEID_TORNADO_656C:		/* 3c656C */
113383825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B:	/* 3c920B-EMB */
113483825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_920B_WNM:	/* 3c920B-EMB-WNM */
113583825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_MII;
113683825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
113783825b71SWarner Losh 		if (verbose)
113883825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing MII\n");
113983825b71SWarner Losh 		break;
114083825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_100BT4:	/* 3c905-T4 */
114183825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100BT4:	/* 3c905B-T4 */
114283825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT4;
114383825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_MII;
114483825b71SWarner Losh 		if (verbose)
114583825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 100baseT4/MII\n");
114683825b71SWarner Losh 		break;
114783825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT:	/* 3c905B-TX */
114883825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
114983825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT_SERV:	/* 3c980C-TX */
115083825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_SOHO100TX:	/* 3cSOHO100-TX */
115183825b71SWarner Losh 	case TC_DEVICEID_TORNADO_10_100BT:	/* 3c905C-TX */
115283825b71SWarner Losh 	case TC_DEVICEID_TORNADO_HOMECONNECT:	/* 3c450-TX */
115383825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX;
115483825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
115583825b71SWarner Losh 		if (verbose)
115683825b71SWarner Losh 			device_printf(sc->xl_dev, "guessing 10/100 internal\n");
115783825b71SWarner Losh 		break;
115883825b71SWarner Losh 	case TC_DEVICEID_CYCLONE_10_100_COMBO:	/* 3c905B-COMBO */
115983825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
116083825b71SWarner Losh 		sc->xl_xcvr = XL_XCVR_AUTO;
116183825b71SWarner Losh 		if (verbose)
116283825b71SWarner Losh 			device_printf(sc->xl_dev,
116383825b71SWarner Losh 			    "guessing 10/100 plus BNC/AUI\n");
116483825b71SWarner Losh 		break;
116583825b71SWarner Losh 	default:
116683825b71SWarner Losh 		device_printf(sc->xl_dev,
116783825b71SWarner Losh 		    "unknown device ID: %x -- defaulting to 10baseT\n", devid);
116883825b71SWarner Losh 		sc->xl_media = XL_MEDIAOPT_BT;
116983825b71SWarner Losh 		break;
117083825b71SWarner Losh 	}
117183825b71SWarner Losh }
117283825b71SWarner Losh 
117383825b71SWarner Losh /*
117483825b71SWarner Losh  * Attach the interface. Allocate softc structures, do ifmedia
117583825b71SWarner Losh  * setup and ethernet/BPF attach.
117683825b71SWarner Losh  */
117783825b71SWarner Losh static int
117883825b71SWarner Losh xl_attach(device_t dev)
117983825b71SWarner Losh {
118083825b71SWarner Losh 	u_char			eaddr[ETHER_ADDR_LEN];
11819ae11bbaSPyun YongHyeon 	u_int16_t		sinfo2, xcvr[2];
118283825b71SWarner Losh 	struct xl_softc		*sc;
118383825b71SWarner Losh 	struct ifnet		*ifp;
11849ae11bbaSPyun YongHyeon 	int			media, pmcap;
11858e5d93dbSMarius Strobl 	int			error = 0, phy, rid, res, unit;
118683825b71SWarner Losh 	uint16_t		did;
118783825b71SWarner Losh 
118883825b71SWarner Losh 	sc = device_get_softc(dev);
118983825b71SWarner Losh 	sc->xl_dev = dev;
119083825b71SWarner Losh 
119183825b71SWarner Losh 	unit = device_get_unit(dev);
119283825b71SWarner Losh 
119383825b71SWarner Losh 	mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
119483825b71SWarner Losh 	    MTX_DEF);
119583825b71SWarner Losh 	ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
119683825b71SWarner Losh 
119783825b71SWarner Losh 	did = pci_get_device(dev);
119883825b71SWarner Losh 
119983825b71SWarner Losh 	sc->xl_flags = 0;
120083825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555)
120183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
120283825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556 ||
120383825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556B)
120483825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
120583825b71SWarner Losh 		    XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
120683825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
120783825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_555 ||
120883825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_556)
120983825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_8BITROM;
121083825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_556B)
121183825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
121283825b71SWarner Losh 
121383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575B ||
121483825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575C ||
121583825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B ||
121683825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_656C)
121783825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG;
121883825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575A ||
121983825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575B ||
122083825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_575C ||
122183825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B ||
122283825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_656C)
122383825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 |
122483825b71SWarner Losh 		  XL_FLAG_8BITROM;
122583825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656)
122683825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
122783825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575B)
122883825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
122983825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_575C)
123083825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
123183825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_656C)
123283825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
123383825b71SWarner Losh 	if (did == TC_DEVICEID_HURRICANE_656 ||
123483825b71SWarner Losh 	    did == TC_DEVICEID_HURRICANE_656B)
123583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
123683825b71SWarner Losh 		    XL_FLAG_INVERT_LED_PWR;
123783825b71SWarner Losh 	if (did == TC_DEVICEID_TORNADO_10_100BT_920B ||
123883825b71SWarner Losh 	    did == TC_DEVICEID_TORNADO_10_100BT_920B_WNM)
123983825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_PHYOK;
124083825b71SWarner Losh 
124183825b71SWarner Losh 	switch (did) {
124283825b71SWarner Losh 	case TC_DEVICEID_BOOMERANG_10_100BT:	/* 3c905-TX */
124383825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575A:
124483825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575B:
124583825b71SWarner Losh 	case TC_DEVICEID_HURRICANE_575C:
124683825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_NO_MMIO;
124783825b71SWarner Losh 		break;
124883825b71SWarner Losh 	default:
124983825b71SWarner Losh 		break;
125083825b71SWarner Losh 	}
125183825b71SWarner Losh 
125283825b71SWarner Losh 	/*
125383825b71SWarner Losh 	 * Map control/status registers.
125483825b71SWarner Losh 	 */
125583825b71SWarner Losh 	pci_enable_busmaster(dev);
125683825b71SWarner Losh 
125783825b71SWarner Losh 	if ((sc->xl_flags & XL_FLAG_NO_MMIO) == 0) {
125883825b71SWarner Losh 		rid = XL_PCI_LOMEM;
125983825b71SWarner Losh 		res = SYS_RES_MEMORY;
126083825b71SWarner Losh 
126183825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
126283825b71SWarner Losh 	}
126383825b71SWarner Losh 
126483825b71SWarner Losh 	if (sc->xl_res != NULL) {
126583825b71SWarner Losh 		sc->xl_flags |= XL_FLAG_USE_MMIO;
126683825b71SWarner Losh 		if (bootverbose)
126783825b71SWarner Losh 			device_printf(dev, "using memory mapped I/O\n");
126883825b71SWarner Losh 	} else {
126983825b71SWarner Losh 		rid = XL_PCI_LOIO;
127083825b71SWarner Losh 		res = SYS_RES_IOPORT;
127183825b71SWarner Losh 		sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
127283825b71SWarner Losh 		if (sc->xl_res == NULL) {
127383825b71SWarner Losh 			device_printf(dev, "couldn't map ports/memory\n");
127483825b71SWarner Losh 			error = ENXIO;
127583825b71SWarner Losh 			goto fail;
127683825b71SWarner Losh 		}
127783825b71SWarner Losh 		if (bootverbose)
127883825b71SWarner Losh 			device_printf(dev, "using port I/O\n");
127983825b71SWarner Losh 	}
128083825b71SWarner Losh 
128183825b71SWarner Losh 	sc->xl_btag = rman_get_bustag(sc->xl_res);
128283825b71SWarner Losh 	sc->xl_bhandle = rman_get_bushandle(sc->xl_res);
128383825b71SWarner Losh 
128483825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG) {
128583825b71SWarner Losh 		rid = XL_PCI_FUNCMEM;
128683825b71SWarner Losh 		sc->xl_fres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
128783825b71SWarner Losh 		    RF_ACTIVE);
128883825b71SWarner Losh 
128983825b71SWarner Losh 		if (sc->xl_fres == NULL) {
129083825b71SWarner Losh 			device_printf(dev, "couldn't map funcreg memory\n");
129183825b71SWarner Losh 			error = ENXIO;
129283825b71SWarner Losh 			goto fail;
129383825b71SWarner Losh 		}
129483825b71SWarner Losh 
129583825b71SWarner Losh 		sc->xl_ftag = rman_get_bustag(sc->xl_fres);
129683825b71SWarner Losh 		sc->xl_fhandle = rman_get_bushandle(sc->xl_fres);
129783825b71SWarner Losh 	}
129883825b71SWarner Losh 
129983825b71SWarner Losh 	/* Allocate interrupt */
130083825b71SWarner Losh 	rid = 0;
130183825b71SWarner Losh 	sc->xl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
130283825b71SWarner Losh 	    RF_SHAREABLE | RF_ACTIVE);
130383825b71SWarner Losh 	if (sc->xl_irq == NULL) {
130483825b71SWarner Losh 		device_printf(dev, "couldn't map interrupt\n");
130583825b71SWarner Losh 		error = ENXIO;
130683825b71SWarner Losh 		goto fail;
130783825b71SWarner Losh 	}
130883825b71SWarner Losh 
130983825b71SWarner Losh 	/* Initialize interface name. */
131083825b71SWarner Losh 	ifp = sc->xl_ifp = if_alloc(IFT_ETHER);
131183825b71SWarner Losh 	if (ifp == NULL) {
131283825b71SWarner Losh 		device_printf(dev, "can not if_alloc()\n");
131383825b71SWarner Losh 		error = ENOSPC;
131483825b71SWarner Losh 		goto fail;
131583825b71SWarner Losh 	}
131683825b71SWarner Losh 	ifp->if_softc = sc;
131783825b71SWarner Losh 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
131883825b71SWarner Losh 
131983825b71SWarner Losh 	/* Reset the adapter. */
132083825b71SWarner Losh 	XL_LOCK(sc);
132183825b71SWarner Losh 	xl_reset(sc);
132283825b71SWarner Losh 	XL_UNLOCK(sc);
132383825b71SWarner Losh 
132483825b71SWarner Losh 	/*
132583825b71SWarner Losh 	 * Get station address from the EEPROM.
132683825b71SWarner Losh 	 */
132783825b71SWarner Losh 	if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) {
132883825b71SWarner Losh 		device_printf(dev, "failed to read station address\n");
132983825b71SWarner Losh 		error = ENXIO;
133083825b71SWarner Losh 		goto fail;
133183825b71SWarner Losh 	}
133283825b71SWarner Losh 
133348dcbc33SPyun YongHyeon 	callout_init_mtx(&sc->xl_tick_callout, &sc->xl_mtx, 0);
133483825b71SWarner Losh 	TASK_INIT(&sc->xl_task, 0, xl_rxeof_task, sc);
133583825b71SWarner Losh 
133683825b71SWarner Losh 	/*
133783825b71SWarner Losh 	 * Now allocate a tag for the DMA descriptor lists and a chunk
133883825b71SWarner Losh 	 * of DMA-able memory based on the tag.  Also obtain the DMA
133983825b71SWarner Losh 	 * addresses of the RX and TX ring, which we'll need later.
134083825b71SWarner Losh 	 * All of our lists are allocated as a contiguous block
134183825b71SWarner Losh 	 * of memory.
134283825b71SWarner Losh 	 */
134383825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
134483825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
134583825b71SWarner Losh 	    XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, NULL, NULL,
134683825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_tag);
134783825b71SWarner Losh 	if (error) {
134883825b71SWarner Losh 		device_printf(dev, "failed to allocate rx dma tag\n");
134983825b71SWarner Losh 		goto fail;
135083825b71SWarner Losh 	}
135183825b71SWarner Losh 
135283825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag,
1353006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT |
1354006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_rx_dmamap);
135583825b71SWarner Losh 	if (error) {
135683825b71SWarner Losh 		device_printf(dev, "no memory for rx list buffers!\n");
135783825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
135883825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
135983825b71SWarner Losh 		goto fail;
136083825b71SWarner Losh 	}
136183825b71SWarner Losh 
136283825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag,
136383825b71SWarner Losh 	    sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list,
136483825b71SWarner Losh 	    XL_RX_LIST_SZ, xl_dma_map_addr,
136583825b71SWarner Losh 	    &sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT);
136683825b71SWarner Losh 	if (error) {
136783825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the rx ring!\n");
136883825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
136983825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
137083825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
137183825b71SWarner Losh 		sc->xl_ldata.xl_rx_tag = NULL;
137283825b71SWarner Losh 		goto fail;
137383825b71SWarner Losh 	}
137483825b71SWarner Losh 
137583825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
137683825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
137783825b71SWarner Losh 	    XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, NULL, NULL,
137883825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_tag);
137983825b71SWarner Losh 	if (error) {
138083825b71SWarner Losh 		device_printf(dev, "failed to allocate tx dma tag\n");
138183825b71SWarner Losh 		goto fail;
138283825b71SWarner Losh 	}
138383825b71SWarner Losh 
138483825b71SWarner Losh 	error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag,
1385006aaeeaSMarius Strobl 	    (void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT |
1386006aaeeaSMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->xl_ldata.xl_tx_dmamap);
138783825b71SWarner Losh 	if (error) {
138883825b71SWarner Losh 		device_printf(dev, "no memory for list buffers!\n");
138983825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
139083825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
139183825b71SWarner Losh 		goto fail;
139283825b71SWarner Losh 	}
139383825b71SWarner Losh 
139483825b71SWarner Losh 	error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag,
139583825b71SWarner Losh 	    sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list,
139683825b71SWarner Losh 	    XL_TX_LIST_SZ, xl_dma_map_addr,
139783825b71SWarner Losh 	    &sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT);
139883825b71SWarner Losh 	if (error) {
139983825b71SWarner Losh 		device_printf(dev, "cannot get dma address of the tx ring!\n");
140083825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
140183825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
140283825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
140383825b71SWarner Losh 		sc->xl_ldata.xl_tx_tag = NULL;
140483825b71SWarner Losh 		goto fail;
140583825b71SWarner Losh 	}
140683825b71SWarner Losh 
140783825b71SWarner Losh 	/*
140883825b71SWarner Losh 	 * Allocate a DMA tag for the mapping of mbufs.
140983825b71SWarner Losh 	 */
141083825b71SWarner Losh 	error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
141183825b71SWarner Losh 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
141283825b71SWarner Losh 	    MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0, NULL,
141383825b71SWarner Losh 	    NULL, &sc->xl_mtag);
141483825b71SWarner Losh 	if (error) {
141583825b71SWarner Losh 		device_printf(dev, "failed to allocate mbuf dma tag\n");
141683825b71SWarner Losh 		goto fail;
141783825b71SWarner Losh 	}
141883825b71SWarner Losh 
141983825b71SWarner Losh 	/* We need a spare DMA map for the RX ring. */
142083825b71SWarner Losh 	error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap);
142183825b71SWarner Losh 	if (error)
142283825b71SWarner Losh 		goto fail;
142383825b71SWarner Losh 
142483825b71SWarner Losh 	/*
142583825b71SWarner Losh 	 * Figure out the card type. 3c905B adapters have the
142683825b71SWarner Losh 	 * 'supportsNoTxLength' bit set in the capabilities
142783825b71SWarner Losh 	 * word in the EEPROM.
1428f33a1c16SWarner Losh 	 * Note: my 3c575C CardBus card lies. It returns a value
142983825b71SWarner Losh 	 * of 0x1578 for its capabilities word, which is somewhat
143083825b71SWarner Losh 	 * nonsensical. Another way to distinguish a 3c90x chip
143183825b71SWarner Losh 	 * from a 3c90xB/C chip is to check for the 'supportsLargePackets'
143283825b71SWarner Losh 	 * bit. This will only be set for 3c90x boomerage chips.
143383825b71SWarner Losh 	 */
143483825b71SWarner Losh 	xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
143583825b71SWarner Losh 	if (sc->xl_caps & XL_CAPS_NO_TXLENGTH ||
143683825b71SWarner Losh 	    !(sc->xl_caps & XL_CAPS_LARGE_PKTS))
143783825b71SWarner Losh 		sc->xl_type = XL_TYPE_905B;
143883825b71SWarner Losh 	else
143983825b71SWarner Losh 		sc->xl_type = XL_TYPE_90X;
144083825b71SWarner Losh 
14419ae11bbaSPyun YongHyeon 	/* Check availability of WOL. */
14429ae11bbaSPyun YongHyeon 	if ((sc->xl_caps & XL_CAPS_PWRMGMT) != 0 &&
14433b0a4aefSJohn Baldwin 	    pci_find_cap(dev, PCIY_PMG, &pmcap) == 0) {
14449ae11bbaSPyun YongHyeon 		sc->xl_pmcap = pmcap;
14459ae11bbaSPyun YongHyeon 		sc->xl_flags |= XL_FLAG_WOL;
14469ae11bbaSPyun YongHyeon 		sinfo2 = 0;
14479ae11bbaSPyun YongHyeon 		xl_read_eeprom(sc, (caddr_t)&sinfo2, XL_EE_SOFTINFO2, 1, 0);
14489ae11bbaSPyun YongHyeon 		if ((sinfo2 & XL_SINFO2_AUX_WOL_CON) == 0 && bootverbose)
14499ae11bbaSPyun YongHyeon 			device_printf(dev,
14509ae11bbaSPyun YongHyeon 			    "No auxiliary remote wakeup connector!\n");
14519ae11bbaSPyun YongHyeon 	}
14529ae11bbaSPyun YongHyeon 
145383825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
145483825b71SWarner Losh 	sc->xl_tx_thresh = XL_MIN_FRAMELEN;
145583825b71SWarner Losh 
145683825b71SWarner Losh 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
145783825b71SWarner Losh 	ifp->if_ioctl = xl_ioctl;
145883825b71SWarner Losh 	ifp->if_capabilities = IFCAP_VLAN_MTU;
145983825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
146083825b71SWarner Losh 		ifp->if_hwassist = XL905B_CSUM_FEATURES;
146183825b71SWarner Losh #ifdef XL905B_TXCSUM_BROKEN
146283825b71SWarner Losh 		ifp->if_capabilities |= IFCAP_RXCSUM;
146383825b71SWarner Losh #else
146483825b71SWarner Losh 		ifp->if_capabilities |= IFCAP_HWCSUM;
146583825b71SWarner Losh #endif
146683825b71SWarner Losh 	}
14679ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0)
14689ae11bbaSPyun YongHyeon 		ifp->if_capabilities |= IFCAP_WOL_MAGIC;
146983825b71SWarner Losh 	ifp->if_capenable = ifp->if_capabilities;
147083825b71SWarner Losh #ifdef DEVICE_POLLING
147183825b71SWarner Losh 	ifp->if_capabilities |= IFCAP_POLLING;
147283825b71SWarner Losh #endif
147383825b71SWarner Losh 	ifp->if_start = xl_start;
147483825b71SWarner Losh 	ifp->if_init = xl_init;
147583825b71SWarner Losh 	IFQ_SET_MAXLEN(&ifp->if_snd, XL_TX_LIST_CNT - 1);
147683825b71SWarner Losh 	ifp->if_snd.ifq_drv_maxlen = XL_TX_LIST_CNT - 1;
147783825b71SWarner Losh 	IFQ_SET_READY(&ifp->if_snd);
147883825b71SWarner Losh 
147983825b71SWarner Losh 	/*
148083825b71SWarner Losh 	 * Now we have to see what sort of media we have.
148183825b71SWarner Losh 	 * This includes probing for an MII interace and a
148283825b71SWarner Losh 	 * possible PHY.
148383825b71SWarner Losh 	 */
148483825b71SWarner Losh 	XL_SEL_WIN(3);
148583825b71SWarner Losh 	sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
148683825b71SWarner Losh 	if (bootverbose)
148783825b71SWarner Losh 		device_printf(dev, "media options word: %x\n", sc->xl_media);
148883825b71SWarner Losh 
148983825b71SWarner Losh 	xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
149083825b71SWarner Losh 	sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
149183825b71SWarner Losh 	sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
149283825b71SWarner Losh 	sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
149383825b71SWarner Losh 
149483825b71SWarner Losh 	xl_mediacheck(sc);
149583825b71SWarner Losh 
149683825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
149783825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
149883825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
149983825b71SWarner Losh 		if (bootverbose)
150083825b71SWarner Losh 			device_printf(dev, "found MII/AUTO\n");
150183825b71SWarner Losh 		xl_setcfg(sc);
15028e5d93dbSMarius Strobl 		/*
15038e5d93dbSMarius Strobl 		 * Attach PHYs only at MII address 24 if !XL_FLAG_PHYOK.
15048e5d93dbSMarius Strobl 		 * This is to guard against problems with certain 3Com ASIC
15058e5d93dbSMarius Strobl 		 * revisions that incorrectly map the internal transceiver
15068e5d93dbSMarius Strobl 		 * control registers at all MII addresses.
15078e5d93dbSMarius Strobl 		 */
15088e5d93dbSMarius Strobl 		phy = MII_PHY_ANY;
15098edfedadSMarius Strobl 		if ((sc->xl_flags & XL_FLAG_PHYOK) == 0)
15108e5d93dbSMarius Strobl 			phy = 24;
15118e5d93dbSMarius Strobl 		error = mii_attach(dev, &sc->xl_miibus, ifp, xl_ifmedia_upd,
1512aee0e786SPyun YongHyeon 		    xl_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY,
1513aee0e786SPyun YongHyeon 		    sc->xl_type == XL_TYPE_905B ? MIIF_DOPAUSE : 0);
15148e5d93dbSMarius Strobl 		if (error != 0) {
15158e5d93dbSMarius Strobl 			device_printf(dev, "attaching PHYs failed\n");
151683825b71SWarner Losh 			goto fail;
151783825b71SWarner Losh 		}
151883825b71SWarner Losh 		goto done;
151983825b71SWarner Losh 	}
152083825b71SWarner Losh 
152183825b71SWarner Losh 	/*
152283825b71SWarner Losh 	 * Sanity check. If the user has selected "auto" and this isn't
152383825b71SWarner Losh 	 * a 10/100 card of some kind, we need to force the transceiver
152483825b71SWarner Losh 	 * type to something sane.
152583825b71SWarner Losh 	 */
152683825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_AUTO)
152783825b71SWarner Losh 		xl_choose_xcvr(sc, bootverbose);
152883825b71SWarner Losh 
152983825b71SWarner Losh 	/*
153083825b71SWarner Losh 	 * Do ifmedia setup.
153183825b71SWarner Losh 	 */
153283825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BT) {
153383825b71SWarner Losh 		if (bootverbose)
153483825b71SWarner Losh 			device_printf(dev, "found 10baseT\n");
153583825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
153683825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
153783825b71SWarner Losh 		if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
153883825b71SWarner Losh 			ifmedia_add(&sc->ifmedia,
153983825b71SWarner Losh 			    IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
154083825b71SWarner Losh 	}
154183825b71SWarner Losh 
154283825b71SWarner Losh 	if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
154383825b71SWarner Losh 		/*
154483825b71SWarner Losh 		 * Check for a 10baseFL board in disguise.
154583825b71SWarner Losh 		 */
154683825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
154783825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
154883825b71SWarner Losh 			if (bootverbose)
154983825b71SWarner Losh 				device_printf(dev, "found 10baseFL\n");
155083825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL);
155183825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX,
155283825b71SWarner Losh 			    0, NULL);
155383825b71SWarner Losh 			if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
155483825b71SWarner Losh 				ifmedia_add(&sc->ifmedia,
155583825b71SWarner Losh 				    IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL);
155683825b71SWarner Losh 		} else {
155783825b71SWarner Losh 			if (bootverbose)
155883825b71SWarner Losh 				device_printf(dev, "found AUI\n");
155983825b71SWarner Losh 			ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
156083825b71SWarner Losh 		}
156183825b71SWarner Losh 	}
156283825b71SWarner Losh 
156383825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BNC) {
156483825b71SWarner Losh 		if (bootverbose)
156583825b71SWarner Losh 			device_printf(dev, "found BNC\n");
156683825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
156783825b71SWarner Losh 	}
156883825b71SWarner Losh 
156983825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_BFX) {
157083825b71SWarner Losh 		if (bootverbose)
157183825b71SWarner Losh 			device_printf(dev, "found 100baseFX\n");
157283825b71SWarner Losh 		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL);
157383825b71SWarner Losh 	}
157483825b71SWarner Losh 
157583825b71SWarner Losh 	media = IFM_ETHER|IFM_100_TX|IFM_FDX;
157683825b71SWarner Losh 	xl_choose_media(sc, &media);
157783825b71SWarner Losh 
157883825b71SWarner Losh 	if (sc->xl_miibus == NULL)
157983825b71SWarner Losh 		ifmedia_set(&sc->ifmedia, media);
158083825b71SWarner Losh 
158183825b71SWarner Losh done:
158283825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
158383825b71SWarner Losh 		XL_SEL_WIN(0);
158483825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
158583825b71SWarner Losh 	}
158683825b71SWarner Losh 
158783825b71SWarner Losh 	/*
158883825b71SWarner Losh 	 * Call MI attach routine.
158983825b71SWarner Losh 	 */
159083825b71SWarner Losh 	ether_ifattach(ifp, eaddr);
159183825b71SWarner Losh 
159283825b71SWarner Losh 	error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET | INTR_MPSAFE,
159383825b71SWarner Losh 	    NULL, xl_intr, sc, &sc->xl_intrhand);
159483825b71SWarner Losh 	if (error) {
159583825b71SWarner Losh 		device_printf(dev, "couldn't set up irq\n");
159683825b71SWarner Losh 		ether_ifdetach(ifp);
159783825b71SWarner Losh 		goto fail;
159883825b71SWarner Losh 	}
159983825b71SWarner Losh 
160083825b71SWarner Losh fail:
160183825b71SWarner Losh 	if (error)
160283825b71SWarner Losh 		xl_detach(dev);
160383825b71SWarner Losh 
160483825b71SWarner Losh 	return (error);
160583825b71SWarner Losh }
160683825b71SWarner Losh 
160783825b71SWarner Losh /*
160883825b71SWarner Losh  * Choose a default media.
160983825b71SWarner Losh  * XXX This is a leaf function only called by xl_attach() and
161083825b71SWarner Losh  *     acquires/releases the non-recursible driver mutex to
161183825b71SWarner Losh  *     satisfy lock assertions.
161283825b71SWarner Losh  */
161383825b71SWarner Losh static void
161483825b71SWarner Losh xl_choose_media(struct xl_softc *sc, int *media)
161583825b71SWarner Losh {
161683825b71SWarner Losh 
161783825b71SWarner Losh 	XL_LOCK(sc);
161883825b71SWarner Losh 
161983825b71SWarner Losh 	switch (sc->xl_xcvr) {
162083825b71SWarner Losh 	case XL_XCVR_10BT:
162183825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
162283825b71SWarner Losh 		xl_setmode(sc, *media);
162383825b71SWarner Losh 		break;
162483825b71SWarner Losh 	case XL_XCVR_AUI:
162583825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
162683825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
162783825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_FL;
162883825b71SWarner Losh 			xl_setmode(sc, *media);
162983825b71SWarner Losh 		} else {
163083825b71SWarner Losh 			*media = IFM_ETHER|IFM_10_5;
163183825b71SWarner Losh 			xl_setmode(sc, *media);
163283825b71SWarner Losh 		}
163383825b71SWarner Losh 		break;
163483825b71SWarner Losh 	case XL_XCVR_COAX:
163583825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_2;
163683825b71SWarner Losh 		xl_setmode(sc, *media);
163783825b71SWarner Losh 		break;
163883825b71SWarner Losh 	case XL_XCVR_AUTO:
163983825b71SWarner Losh 	case XL_XCVR_100BTX:
164083825b71SWarner Losh 	case XL_XCVR_MII:
164183825b71SWarner Losh 		/* Chosen by miibus */
164283825b71SWarner Losh 		break;
164383825b71SWarner Losh 	case XL_XCVR_100BFX:
164483825b71SWarner Losh 		*media = IFM_ETHER|IFM_100_FX;
164583825b71SWarner Losh 		break;
164683825b71SWarner Losh 	default:
164783825b71SWarner Losh 		device_printf(sc->xl_dev, "unknown XCVR type: %d\n",
164883825b71SWarner Losh 		    sc->xl_xcvr);
164983825b71SWarner Losh 		/*
165083825b71SWarner Losh 		 * This will probably be wrong, but it prevents
165183825b71SWarner Losh 		 * the ifmedia code from panicking.
165283825b71SWarner Losh 		 */
165383825b71SWarner Losh 		*media = IFM_ETHER|IFM_10_T;
165483825b71SWarner Losh 		break;
165583825b71SWarner Losh 	}
165683825b71SWarner Losh 
165783825b71SWarner Losh 	XL_UNLOCK(sc);
165883825b71SWarner Losh }
165983825b71SWarner Losh 
166083825b71SWarner Losh /*
166183825b71SWarner Losh  * Shutdown hardware and free up resources. This can be called any
166283825b71SWarner Losh  * time after the mutex has been initialized. It is called in both
166383825b71SWarner Losh  * the error case in attach and the normal detach case so it needs
166483825b71SWarner Losh  * to be careful about only freeing resources that have actually been
166583825b71SWarner Losh  * allocated.
166683825b71SWarner Losh  */
166783825b71SWarner Losh static int
166883825b71SWarner Losh xl_detach(device_t dev)
166983825b71SWarner Losh {
167083825b71SWarner Losh 	struct xl_softc		*sc;
167183825b71SWarner Losh 	struct ifnet		*ifp;
167283825b71SWarner Losh 	int			rid, res;
167383825b71SWarner Losh 
167483825b71SWarner Losh 	sc = device_get_softc(dev);
167583825b71SWarner Losh 	ifp = sc->xl_ifp;
167683825b71SWarner Losh 
167783825b71SWarner Losh 	KASSERT(mtx_initialized(&sc->xl_mtx), ("xl mutex not initialized"));
167883825b71SWarner Losh 
167983825b71SWarner Losh #ifdef DEVICE_POLLING
168083825b71SWarner Losh 	if (ifp && ifp->if_capenable & IFCAP_POLLING)
168183825b71SWarner Losh 		ether_poll_deregister(ifp);
168283825b71SWarner Losh #endif
168383825b71SWarner Losh 
168483825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_USE_MMIO) {
168583825b71SWarner Losh 		rid = XL_PCI_LOMEM;
168683825b71SWarner Losh 		res = SYS_RES_MEMORY;
168783825b71SWarner Losh 	} else {
168883825b71SWarner Losh 		rid = XL_PCI_LOIO;
168983825b71SWarner Losh 		res = SYS_RES_IOPORT;
169083825b71SWarner Losh 	}
169183825b71SWarner Losh 
169283825b71SWarner Losh 	/* These should only be active if attach succeeded */
169383825b71SWarner Losh 	if (device_is_attached(dev)) {
169483825b71SWarner Losh 		XL_LOCK(sc);
169583825b71SWarner Losh 		xl_stop(sc);
169683825b71SWarner Losh 		XL_UNLOCK(sc);
169783825b71SWarner Losh 		taskqueue_drain(taskqueue_swi, &sc->xl_task);
169848dcbc33SPyun YongHyeon 		callout_drain(&sc->xl_tick_callout);
169983825b71SWarner Losh 		ether_ifdetach(ifp);
170083825b71SWarner Losh 	}
170183825b71SWarner Losh 	if (sc->xl_miibus)
170283825b71SWarner Losh 		device_delete_child(dev, sc->xl_miibus);
170383825b71SWarner Losh 	bus_generic_detach(dev);
170483825b71SWarner Losh 	ifmedia_removeall(&sc->ifmedia);
170583825b71SWarner Losh 
170683825b71SWarner Losh 	if (sc->xl_intrhand)
170783825b71SWarner Losh 		bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
170883825b71SWarner Losh 	if (sc->xl_irq)
170983825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
171083825b71SWarner Losh 	if (sc->xl_fres != NULL)
171183825b71SWarner Losh 		bus_release_resource(dev, SYS_RES_MEMORY,
171283825b71SWarner Losh 		    XL_PCI_FUNCMEM, sc->xl_fres);
171383825b71SWarner Losh 	if (sc->xl_res)
171483825b71SWarner Losh 		bus_release_resource(dev, res, rid, sc->xl_res);
171583825b71SWarner Losh 
171683825b71SWarner Losh 	if (ifp)
171783825b71SWarner Losh 		if_free(ifp);
171883825b71SWarner Losh 
171983825b71SWarner Losh 	if (sc->xl_mtag) {
172083825b71SWarner Losh 		bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap);
172183825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_mtag);
172283825b71SWarner Losh 	}
172383825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_tag) {
172483825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_rx_tag,
172583825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
172683825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
172783825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap);
172883825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
172983825b71SWarner Losh 	}
173083825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_tag) {
173183825b71SWarner Losh 		bus_dmamap_unload(sc->xl_ldata.xl_tx_tag,
173283825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
173383825b71SWarner Losh 		bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
173483825b71SWarner Losh 		    sc->xl_ldata.xl_tx_dmamap);
173583825b71SWarner Losh 		bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
173683825b71SWarner Losh 	}
173783825b71SWarner Losh 
173883825b71SWarner Losh 	mtx_destroy(&sc->xl_mtx);
173983825b71SWarner Losh 
174083825b71SWarner Losh 	return (0);
174183825b71SWarner Losh }
174283825b71SWarner Losh 
174383825b71SWarner Losh /*
174483825b71SWarner Losh  * Initialize the transmit descriptors.
174583825b71SWarner Losh  */
174683825b71SWarner Losh static int
174783825b71SWarner Losh xl_list_tx_init(struct xl_softc *sc)
174883825b71SWarner Losh {
174983825b71SWarner Losh 	struct xl_chain_data	*cd;
175083825b71SWarner Losh 	struct xl_list_data	*ld;
175183825b71SWarner Losh 	int			error, i;
175283825b71SWarner Losh 
175383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
175483825b71SWarner Losh 
175583825b71SWarner Losh 	cd = &sc->xl_cdata;
175683825b71SWarner Losh 	ld = &sc->xl_ldata;
175783825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
175883825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
175983825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
176083825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
176183825b71SWarner Losh 		if (error)
176283825b71SWarner Losh 			return (error);
176383825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
176483825b71SWarner Losh 		    i * sizeof(struct xl_list);
176583825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
176683825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = NULL;
176783825b71SWarner Losh 		else
176883825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
176983825b71SWarner Losh 	}
177083825b71SWarner Losh 
177183825b71SWarner Losh 	cd->xl_tx_free = &cd->xl_tx_chain[0];
177283825b71SWarner Losh 	cd->xl_tx_tail = cd->xl_tx_head = NULL;
177383825b71SWarner Losh 
177483825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
177583825b71SWarner Losh 	return (0);
177683825b71SWarner Losh }
177783825b71SWarner Losh 
177883825b71SWarner Losh /*
177983825b71SWarner Losh  * Initialize the transmit descriptors.
178083825b71SWarner Losh  */
178183825b71SWarner Losh static int
178283825b71SWarner Losh xl_list_tx_init_90xB(struct xl_softc *sc)
178383825b71SWarner Losh {
178483825b71SWarner Losh 	struct xl_chain_data	*cd;
178583825b71SWarner Losh 	struct xl_list_data	*ld;
178683825b71SWarner Losh 	int			error, i;
178783825b71SWarner Losh 
178883825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
178983825b71SWarner Losh 
179083825b71SWarner Losh 	cd = &sc->xl_cdata;
179183825b71SWarner Losh 	ld = &sc->xl_ldata;
179283825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
179383825b71SWarner Losh 		cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
179483825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
179583825b71SWarner Losh 		    &cd->xl_tx_chain[i].xl_map);
179683825b71SWarner Losh 		if (error)
179783825b71SWarner Losh 			return (error);
179883825b71SWarner Losh 		cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
179983825b71SWarner Losh 		    i * sizeof(struct xl_list);
180083825b71SWarner Losh 		if (i == (XL_TX_LIST_CNT - 1))
180183825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0];
180283825b71SWarner Losh 		else
180383825b71SWarner Losh 			cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
180483825b71SWarner Losh 		if (i == 0)
180583825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
180683825b71SWarner Losh 			    &cd->xl_tx_chain[XL_TX_LIST_CNT - 1];
180783825b71SWarner Losh 		else
180883825b71SWarner Losh 			cd->xl_tx_chain[i].xl_prev =
180983825b71SWarner Losh 			    &cd->xl_tx_chain[i - 1];
181083825b71SWarner Losh 	}
181183825b71SWarner Losh 
181283825b71SWarner Losh 	bzero(ld->xl_tx_list, XL_TX_LIST_SZ);
181383825b71SWarner Losh 	ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY);
181483825b71SWarner Losh 
181583825b71SWarner Losh 	cd->xl_tx_prod = 1;
181683825b71SWarner Losh 	cd->xl_tx_cons = 1;
181783825b71SWarner Losh 	cd->xl_tx_cnt = 0;
181883825b71SWarner Losh 
181983825b71SWarner Losh 	bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
182083825b71SWarner Losh 	return (0);
182183825b71SWarner Losh }
182283825b71SWarner Losh 
182383825b71SWarner Losh /*
182483825b71SWarner Losh  * Initialize the RX descriptors and allocate mbufs for them. Note that
182583825b71SWarner Losh  * we arrange the descriptors in a closed ring, so that the last descriptor
182683825b71SWarner Losh  * points back to the first.
182783825b71SWarner Losh  */
182883825b71SWarner Losh static int
182983825b71SWarner Losh xl_list_rx_init(struct xl_softc *sc)
183083825b71SWarner Losh {
183183825b71SWarner Losh 	struct xl_chain_data	*cd;
183283825b71SWarner Losh 	struct xl_list_data	*ld;
183383825b71SWarner Losh 	int			error, i, next;
183483825b71SWarner Losh 	u_int32_t		nextptr;
183583825b71SWarner Losh 
183683825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
183783825b71SWarner Losh 
183883825b71SWarner Losh 	cd = &sc->xl_cdata;
183983825b71SWarner Losh 	ld = &sc->xl_ldata;
184083825b71SWarner Losh 
184183825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
184283825b71SWarner Losh 		cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i];
184383825b71SWarner Losh 		error = bus_dmamap_create(sc->xl_mtag, 0,
184483825b71SWarner Losh 		    &cd->xl_rx_chain[i].xl_map);
184583825b71SWarner Losh 		if (error)
184683825b71SWarner Losh 			return (error);
184783825b71SWarner Losh 		error = xl_newbuf(sc, &cd->xl_rx_chain[i]);
184883825b71SWarner Losh 		if (error)
184983825b71SWarner Losh 			return (error);
185083825b71SWarner Losh 		if (i == (XL_RX_LIST_CNT - 1))
185183825b71SWarner Losh 			next = 0;
185283825b71SWarner Losh 		else
185383825b71SWarner Losh 			next = i + 1;
185483825b71SWarner Losh 		nextptr = ld->xl_rx_dmaaddr +
185583825b71SWarner Losh 		    next * sizeof(struct xl_list_onefrag);
185683825b71SWarner Losh 		cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next];
185783825b71SWarner Losh 		ld->xl_rx_list[i].xl_next = htole32(nextptr);
185883825b71SWarner Losh 	}
185983825b71SWarner Losh 
186083825b71SWarner Losh 	bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
186183825b71SWarner Losh 	cd->xl_rx_head = &cd->xl_rx_chain[0];
186283825b71SWarner Losh 
186383825b71SWarner Losh 	return (0);
186483825b71SWarner Losh }
186583825b71SWarner Losh 
186683825b71SWarner Losh /*
186783825b71SWarner Losh  * Initialize an RX descriptor and attach an MBUF cluster.
186883825b71SWarner Losh  * If we fail to do so, we need to leave the old mbuf and
186983825b71SWarner Losh  * the old DMA map untouched so that it can be reused.
187083825b71SWarner Losh  */
187183825b71SWarner Losh static int
187283825b71SWarner Losh xl_newbuf(struct xl_softc *sc, struct xl_chain_onefrag *c)
187383825b71SWarner Losh {
187483825b71SWarner Losh 	struct mbuf		*m_new = NULL;
187583825b71SWarner Losh 	bus_dmamap_t		map;
187683825b71SWarner Losh 	bus_dma_segment_t	segs[1];
187783825b71SWarner Losh 	int			error, nseg;
187883825b71SWarner Losh 
187983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
188083825b71SWarner Losh 
188183825b71SWarner Losh 	m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
188283825b71SWarner Losh 	if (m_new == NULL)
188383825b71SWarner Losh 		return (ENOBUFS);
188483825b71SWarner Losh 
188583825b71SWarner Losh 	m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
188683825b71SWarner Losh 
188783825b71SWarner Losh 	/* Force longword alignment for packet payload. */
188883825b71SWarner Losh 	m_adj(m_new, ETHER_ALIGN);
188983825b71SWarner Losh 
189083825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, sc->xl_tmpmap, m_new,
189183825b71SWarner Losh 	    segs, &nseg, BUS_DMA_NOWAIT);
189283825b71SWarner Losh 	if (error) {
189383825b71SWarner Losh 		m_freem(m_new);
189483825b71SWarner Losh 		device_printf(sc->xl_dev, "can't map mbuf (error %d)\n",
189583825b71SWarner Losh 		    error);
189683825b71SWarner Losh 		return (error);
189783825b71SWarner Losh 	}
189883825b71SWarner Losh 	KASSERT(nseg == 1,
189983825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
190083825b71SWarner Losh 
190183825b71SWarner Losh 	bus_dmamap_unload(sc->xl_mtag, c->xl_map);
190283825b71SWarner Losh 	map = c->xl_map;
190383825b71SWarner Losh 	c->xl_map = sc->xl_tmpmap;
190483825b71SWarner Losh 	sc->xl_tmpmap = map;
190583825b71SWarner Losh 	c->xl_mbuf = m_new;
190683825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG);
190783825b71SWarner Losh 	c->xl_ptr->xl_frag.xl_addr = htole32(segs->ds_addr);
1908f321edf9SPyun YongHyeon 	c->xl_ptr->xl_status = 0;
190983825b71SWarner Losh 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD);
191083825b71SWarner Losh 	return (0);
191183825b71SWarner Losh }
191283825b71SWarner Losh 
191383825b71SWarner Losh static int
191483825b71SWarner Losh xl_rx_resync(struct xl_softc *sc)
191583825b71SWarner Losh {
191683825b71SWarner Losh 	struct xl_chain_onefrag	*pos;
191783825b71SWarner Losh 	int			i;
191883825b71SWarner Losh 
191983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
192083825b71SWarner Losh 
192183825b71SWarner Losh 	pos = sc->xl_cdata.xl_rx_head;
192283825b71SWarner Losh 
192383825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
192483825b71SWarner Losh 		if (pos->xl_ptr->xl_status)
192583825b71SWarner Losh 			break;
192683825b71SWarner Losh 		pos = pos->xl_next;
192783825b71SWarner Losh 	}
192883825b71SWarner Losh 
192983825b71SWarner Losh 	if (i == XL_RX_LIST_CNT)
193083825b71SWarner Losh 		return (0);
193183825b71SWarner Losh 
193283825b71SWarner Losh 	sc->xl_cdata.xl_rx_head = pos;
193383825b71SWarner Losh 
193483825b71SWarner Losh 	return (EAGAIN);
193583825b71SWarner Losh }
193683825b71SWarner Losh 
193783825b71SWarner Losh /*
193883825b71SWarner Losh  * A frame has been uploaded: pass the resulting mbuf chain up to
193983825b71SWarner Losh  * the higher level protocols.
194083825b71SWarner Losh  */
19411abcdbd1SAttilio Rao static int
194283825b71SWarner Losh xl_rxeof(struct xl_softc *sc)
194383825b71SWarner Losh {
194483825b71SWarner Losh 	struct mbuf		*m;
194583825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
194683825b71SWarner Losh 	struct xl_chain_onefrag	*cur_rx;
19475a13764bSPyun YongHyeon 	int			total_len;
19481abcdbd1SAttilio Rao 	int			rx_npkts = 0;
194983825b71SWarner Losh 	u_int32_t		rxstat;
195083825b71SWarner Losh 
195183825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
195283825b71SWarner Losh again:
195383825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap,
195483825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
195583825b71SWarner Losh 	while ((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
195683825b71SWarner Losh #ifdef DEVICE_POLLING
195783825b71SWarner Losh 		if (ifp->if_capenable & IFCAP_POLLING) {
195883825b71SWarner Losh 			if (sc->rxcycles <= 0)
195983825b71SWarner Losh 				break;
196083825b71SWarner Losh 			sc->rxcycles--;
196183825b71SWarner Losh 		}
196283825b71SWarner Losh #endif
196383825b71SWarner Losh 		cur_rx = sc->xl_cdata.xl_rx_head;
196483825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = cur_rx->xl_next;
196583825b71SWarner Losh 		total_len = rxstat & XL_RXSTAT_LENMASK;
19665a13764bSPyun YongHyeon 		rx_npkts++;
196783825b71SWarner Losh 
196883825b71SWarner Losh 		/*
196983825b71SWarner Losh 		 * Since we have told the chip to allow large frames,
197083825b71SWarner Losh 		 * we need to trap giant frame errors in software. We allow
197183825b71SWarner Losh 		 * a little more than the normal frame size to account for
197283825b71SWarner Losh 		 * frames with VLAN tags.
197383825b71SWarner Losh 		 */
197483825b71SWarner Losh 		if (total_len > XL_MAX_FRAMELEN)
197583825b71SWarner Losh 			rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE);
197683825b71SWarner Losh 
197783825b71SWarner Losh 		/*
197883825b71SWarner Losh 		 * If an error occurs, update stats, clear the
197983825b71SWarner Losh 		 * status word and leave the mbuf cluster in place:
198083825b71SWarner Losh 		 * it should simply get re-used next time this descriptor
198183825b71SWarner Losh 		 * comes up in the ring.
198283825b71SWarner Losh 		 */
198383825b71SWarner Losh 		if (rxstat & XL_RXSTAT_UP_ERROR) {
198483825b71SWarner Losh 			ifp->if_ierrors++;
198583825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
198683825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
198783825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
198883825b71SWarner Losh 			continue;
198983825b71SWarner Losh 		}
199083825b71SWarner Losh 
199183825b71SWarner Losh 		/*
199283825b71SWarner Losh 		 * If the error bit was not set, the upload complete
199383825b71SWarner Losh 		 * bit should be set which means we have a valid packet.
199483825b71SWarner Losh 		 * If not, something truly strange has happened.
199583825b71SWarner Losh 		 */
199683825b71SWarner Losh 		if (!(rxstat & XL_RXSTAT_UP_CMPLT)) {
199783825b71SWarner Losh 			device_printf(sc->xl_dev,
199883825b71SWarner Losh 			    "bad receive status -- packet dropped\n");
199983825b71SWarner Losh 			ifp->if_ierrors++;
200083825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
200183825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
200283825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
200383825b71SWarner Losh 			continue;
200483825b71SWarner Losh 		}
200583825b71SWarner Losh 
200683825b71SWarner Losh 		/* No errors; receive the packet. */
200783825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map,
200883825b71SWarner Losh 		    BUS_DMASYNC_POSTREAD);
200983825b71SWarner Losh 		m = cur_rx->xl_mbuf;
201083825b71SWarner Losh 
201183825b71SWarner Losh 		/*
201283825b71SWarner Losh 		 * Try to conjure up a new mbuf cluster. If that
201383825b71SWarner Losh 		 * fails, it means we have an out of memory condition and
201483825b71SWarner Losh 		 * should leave the buffer in place and continue. This will
201583825b71SWarner Losh 		 * result in a lost packet, but there's little else we
201683825b71SWarner Losh 		 * can do in this situation.
201783825b71SWarner Losh 		 */
201883825b71SWarner Losh 		if (xl_newbuf(sc, cur_rx)) {
201983825b71SWarner Losh 			ifp->if_ierrors++;
202083825b71SWarner Losh 			cur_rx->xl_ptr->xl_status = 0;
202183825b71SWarner Losh 			bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
202283825b71SWarner Losh 			    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
202383825b71SWarner Losh 			continue;
202483825b71SWarner Losh 		}
202583825b71SWarner Losh 		bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
202683825b71SWarner Losh 		    sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
202783825b71SWarner Losh 
202883825b71SWarner Losh 		ifp->if_ipackets++;
202983825b71SWarner Losh 		m->m_pkthdr.rcvif = ifp;
203083825b71SWarner Losh 		m->m_pkthdr.len = m->m_len = total_len;
203183825b71SWarner Losh 
203283825b71SWarner Losh 		if (ifp->if_capenable & IFCAP_RXCSUM) {
203383825b71SWarner Losh 			/* Do IP checksum checking. */
203483825b71SWarner Losh 			if (rxstat & XL_RXSTAT_IPCKOK)
203583825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
203683825b71SWarner Losh 			if (!(rxstat & XL_RXSTAT_IPCKERR))
203783825b71SWarner Losh 				m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
203883825b71SWarner Losh 			if ((rxstat & XL_RXSTAT_TCPCOK &&
203983825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_TCPCKERR)) ||
204083825b71SWarner Losh 			    (rxstat & XL_RXSTAT_UDPCKOK &&
204183825b71SWarner Losh 			     !(rxstat & XL_RXSTAT_UDPCKERR))) {
204283825b71SWarner Losh 				m->m_pkthdr.csum_flags |=
204383825b71SWarner Losh 					CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
204483825b71SWarner Losh 				m->m_pkthdr.csum_data = 0xffff;
204583825b71SWarner Losh 			}
204683825b71SWarner Losh 		}
204783825b71SWarner Losh 
204883825b71SWarner Losh 		XL_UNLOCK(sc);
204983825b71SWarner Losh 		(*ifp->if_input)(ifp, m);
205083825b71SWarner Losh 		XL_LOCK(sc);
205183825b71SWarner Losh 
205283825b71SWarner Losh 		/*
205383825b71SWarner Losh 		 * If we are running from the taskqueue, the interface
205483825b71SWarner Losh 		 * might have been stopped while we were passing the last
205583825b71SWarner Losh 		 * packet up the network stack.
205683825b71SWarner Losh 		 */
205783825b71SWarner Losh 		if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
20581abcdbd1SAttilio Rao 			return (rx_npkts);
205983825b71SWarner Losh 	}
206083825b71SWarner Losh 
206183825b71SWarner Losh 	/*
206283825b71SWarner Losh 	 * Handle the 'end of channel' condition. When the upload
206383825b71SWarner Losh 	 * engine hits the end of the RX ring, it will stall. This
206483825b71SWarner Losh 	 * is our cue to flush the RX ring, reload the uplist pointer
206583825b71SWarner Losh 	 * register and unstall the engine.
206683825b71SWarner Losh 	 * XXX This is actually a little goofy. With the ThunderLAN
206783825b71SWarner Losh 	 * chip, you get an interrupt when the receiver hits the end
206883825b71SWarner Losh 	 * of the receive ring, which tells you exactly when you
206983825b71SWarner Losh 	 * you need to reload the ring pointer. Here we have to
207083825b71SWarner Losh 	 * fake it. I'm mad at myself for not being clever enough
207183825b71SWarner Losh 	 * to avoid the use of a goto here.
207283825b71SWarner Losh 	 */
207383825b71SWarner Losh 	if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 ||
207483825b71SWarner Losh 		CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) {
207583825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
207683825b71SWarner Losh 		xl_wait(sc);
207783825b71SWarner Losh 		CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
207883825b71SWarner Losh 		sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0];
207983825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
208083825b71SWarner Losh 		goto again;
208183825b71SWarner Losh 	}
20821abcdbd1SAttilio Rao 	return (rx_npkts);
208383825b71SWarner Losh }
208483825b71SWarner Losh 
208583825b71SWarner Losh /*
208683825b71SWarner Losh  * Taskqueue wrapper for xl_rxeof().
208783825b71SWarner Losh  */
208883825b71SWarner Losh static void
208983825b71SWarner Losh xl_rxeof_task(void *arg, int pending)
209083825b71SWarner Losh {
209183825b71SWarner Losh 	struct xl_softc *sc = (struct xl_softc *)arg;
209283825b71SWarner Losh 
209383825b71SWarner Losh 	XL_LOCK(sc);
209483825b71SWarner Losh 	if (sc->xl_ifp->if_drv_flags & IFF_DRV_RUNNING)
209583825b71SWarner Losh 		xl_rxeof(sc);
209683825b71SWarner Losh 	XL_UNLOCK(sc);
209783825b71SWarner Losh }
209883825b71SWarner Losh 
209983825b71SWarner Losh /*
210083825b71SWarner Losh  * A frame was downloaded to the chip. It's safe for us to clean up
210183825b71SWarner Losh  * the list buffers.
210283825b71SWarner Losh  */
210383825b71SWarner Losh static void
210483825b71SWarner Losh xl_txeof(struct xl_softc *sc)
210583825b71SWarner Losh {
210683825b71SWarner Losh 	struct xl_chain		*cur_tx;
210783825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
210883825b71SWarner Losh 
210983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
211083825b71SWarner Losh 
211183825b71SWarner Losh 	/*
211283825b71SWarner Losh 	 * Go through our tx list and free mbufs for those
211383825b71SWarner Losh 	 * frames that have been uploaded. Note: the 3c905B
211483825b71SWarner Losh 	 * sets a special bit in the status word to let us
211583825b71SWarner Losh 	 * know that a frame has been downloaded, but the
211683825b71SWarner Losh 	 * original 3c900/3c905 adapters don't do that.
211783825b71SWarner Losh 	 * Consequently, we have to use a different test if
211883825b71SWarner Losh 	 * xl_type != XL_TYPE_905B.
211983825b71SWarner Losh 	 */
212083825b71SWarner Losh 	while (sc->xl_cdata.xl_tx_head != NULL) {
212183825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_head;
212283825b71SWarner Losh 
212383825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DOWNLIST_PTR))
212483825b71SWarner Losh 			break;
212583825b71SWarner Losh 
212683825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = cur_tx->xl_next;
212783825b71SWarner Losh 		bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
212883825b71SWarner Losh 		    BUS_DMASYNC_POSTWRITE);
212983825b71SWarner Losh 		bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
213083825b71SWarner Losh 		m_freem(cur_tx->xl_mbuf);
213183825b71SWarner Losh 		cur_tx->xl_mbuf = NULL;
213283825b71SWarner Losh 		ifp->if_opackets++;
2133ba65e0ccSPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
213483825b71SWarner Losh 
213583825b71SWarner Losh 		cur_tx->xl_next = sc->xl_cdata.xl_tx_free;
213683825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx;
213783825b71SWarner Losh 	}
213883825b71SWarner Losh 
213983825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head == NULL) {
214083825b71SWarner Losh 		sc->xl_wdog_timer = 0;
214183825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = NULL;
214283825b71SWarner Losh 	} else {
214383825b71SWarner Losh 		if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED ||
214483825b71SWarner Losh 			!CSR_READ_4(sc, XL_DOWNLIST_PTR)) {
214583825b71SWarner Losh 			CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
214683825b71SWarner Losh 				sc->xl_cdata.xl_tx_head->xl_phys);
214783825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
214883825b71SWarner Losh 		}
214983825b71SWarner Losh 	}
215083825b71SWarner Losh }
215183825b71SWarner Losh 
215283825b71SWarner Losh static void
215383825b71SWarner Losh xl_txeof_90xB(struct xl_softc *sc)
215483825b71SWarner Losh {
215583825b71SWarner Losh 	struct xl_chain		*cur_tx = NULL;
215683825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
215783825b71SWarner Losh 	int			idx;
215883825b71SWarner Losh 
215983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
216083825b71SWarner Losh 
216183825b71SWarner Losh 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
216283825b71SWarner Losh 	    BUS_DMASYNC_POSTREAD);
216383825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_cons;
216483825b71SWarner Losh 	while (idx != sc->xl_cdata.xl_tx_prod) {
216583825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
216683825b71SWarner Losh 
216783825b71SWarner Losh 		if (!(le32toh(cur_tx->xl_ptr->xl_status) &
216883825b71SWarner Losh 		      XL_TXSTAT_DL_COMPLETE))
216983825b71SWarner Losh 			break;
217083825b71SWarner Losh 
217183825b71SWarner Losh 		if (cur_tx->xl_mbuf != NULL) {
217283825b71SWarner Losh 			bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
217383825b71SWarner Losh 			    BUS_DMASYNC_POSTWRITE);
217483825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
217583825b71SWarner Losh 			m_freem(cur_tx->xl_mbuf);
217683825b71SWarner Losh 			cur_tx->xl_mbuf = NULL;
217783825b71SWarner Losh 		}
217883825b71SWarner Losh 
217983825b71SWarner Losh 		ifp->if_opackets++;
218083825b71SWarner Losh 
218183825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt--;
218283825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
218383825b71SWarner Losh 	}
218483825b71SWarner Losh 
218583825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_cnt == 0)
218683825b71SWarner Losh 		sc->xl_wdog_timer = 0;
218783825b71SWarner Losh 	sc->xl_cdata.xl_tx_cons = idx;
218883825b71SWarner Losh 
218983825b71SWarner Losh 	if (cur_tx != NULL)
219083825b71SWarner Losh 		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
219183825b71SWarner Losh }
219283825b71SWarner Losh 
219383825b71SWarner Losh /*
219483825b71SWarner Losh  * TX 'end of channel' interrupt handler. Actually, we should
219583825b71SWarner Losh  * only get a 'TX complete' interrupt if there's a transmit error,
219683825b71SWarner Losh  * so this is really TX error handler.
219783825b71SWarner Losh  */
219883825b71SWarner Losh static void
219983825b71SWarner Losh xl_txeoc(struct xl_softc *sc)
220083825b71SWarner Losh {
220183825b71SWarner Losh 	u_int8_t		txstat;
220283825b71SWarner Losh 
220383825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
220483825b71SWarner Losh 
220583825b71SWarner Losh 	while ((txstat = CSR_READ_1(sc, XL_TX_STATUS))) {
220683825b71SWarner Losh 		if (txstat & XL_TXSTATUS_UNDERRUN ||
220783825b71SWarner Losh 			txstat & XL_TXSTATUS_JABBER ||
220883825b71SWarner Losh 			txstat & XL_TXSTATUS_RECLAIM) {
220983825b71SWarner Losh 			device_printf(sc->xl_dev,
22107498e81aSPyun YongHyeon 			    "transmission error: 0x%02x\n", txstat);
221183825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
221283825b71SWarner Losh 			xl_wait(sc);
221383825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
221483825b71SWarner Losh 				if (sc->xl_cdata.xl_tx_cnt) {
221583825b71SWarner Losh 					int			i;
221683825b71SWarner Losh 					struct xl_chain		*c;
221783825b71SWarner Losh 
221883825b71SWarner Losh 					i = sc->xl_cdata.xl_tx_cons;
221983825b71SWarner Losh 					c = &sc->xl_cdata.xl_tx_chain[i];
222083825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
222183825b71SWarner Losh 					    c->xl_phys);
222283825b71SWarner Losh 					CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
22237498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
222483825b71SWarner Losh 				}
222583825b71SWarner Losh 			} else {
22267498e81aSPyun YongHyeon 				if (sc->xl_cdata.xl_tx_head != NULL) {
222783825b71SWarner Losh 					CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
222883825b71SWarner Losh 					    sc->xl_cdata.xl_tx_head->xl_phys);
22297498e81aSPyun YongHyeon 					sc->xl_wdog_timer = 5;
22307498e81aSPyun YongHyeon 				}
223183825b71SWarner Losh 			}
223283825b71SWarner Losh 			/*
223383825b71SWarner Losh 			 * Remember to set this for the
223483825b71SWarner Losh 			 * first generation 3c90X chips.
223583825b71SWarner Losh 			 */
223683825b71SWarner Losh 			CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
223783825b71SWarner Losh 			if (txstat & XL_TXSTATUS_UNDERRUN &&
223883825b71SWarner Losh 			    sc->xl_tx_thresh < XL_PACKET_SIZE) {
223983825b71SWarner Losh 				sc->xl_tx_thresh += XL_MIN_FRAMELEN;
224083825b71SWarner Losh 				device_printf(sc->xl_dev,
224183825b71SWarner Losh "tx underrun, increasing tx start threshold to %d bytes\n", sc->xl_tx_thresh);
224283825b71SWarner Losh 			}
224383825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
224483825b71SWarner Losh 			    XL_CMD_TX_SET_START|sc->xl_tx_thresh);
224583825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B) {
224683825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND,
224783825b71SWarner Losh 				XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
224883825b71SWarner Losh 			}
224983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
225083825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
225183825b71SWarner Losh 		} else {
225283825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
225383825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
225483825b71SWarner Losh 		}
225583825b71SWarner Losh 		/*
225683825b71SWarner Losh 		 * Write an arbitrary byte to the TX_STATUS register
225783825b71SWarner Losh 		 * to clear this interrupt/error and advance to the next.
225883825b71SWarner Losh 		 */
225983825b71SWarner Losh 		CSR_WRITE_1(sc, XL_TX_STATUS, 0x01);
226083825b71SWarner Losh 	}
226183825b71SWarner Losh }
226283825b71SWarner Losh 
226383825b71SWarner Losh static void
226483825b71SWarner Losh xl_intr(void *arg)
226583825b71SWarner Losh {
226683825b71SWarner Losh 	struct xl_softc		*sc = arg;
226783825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
226883825b71SWarner Losh 	u_int16_t		status;
226983825b71SWarner Losh 
227083825b71SWarner Losh 	XL_LOCK(sc);
227183825b71SWarner Losh 
227283825b71SWarner Losh #ifdef DEVICE_POLLING
227383825b71SWarner Losh 	if (ifp->if_capenable & IFCAP_POLLING) {
227483825b71SWarner Losh 		XL_UNLOCK(sc);
227583825b71SWarner Losh 		return;
227683825b71SWarner Losh 	}
227783825b71SWarner Losh #endif
227883825b71SWarner Losh 
227974517b07SPyun YongHyeon 	for (;;) {
228074517b07SPyun YongHyeon 		status = CSR_READ_2(sc, XL_STATUS);
228174517b07SPyun YongHyeon 		if ((status & XL_INTRS) == 0 || status == 0xFFFF)
228274517b07SPyun YongHyeon 			break;
228383825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
228483825b71SWarner Losh 		    XL_CMD_INTR_ACK|(status & XL_INTRS));
228574517b07SPyun YongHyeon 		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
228674517b07SPyun YongHyeon 			break;
228783825b71SWarner Losh 
228883825b71SWarner Losh 		if (status & XL_STAT_UP_COMPLETE) {
22895a13764bSPyun YongHyeon 			if (xl_rxeof(sc) == 0) {
229083825b71SWarner Losh 				while (xl_rx_resync(sc))
229183825b71SWarner Losh 					xl_rxeof(sc);
229283825b71SWarner Losh 			}
229383825b71SWarner Losh 		}
229483825b71SWarner Losh 
229583825b71SWarner Losh 		if (status & XL_STAT_DOWN_COMPLETE) {
229683825b71SWarner Losh 			if (sc->xl_type == XL_TYPE_905B)
229783825b71SWarner Losh 				xl_txeof_90xB(sc);
229883825b71SWarner Losh 			else
229983825b71SWarner Losh 				xl_txeof(sc);
230083825b71SWarner Losh 		}
230183825b71SWarner Losh 
230283825b71SWarner Losh 		if (status & XL_STAT_TX_COMPLETE) {
230383825b71SWarner Losh 			ifp->if_oerrors++;
230483825b71SWarner Losh 			xl_txeoc(sc);
230583825b71SWarner Losh 		}
230683825b71SWarner Losh 
230783825b71SWarner Losh 		if (status & XL_STAT_ADFAIL) {
230827b031a9SPyun YongHyeon 			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
230983825b71SWarner Losh 			xl_init_locked(sc);
231074517b07SPyun YongHyeon 			break;
231183825b71SWarner Losh 		}
231283825b71SWarner Losh 
231348dcbc33SPyun YongHyeon 		if (status & XL_STAT_STATSOFLOW)
231448dcbc33SPyun YongHyeon 			xl_stats_update(sc);
231583825b71SWarner Losh 	}
231683825b71SWarner Losh 
231774517b07SPyun YongHyeon 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
231874517b07SPyun YongHyeon 	    ifp->if_drv_flags & IFF_DRV_RUNNING) {
231983825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
232083825b71SWarner Losh 			xl_start_90xB_locked(ifp);
232183825b71SWarner Losh 		else
232283825b71SWarner Losh 			xl_start_locked(ifp);
232383825b71SWarner Losh 	}
232483825b71SWarner Losh 
232583825b71SWarner Losh 	XL_UNLOCK(sc);
232683825b71SWarner Losh }
232783825b71SWarner Losh 
232883825b71SWarner Losh #ifdef DEVICE_POLLING
23291abcdbd1SAttilio Rao static int
233083825b71SWarner Losh xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
233183825b71SWarner Losh {
233283825b71SWarner Losh 	struct xl_softc *sc = ifp->if_softc;
23331abcdbd1SAttilio Rao 	int rx_npkts = 0;
233483825b71SWarner Losh 
233583825b71SWarner Losh 	XL_LOCK(sc);
233683825b71SWarner Losh 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
23371abcdbd1SAttilio Rao 		rx_npkts = xl_poll_locked(ifp, cmd, count);
233883825b71SWarner Losh 	XL_UNLOCK(sc);
23391abcdbd1SAttilio Rao 	return (rx_npkts);
234083825b71SWarner Losh }
234183825b71SWarner Losh 
23421abcdbd1SAttilio Rao static int
234383825b71SWarner Losh xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
234483825b71SWarner Losh {
234583825b71SWarner Losh 	struct xl_softc *sc = ifp->if_softc;
23461abcdbd1SAttilio Rao 	int rx_npkts;
234783825b71SWarner Losh 
234883825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
234983825b71SWarner Losh 
235083825b71SWarner Losh 	sc->rxcycles = count;
23511abcdbd1SAttilio Rao 	rx_npkts = xl_rxeof(sc);
235283825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
235383825b71SWarner Losh 		xl_txeof_90xB(sc);
235483825b71SWarner Losh 	else
235583825b71SWarner Losh 		xl_txeof(sc);
235683825b71SWarner Losh 
235783825b71SWarner Losh 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
235883825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
235983825b71SWarner Losh 			xl_start_90xB_locked(ifp);
236083825b71SWarner Losh 		else
236183825b71SWarner Losh 			xl_start_locked(ifp);
236283825b71SWarner Losh 	}
236383825b71SWarner Losh 
236483825b71SWarner Losh 	if (cmd == POLL_AND_CHECK_STATUS) {
236583825b71SWarner Losh 		u_int16_t status;
236683825b71SWarner Losh 
236783825b71SWarner Losh 		status = CSR_READ_2(sc, XL_STATUS);
236883825b71SWarner Losh 		if (status & XL_INTRS && status != 0xFFFF) {
236983825b71SWarner Losh 			CSR_WRITE_2(sc, XL_COMMAND,
237083825b71SWarner Losh 			    XL_CMD_INTR_ACK|(status & XL_INTRS));
237183825b71SWarner Losh 
237283825b71SWarner Losh 			if (status & XL_STAT_TX_COMPLETE) {
237383825b71SWarner Losh 				ifp->if_oerrors++;
237483825b71SWarner Losh 				xl_txeoc(sc);
237583825b71SWarner Losh 			}
237683825b71SWarner Losh 
237783825b71SWarner Losh 			if (status & XL_STAT_ADFAIL) {
237827b031a9SPyun YongHyeon 				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
237983825b71SWarner Losh 				xl_init_locked(sc);
238083825b71SWarner Losh 			}
238183825b71SWarner Losh 
238248dcbc33SPyun YongHyeon 			if (status & XL_STAT_STATSOFLOW)
2383*de53eba0SPyun YongHyeon 				xl_stats_update(sc);
238483825b71SWarner Losh 		}
238583825b71SWarner Losh 	}
23861abcdbd1SAttilio Rao 	return (rx_npkts);
238783825b71SWarner Losh }
238883825b71SWarner Losh #endif /* DEVICE_POLLING */
238983825b71SWarner Losh 
239083825b71SWarner Losh static void
239148dcbc33SPyun YongHyeon xl_tick(void *xsc)
239283825b71SWarner Losh {
239383825b71SWarner Losh 	struct xl_softc *sc = xsc;
239448dcbc33SPyun YongHyeon 	struct mii_data *mii;
239583825b71SWarner Losh 
239683825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
239783825b71SWarner Losh 
239848dcbc33SPyun YongHyeon 	if (sc->xl_miibus != NULL) {
239948dcbc33SPyun YongHyeon 		mii = device_get_softc(sc->xl_miibus);
240048dcbc33SPyun YongHyeon 		mii_tick(mii);
240148dcbc33SPyun YongHyeon 	}
240248dcbc33SPyun YongHyeon 
240348dcbc33SPyun YongHyeon 	xl_stats_update(sc);
240483825b71SWarner Losh 	if (xl_watchdog(sc) == EJUSTRETURN)
240583825b71SWarner Losh 		return;
240683825b71SWarner Losh 
240748dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
240883825b71SWarner Losh }
240983825b71SWarner Losh 
241083825b71SWarner Losh static void
241148dcbc33SPyun YongHyeon xl_stats_update(struct xl_softc *sc)
241283825b71SWarner Losh {
241383825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
241483825b71SWarner Losh 	struct xl_stats		xl_stats;
241583825b71SWarner Losh 	u_int8_t		*p;
241683825b71SWarner Losh 	int			i;
241783825b71SWarner Losh 
241883825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
241983825b71SWarner Losh 
242083825b71SWarner Losh 	bzero((char *)&xl_stats, sizeof(struct xl_stats));
242183825b71SWarner Losh 
242283825b71SWarner Losh 	p = (u_int8_t *)&xl_stats;
242383825b71SWarner Losh 
242483825b71SWarner Losh 	/* Read all the stats registers. */
242583825b71SWarner Losh 	XL_SEL_WIN(6);
242683825b71SWarner Losh 
242783825b71SWarner Losh 	for (i = 0; i < 16; i++)
242883825b71SWarner Losh 		*p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i);
242983825b71SWarner Losh 
243083825b71SWarner Losh 	ifp->if_ierrors += xl_stats.xl_rx_overrun;
243183825b71SWarner Losh 
243283825b71SWarner Losh 	ifp->if_collisions += xl_stats.xl_tx_multi_collision +
243383825b71SWarner Losh 	    xl_stats.xl_tx_single_collision + xl_stats.xl_tx_late_collision;
243483825b71SWarner Losh 
243583825b71SWarner Losh 	/*
243683825b71SWarner Losh 	 * Boomerang and cyclone chips have an extra stats counter
243783825b71SWarner Losh 	 * in window 4 (BadSSD). We have to read this too in order
243883825b71SWarner Losh 	 * to clear out all the stats registers and avoid a statsoflow
243983825b71SWarner Losh 	 * interrupt.
244083825b71SWarner Losh 	 */
244183825b71SWarner Losh 	XL_SEL_WIN(4);
244283825b71SWarner Losh 	CSR_READ_1(sc, XL_W4_BADSSD);
244383825b71SWarner Losh 	XL_SEL_WIN(7);
244483825b71SWarner Losh }
244583825b71SWarner Losh 
244683825b71SWarner Losh /*
244783825b71SWarner Losh  * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
244883825b71SWarner Losh  * pointers to the fragment pointers.
244983825b71SWarner Losh  */
245083825b71SWarner Losh static int
245183825b71SWarner Losh xl_encap(struct xl_softc *sc, struct xl_chain *c, struct mbuf **m_head)
245283825b71SWarner Losh {
245383825b71SWarner Losh 	struct mbuf		*m_new;
245483825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
245583825b71SWarner Losh 	int			error, i, nseg, total_len;
245683825b71SWarner Losh 	u_int32_t		status;
245783825b71SWarner Losh 
245883825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
245983825b71SWarner Losh 
246083825b71SWarner Losh 	error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map, *m_head,
246183825b71SWarner Losh 	    sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
246283825b71SWarner Losh 
246383825b71SWarner Losh 	if (error && error != EFBIG) {
246483825b71SWarner Losh 		if_printf(ifp, "can't map mbuf (error %d)\n", error);
246583825b71SWarner Losh 		return (error);
246683825b71SWarner Losh 	}
246783825b71SWarner Losh 
246883825b71SWarner Losh 	/*
246983825b71SWarner Losh 	 * Handle special case: we used up all 63 fragments,
247083825b71SWarner Losh 	 * but we have more mbufs left in the chain. Copy the
247183825b71SWarner Losh 	 * data into an mbuf cluster. Note that we don't
247283825b71SWarner Losh 	 * bother clearing the values in the other fragment
247383825b71SWarner Losh 	 * pointers/counters; it wouldn't gain us anything,
247483825b71SWarner Losh 	 * and would waste cycles.
247583825b71SWarner Losh 	 */
247683825b71SWarner Losh 	if (error) {
247783825b71SWarner Losh 		m_new = m_collapse(*m_head, M_DONTWAIT, XL_MAXFRAGS);
247883825b71SWarner Losh 		if (m_new == NULL) {
247983825b71SWarner Losh 			m_freem(*m_head);
248083825b71SWarner Losh 			*m_head = NULL;
248183825b71SWarner Losh 			return (ENOBUFS);
248283825b71SWarner Losh 		}
248383825b71SWarner Losh 		*m_head = m_new;
248483825b71SWarner Losh 
248583825b71SWarner Losh 		error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map,
248683825b71SWarner Losh 		    *m_head, sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
248783825b71SWarner Losh 		if (error) {
248883825b71SWarner Losh 			m_freem(*m_head);
248983825b71SWarner Losh 			*m_head = NULL;
249083825b71SWarner Losh 			if_printf(ifp, "can't map mbuf (error %d)\n", error);
249183825b71SWarner Losh 			return (error);
249283825b71SWarner Losh 		}
249383825b71SWarner Losh 	}
249483825b71SWarner Losh 
249583825b71SWarner Losh 	KASSERT(nseg <= XL_MAXFRAGS,
249683825b71SWarner Losh 	    ("%s: too many DMA segments (%d)", __func__, nseg));
249783825b71SWarner Losh 	if (nseg == 0) {
249883825b71SWarner Losh 		m_freem(*m_head);
249983825b71SWarner Losh 		*m_head = NULL;
250083825b71SWarner Losh 		return (EIO);
250183825b71SWarner Losh 	}
25024f58a95cSPyun YongHyeon 	bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE);
250383825b71SWarner Losh 
250483825b71SWarner Losh 	total_len = 0;
250583825b71SWarner Losh 	for (i = 0; i < nseg; i++) {
250683825b71SWarner Losh 		KASSERT(sc->xl_cdata.xl_tx_segs[i].ds_len <= MCLBYTES,
250783825b71SWarner Losh 		    ("segment size too large"));
250883825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_addr =
250983825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_addr);
251083825b71SWarner Losh 		c->xl_ptr->xl_frag[i].xl_len =
251183825b71SWarner Losh 		    htole32(sc->xl_cdata.xl_tx_segs[i].ds_len);
251283825b71SWarner Losh 		total_len += sc->xl_cdata.xl_tx_segs[i].ds_len;
251383825b71SWarner Losh 	}
251478564edaSPyun YongHyeon 	c->xl_ptr->xl_frag[nseg - 1].xl_len |= htole32(XL_LAST_FRAG);
251583825b71SWarner Losh 
251683825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
251783825b71SWarner Losh 		status = XL_TXSTAT_RND_DEFEAT;
251883825b71SWarner Losh 
251983825b71SWarner Losh #ifndef XL905B_TXCSUM_BROKEN
25208e95322aSPyun YongHyeon 		if ((*m_head)->m_pkthdr.csum_flags) {
25218e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP)
252283825b71SWarner Losh 				status |= XL_TXSTAT_IPCKSUM;
25238e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
252483825b71SWarner Losh 				status |= XL_TXSTAT_TCPCKSUM;
25258e95322aSPyun YongHyeon 			if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
252683825b71SWarner Losh 				status |= XL_TXSTAT_UDPCKSUM;
252783825b71SWarner Losh 		}
252883825b71SWarner Losh #endif
25294f58a95cSPyun YongHyeon 	} else
25304f58a95cSPyun YongHyeon 		status = total_len;
253183825b71SWarner Losh 	c->xl_ptr->xl_status = htole32(status);
25324f58a95cSPyun YongHyeon 	c->xl_ptr->xl_next = 0;
253383825b71SWarner Losh 
253483825b71SWarner Losh 	c->xl_mbuf = *m_head;
253583825b71SWarner Losh 	return (0);
253683825b71SWarner Losh }
253783825b71SWarner Losh 
253883825b71SWarner Losh /*
253983825b71SWarner Losh  * Main transmit routine. To avoid having to do mbuf copies, we put pointers
254083825b71SWarner Losh  * to the mbuf data regions directly in the transmit lists. We also save a
254183825b71SWarner Losh  * copy of the pointers since the transmit list fragment pointers are
254283825b71SWarner Losh  * physical addresses.
254383825b71SWarner Losh  */
254483825b71SWarner Losh 
254583825b71SWarner Losh static void
254683825b71SWarner Losh xl_start(struct ifnet *ifp)
254783825b71SWarner Losh {
254883825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
254983825b71SWarner Losh 
255083825b71SWarner Losh 	XL_LOCK(sc);
255183825b71SWarner Losh 
255283825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
255383825b71SWarner Losh 		xl_start_90xB_locked(ifp);
255483825b71SWarner Losh 	else
255583825b71SWarner Losh 		xl_start_locked(ifp);
255683825b71SWarner Losh 
255783825b71SWarner Losh 	XL_UNLOCK(sc);
255883825b71SWarner Losh }
255983825b71SWarner Losh 
256083825b71SWarner Losh static void
256183825b71SWarner Losh xl_start_locked(struct ifnet *ifp)
256283825b71SWarner Losh {
256383825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
25644a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
256583825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
25664a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
256783825b71SWarner Losh 	int			error;
256883825b71SWarner Losh 
256983825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
257083825b71SWarner Losh 
2571ba65e0ccSPyun YongHyeon 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2572ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
2573ba65e0ccSPyun YongHyeon 		return;
257483825b71SWarner Losh 	/*
257583825b71SWarner Losh 	 * Check for an available queue slot. If there are none,
257683825b71SWarner Losh 	 * punt.
257783825b71SWarner Losh 	 */
257883825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_free == NULL) {
257983825b71SWarner Losh 		xl_txeoc(sc);
258083825b71SWarner Losh 		xl_txeof(sc);
258183825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_free == NULL) {
258283825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
258383825b71SWarner Losh 			return;
258483825b71SWarner Losh 		}
258583825b71SWarner Losh 	}
258683825b71SWarner Losh 
258783825b71SWarner Losh 	start_tx = sc->xl_cdata.xl_tx_free;
258883825b71SWarner Losh 
258983825b71SWarner Losh 	for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
259083825b71SWarner Losh 	    sc->xl_cdata.xl_tx_free != NULL;) {
259183825b71SWarner Losh 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
259283825b71SWarner Losh 		if (m_head == NULL)
259383825b71SWarner Losh 			break;
259483825b71SWarner Losh 
259583825b71SWarner Losh 		/* Pick a descriptor off the free list. */
25964a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
259783825b71SWarner Losh 		cur_tx = sc->xl_cdata.xl_tx_free;
259883825b71SWarner Losh 
259983825b71SWarner Losh 		/* Pack the data into the descriptor. */
260083825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
260183825b71SWarner Losh 		if (error) {
26024a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
260383825b71SWarner Losh 			if (m_head == NULL)
260483825b71SWarner Losh 				break;
260583825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
260683825b71SWarner Losh 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
260783825b71SWarner Losh 			break;
260883825b71SWarner Losh 		}
260983825b71SWarner Losh 
261083825b71SWarner Losh 		sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
261183825b71SWarner Losh 		cur_tx->xl_next = NULL;
261283825b71SWarner Losh 
261383825b71SWarner Losh 		/* Chain it together. */
261483825b71SWarner Losh 		if (prev != NULL) {
261583825b71SWarner Losh 			prev->xl_next = cur_tx;
261683825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
261783825b71SWarner Losh 		}
261883825b71SWarner Losh 		prev = cur_tx;
261983825b71SWarner Losh 
262083825b71SWarner Losh 		/*
262183825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
262283825b71SWarner Losh 		 * to him.
262383825b71SWarner Losh 		 */
262483825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
262583825b71SWarner Losh 	}
262683825b71SWarner Losh 
262783825b71SWarner Losh 	/*
262883825b71SWarner Losh 	 * If there are no packets queued, bail.
262983825b71SWarner Losh 	 */
263083825b71SWarner Losh 	if (cur_tx == NULL)
263183825b71SWarner Losh 		return;
263283825b71SWarner Losh 
263383825b71SWarner Losh 	/*
263483825b71SWarner Losh 	 * Place the request for the upload interrupt
263583825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
263683825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
263783825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
263883825b71SWarner Losh 	 * once for each packet.
263983825b71SWarner Losh 	 */
264078564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
264183825b71SWarner Losh 
264283825b71SWarner Losh 	/*
264383825b71SWarner Losh 	 * Queue the packets. If the TX channel is clear, update
264483825b71SWarner Losh 	 * the downlist pointer register.
264583825b71SWarner Losh 	 */
264683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
264783825b71SWarner Losh 	xl_wait(sc);
264883825b71SWarner Losh 
264983825b71SWarner Losh 	if (sc->xl_cdata.xl_tx_head != NULL) {
265083825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_next = start_tx;
265183825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next =
265283825b71SWarner Losh 		    htole32(start_tx->xl_phys);
265378564edaSPyun YongHyeon 		sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status &=
265478564edaSPyun YongHyeon 		    htole32(~XL_TXSTAT_DL_INTR);
265583825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
265683825b71SWarner Losh 	} else {
265783825b71SWarner Losh 		sc->xl_cdata.xl_tx_head = start_tx;
265883825b71SWarner Losh 		sc->xl_cdata.xl_tx_tail = cur_tx;
265983825b71SWarner Losh 	}
26600ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
26610ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
266283825b71SWarner Losh 	if (!CSR_READ_4(sc, XL_DOWNLIST_PTR))
266383825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys);
266483825b71SWarner Losh 
266583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
266683825b71SWarner Losh 
266783825b71SWarner Losh 	XL_SEL_WIN(7);
266883825b71SWarner Losh 
266983825b71SWarner Losh 	/*
267083825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
267183825b71SWarner Losh 	 */
267283825b71SWarner Losh 	sc->xl_wdog_timer = 5;
267383825b71SWarner Losh 
267483825b71SWarner Losh 	/*
267583825b71SWarner Losh 	 * XXX Under certain conditions, usually on slower machines
267683825b71SWarner Losh 	 * where interrupts may be dropped, it's possible for the
267783825b71SWarner Losh 	 * adapter to chew up all the buffers in the receive ring
267883825b71SWarner Losh 	 * and stall, without us being able to do anything about it.
267983825b71SWarner Losh 	 * To guard against this, we need to make a pass over the
268083825b71SWarner Losh 	 * RX queue to make sure there aren't any packets pending.
268183825b71SWarner Losh 	 * Doing it here means we can flush the receive ring at the
268283825b71SWarner Losh 	 * same time the chip is DMAing the transmit descriptors we
268383825b71SWarner Losh 	 * just gave it.
268483825b71SWarner Losh 	 *
268583825b71SWarner Losh 	 * 3Com goes to some lengths to emphasize the Parallel Tasking (tm)
268683825b71SWarner Losh 	 * nature of their chips in all their marketing literature;
268783825b71SWarner Losh 	 * we may as well take advantage of it. :)
268883825b71SWarner Losh 	 */
268983825b71SWarner Losh 	taskqueue_enqueue(taskqueue_swi, &sc->xl_task);
269083825b71SWarner Losh }
269183825b71SWarner Losh 
269283825b71SWarner Losh static void
269383825b71SWarner Losh xl_start_90xB_locked(struct ifnet *ifp)
269483825b71SWarner Losh {
269583825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
26964a5c7884SPyun YongHyeon 	struct mbuf		*m_head;
269783825b71SWarner Losh 	struct xl_chain		*prev = NULL, *cur_tx = NULL, *start_tx;
26984a5c7884SPyun YongHyeon 	struct xl_chain		*prev_tx;
269983825b71SWarner Losh 	int			error, idx;
270083825b71SWarner Losh 
270183825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
270283825b71SWarner Losh 
2703ba65e0ccSPyun YongHyeon 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
2704ba65e0ccSPyun YongHyeon 	    IFF_DRV_RUNNING)
270583825b71SWarner Losh 		return;
270683825b71SWarner Losh 
270783825b71SWarner Losh 	idx = sc->xl_cdata.xl_tx_prod;
270883825b71SWarner Losh 	start_tx = &sc->xl_cdata.xl_tx_chain[idx];
270983825b71SWarner Losh 
271083825b71SWarner Losh 	for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
271183825b71SWarner Losh 	    sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL;) {
271283825b71SWarner Losh 		if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) {
271383825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
271483825b71SWarner Losh 			break;
271583825b71SWarner Losh 		}
271683825b71SWarner Losh 
271783825b71SWarner Losh 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
271883825b71SWarner Losh 		if (m_head == NULL)
271983825b71SWarner Losh 			break;
272083825b71SWarner Losh 
27214a5c7884SPyun YongHyeon 		prev_tx = cur_tx;
272283825b71SWarner Losh 		cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
272383825b71SWarner Losh 
272483825b71SWarner Losh 		/* Pack the data into the descriptor. */
272583825b71SWarner Losh 		error = xl_encap(sc, cur_tx, &m_head);
272683825b71SWarner Losh 		if (error) {
27274a5c7884SPyun YongHyeon 			cur_tx = prev_tx;
272883825b71SWarner Losh 			if (m_head == NULL)
272983825b71SWarner Losh 				break;
273083825b71SWarner Losh 			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
273183825b71SWarner Losh 			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
273283825b71SWarner Losh 			break;
273383825b71SWarner Losh 		}
273483825b71SWarner Losh 
273583825b71SWarner Losh 		/* Chain it together. */
273683825b71SWarner Losh 		if (prev != NULL)
273783825b71SWarner Losh 			prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
273883825b71SWarner Losh 		prev = cur_tx;
273983825b71SWarner Losh 
274083825b71SWarner Losh 		/*
274183825b71SWarner Losh 		 * If there's a BPF listener, bounce a copy of this frame
274283825b71SWarner Losh 		 * to him.
274383825b71SWarner Losh 		 */
274483825b71SWarner Losh 		BPF_MTAP(ifp, cur_tx->xl_mbuf);
274583825b71SWarner Losh 
274683825b71SWarner Losh 		XL_INC(idx, XL_TX_LIST_CNT);
274783825b71SWarner Losh 		sc->xl_cdata.xl_tx_cnt++;
274883825b71SWarner Losh 	}
274983825b71SWarner Losh 
275083825b71SWarner Losh 	/*
275183825b71SWarner Losh 	 * If there are no packets queued, bail.
275283825b71SWarner Losh 	 */
275383825b71SWarner Losh 	if (cur_tx == NULL)
275483825b71SWarner Losh 		return;
275583825b71SWarner Losh 
275683825b71SWarner Losh 	/*
275783825b71SWarner Losh 	 * Place the request for the upload interrupt
275883825b71SWarner Losh 	 * in the last descriptor in the chain. This way, if
275983825b71SWarner Losh 	 * we're chaining several packets at once, we'll only
276083825b71SWarner Losh 	 * get an interrupt once for the whole chain rather than
276183825b71SWarner Losh 	 * once for each packet.
276283825b71SWarner Losh 	 */
276378564edaSPyun YongHyeon 	cur_tx->xl_ptr->xl_status |= htole32(XL_TXSTAT_DL_INTR);
276483825b71SWarner Losh 
276583825b71SWarner Losh 	/* Start transmission */
276683825b71SWarner Losh 	sc->xl_cdata.xl_tx_prod = idx;
276783825b71SWarner Losh 	start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys);
27680ecf6b16SPyun YongHyeon 	bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
27690ecf6b16SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
277083825b71SWarner Losh 
277183825b71SWarner Losh 	/*
277283825b71SWarner Losh 	 * Set a timeout in case the chip goes out to lunch.
277383825b71SWarner Losh 	 */
277483825b71SWarner Losh 	sc->xl_wdog_timer = 5;
277583825b71SWarner Losh }
277683825b71SWarner Losh 
277783825b71SWarner Losh static void
277883825b71SWarner Losh xl_init(void *xsc)
277983825b71SWarner Losh {
278083825b71SWarner Losh 	struct xl_softc		*sc = xsc;
278183825b71SWarner Losh 
278283825b71SWarner Losh 	XL_LOCK(sc);
278383825b71SWarner Losh 	xl_init_locked(sc);
278483825b71SWarner Losh 	XL_UNLOCK(sc);
278583825b71SWarner Losh }
278683825b71SWarner Losh 
278783825b71SWarner Losh static void
278883825b71SWarner Losh xl_init_locked(struct xl_softc *sc)
278983825b71SWarner Losh {
279083825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
279183825b71SWarner Losh 	int			error, i;
279283825b71SWarner Losh 	struct mii_data		*mii = NULL;
279383825b71SWarner Losh 
279483825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
279583825b71SWarner Losh 
279627b031a9SPyun YongHyeon 	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
279727b031a9SPyun YongHyeon 		return;
279883825b71SWarner Losh 	/*
279983825b71SWarner Losh 	 * Cancel pending I/O and free all RX/TX buffers.
280083825b71SWarner Losh 	 */
280183825b71SWarner Losh 	xl_stop(sc);
280283825b71SWarner Losh 
2803ac681091SPyun YongHyeon 	/* Reset the chip to a known state. */
2804ac681091SPyun YongHyeon 	xl_reset(sc);
2805ac681091SPyun YongHyeon 
280683825b71SWarner Losh 	if (sc->xl_miibus == NULL) {
280783825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
280883825b71SWarner Losh 		xl_wait(sc);
280983825b71SWarner Losh 	}
281083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
281183825b71SWarner Losh 	xl_wait(sc);
281283825b71SWarner Losh 	DELAY(10000);
281383825b71SWarner Losh 
281483825b71SWarner Losh 	if (sc->xl_miibus != NULL)
281583825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
281683825b71SWarner Losh 
28179ae11bbaSPyun YongHyeon 	/*
28189ae11bbaSPyun YongHyeon 	 * Clear WOL status and disable all WOL feature as WOL
28199ae11bbaSPyun YongHyeon 	 * would interfere Rx operation under normal environments.
28209ae11bbaSPyun YongHyeon 	 */
28219ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) != 0) {
28229ae11bbaSPyun YongHyeon 		XL_SEL_WIN(7);
28239ae11bbaSPyun YongHyeon 		CSR_READ_2(sc, XL_W7_BM_PME);
28249ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_W7_BM_PME, 0);
28259ae11bbaSPyun YongHyeon 	}
282683825b71SWarner Losh 	/* Init our MAC address */
282783825b71SWarner Losh 	XL_SEL_WIN(2);
282883825b71SWarner Losh 	for (i = 0; i < ETHER_ADDR_LEN; i++) {
282983825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i,
283083825b71SWarner Losh 				IF_LLADDR(sc->xl_ifp)[i]);
283183825b71SWarner Losh 	}
283283825b71SWarner Losh 
283383825b71SWarner Losh 	/* Clear the station mask. */
283483825b71SWarner Losh 	for (i = 0; i < 3; i++)
283583825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
283683825b71SWarner Losh #ifdef notdef
283783825b71SWarner Losh 	/* Reset TX and RX. */
283883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
283983825b71SWarner Losh 	xl_wait(sc);
284083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
284183825b71SWarner Losh 	xl_wait(sc);
284283825b71SWarner Losh #endif
284383825b71SWarner Losh 	/* Init circular RX list. */
284483825b71SWarner Losh 	error = xl_list_rx_init(sc);
284583825b71SWarner Losh 	if (error) {
284683825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the rx ring failed (%d)\n",
284783825b71SWarner Losh 		    error);
284883825b71SWarner Losh 		xl_stop(sc);
284983825b71SWarner Losh 		return;
285083825b71SWarner Losh 	}
285183825b71SWarner Losh 
285283825b71SWarner Losh 	/* Init TX descriptors. */
285383825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
285483825b71SWarner Losh 		error = xl_list_tx_init_90xB(sc);
285583825b71SWarner Losh 	else
285683825b71SWarner Losh 		error = xl_list_tx_init(sc);
285783825b71SWarner Losh 	if (error) {
285883825b71SWarner Losh 		device_printf(sc->xl_dev, "initialization of the tx ring failed (%d)\n",
285983825b71SWarner Losh 		    error);
286083825b71SWarner Losh 		xl_stop(sc);
286183825b71SWarner Losh 		return;
286283825b71SWarner Losh 	}
286383825b71SWarner Losh 
286483825b71SWarner Losh 	/*
286583825b71SWarner Losh 	 * Set the TX freethresh value.
286683825b71SWarner Losh 	 * Note that this has no effect on 3c905B "cyclone"
286783825b71SWarner Losh 	 * cards but is required for 3c900/3c905 "boomerang"
286883825b71SWarner Losh 	 * cards in order to enable the download engine.
286983825b71SWarner Losh 	 */
287083825b71SWarner Losh 	CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
287183825b71SWarner Losh 
287283825b71SWarner Losh 	/* Set the TX start threshold for best performance. */
287383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
287483825b71SWarner Losh 
287583825b71SWarner Losh 	/*
287683825b71SWarner Losh 	 * If this is a 3c905B, also set the tx reclaim threshold.
287783825b71SWarner Losh 	 * This helps cut down on the number of tx reclaim errors
287883825b71SWarner Losh 	 * that could happen on a busy network. The chip multiplies
287983825b71SWarner Losh 	 * the register value by 16 to obtain the actual threshold
288083825b71SWarner Losh 	 * in bytes, so we divide by 16 when setting the value here.
288183825b71SWarner Losh 	 * The existing threshold value can be examined by reading
288283825b71SWarner Losh 	 * the register at offset 9 in window 5.
288383825b71SWarner Losh 	 */
288483825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
288583825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND,
288683825b71SWarner Losh 		    XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
288783825b71SWarner Losh 	}
288883825b71SWarner Losh 
288983825b71SWarner Losh 	/* Set RX filter bits. */
2890dd0a7688SPyun YongHyeon 	xl_rxfilter(sc);
289183825b71SWarner Losh 
289283825b71SWarner Losh 	/*
289383825b71SWarner Losh 	 * Load the address of the RX list. We have to
289483825b71SWarner Losh 	 * stall the upload engine before we can manipulate
289583825b71SWarner Losh 	 * the uplist pointer register, then unstall it when
289683825b71SWarner Losh 	 * we're finished. We also have to wait for the
289783825b71SWarner Losh 	 * stall command to complete before proceeding.
289883825b71SWarner Losh 	 * Note that we have to do this after any RX resets
289983825b71SWarner Losh 	 * have completed since the uplist register is cleared
290083825b71SWarner Losh 	 * by a reset.
290183825b71SWarner Losh 	 */
290283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
290383825b71SWarner Losh 	xl_wait(sc);
290483825b71SWarner Losh 	CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
290583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
290683825b71SWarner Losh 	xl_wait(sc);
290783825b71SWarner Losh 
290883825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B) {
290983825b71SWarner Losh 		/* Set polling interval */
291083825b71SWarner Losh 		CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
291183825b71SWarner Losh 		/* Load the address of the TX list */
291283825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
291383825b71SWarner Losh 		xl_wait(sc);
291483825b71SWarner Losh 		CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
291583825b71SWarner Losh 		    sc->xl_cdata.xl_tx_chain[0].xl_phys);
291683825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
291783825b71SWarner Losh 		xl_wait(sc);
291883825b71SWarner Losh 	}
291983825b71SWarner Losh 
292083825b71SWarner Losh 	/*
292183825b71SWarner Losh 	 * If the coax transceiver is on, make sure to enable
292283825b71SWarner Losh 	 * the DC-DC converter.
292383825b71SWarner Losh 	 */
292483825b71SWarner Losh 	XL_SEL_WIN(3);
292583825b71SWarner Losh 	if (sc->xl_xcvr == XL_XCVR_COAX)
292683825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
292783825b71SWarner Losh 	else
292883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
292983825b71SWarner Losh 
293083825b71SWarner Losh 	/*
293183825b71SWarner Losh 	 * increase packet size to allow reception of 802.1q or ISL packets.
293283825b71SWarner Losh 	 * For the 3c90x chip, set the 'allow large packets' bit in the MAC
293383825b71SWarner Losh 	 * control register. For 3c90xB/C chips, use the RX packet size
293483825b71SWarner Losh 	 * register.
293583825b71SWarner Losh 	 */
293683825b71SWarner Losh 
293783825b71SWarner Losh 	if (sc->xl_type == XL_TYPE_905B)
293883825b71SWarner Losh 		CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
293983825b71SWarner Losh 	else {
294083825b71SWarner Losh 		u_int8_t macctl;
294183825b71SWarner Losh 		macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
294283825b71SWarner Losh 		macctl |= XL_MACCTRL_ALLOW_LARGE_PACK;
294383825b71SWarner Losh 		CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
294483825b71SWarner Losh 	}
294583825b71SWarner Losh 
294683825b71SWarner Losh 	/* Clear out the stats counters. */
294783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
294848dcbc33SPyun YongHyeon 	xl_stats_update(sc);
294983825b71SWarner Losh 	XL_SEL_WIN(4);
295083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
295183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
295283825b71SWarner Losh 
295383825b71SWarner Losh 	/*
295483825b71SWarner Losh 	 * Enable interrupts.
295583825b71SWarner Losh 	 */
295683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
295783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
295883825b71SWarner Losh #ifdef DEVICE_POLLING
295983825b71SWarner Losh 	/* Disable interrupts if we are polling. */
296083825b71SWarner Losh 	if (ifp->if_capenable & IFCAP_POLLING)
296183825b71SWarner Losh 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
296283825b71SWarner Losh 	else
296383825b71SWarner Losh #endif
296483825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
296583825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
296683825b71SWarner Losh 	    bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
296783825b71SWarner Losh 
296883825b71SWarner Losh 	/* Set the RX early threshold */
296983825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
29700b96ba12SPyun YongHyeon 	CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY);
297183825b71SWarner Losh 
297283825b71SWarner Losh 	/* Enable receiver and transmitter. */
297383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
297483825b71SWarner Losh 	xl_wait(sc);
297583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
297683825b71SWarner Losh 	xl_wait(sc);
297783825b71SWarner Losh 
297883825b71SWarner Losh 	/* XXX Downcall to miibus. */
297983825b71SWarner Losh 	if (mii != NULL)
298083825b71SWarner Losh 		mii_mediachg(mii);
298183825b71SWarner Losh 
298283825b71SWarner Losh 	/* Select window 7 for normal operations. */
298383825b71SWarner Losh 	XL_SEL_WIN(7);
298483825b71SWarner Losh 
298583825b71SWarner Losh 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
298683825b71SWarner Losh 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
298783825b71SWarner Losh 
298883825b71SWarner Losh 	sc->xl_wdog_timer = 0;
298948dcbc33SPyun YongHyeon 	callout_reset(&sc->xl_tick_callout, hz, xl_tick, sc);
299083825b71SWarner Losh }
299183825b71SWarner Losh 
299283825b71SWarner Losh /*
299383825b71SWarner Losh  * Set media options.
299483825b71SWarner Losh  */
299583825b71SWarner Losh static int
299683825b71SWarner Losh xl_ifmedia_upd(struct ifnet *ifp)
299783825b71SWarner Losh {
299883825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
299983825b71SWarner Losh 	struct ifmedia		*ifm = NULL;
300083825b71SWarner Losh 	struct mii_data		*mii = NULL;
300183825b71SWarner Losh 
300283825b71SWarner Losh 	XL_LOCK(sc);
300383825b71SWarner Losh 
300483825b71SWarner Losh 	if (sc->xl_miibus != NULL)
300583825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
300683825b71SWarner Losh 	if (mii == NULL)
300783825b71SWarner Losh 		ifm = &sc->ifmedia;
300883825b71SWarner Losh 	else
300983825b71SWarner Losh 		ifm = &mii->mii_media;
301083825b71SWarner Losh 
301183825b71SWarner Losh 	switch (IFM_SUBTYPE(ifm->ifm_media)) {
301283825b71SWarner Losh 	case IFM_100_FX:
301383825b71SWarner Losh 	case IFM_10_FL:
301483825b71SWarner Losh 	case IFM_10_2:
301583825b71SWarner Losh 	case IFM_10_5:
301683825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
301783825b71SWarner Losh 		XL_UNLOCK(sc);
301883825b71SWarner Losh 		return (0);
301983825b71SWarner Losh 	}
302083825b71SWarner Losh 
302183825b71SWarner Losh 	if (sc->xl_media & XL_MEDIAOPT_MII ||
302283825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BTX ||
302383825b71SWarner Losh 	    sc->xl_media & XL_MEDIAOPT_BT4) {
302427b031a9SPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
302583825b71SWarner Losh 		xl_init_locked(sc);
302683825b71SWarner Losh 	} else {
302783825b71SWarner Losh 		xl_setmode(sc, ifm->ifm_media);
302883825b71SWarner Losh 	}
302983825b71SWarner Losh 
303083825b71SWarner Losh 	XL_UNLOCK(sc);
303183825b71SWarner Losh 
303283825b71SWarner Losh 	return (0);
303383825b71SWarner Losh }
303483825b71SWarner Losh 
303583825b71SWarner Losh /*
303683825b71SWarner Losh  * Report current media status.
303783825b71SWarner Losh  */
303883825b71SWarner Losh static void
303983825b71SWarner Losh xl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
304083825b71SWarner Losh {
304183825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
304283825b71SWarner Losh 	u_int32_t		icfg;
304383825b71SWarner Losh 	u_int16_t		status = 0;
304483825b71SWarner Losh 	struct mii_data		*mii = NULL;
304583825b71SWarner Losh 
304683825b71SWarner Losh 	XL_LOCK(sc);
304783825b71SWarner Losh 
304883825b71SWarner Losh 	if (sc->xl_miibus != NULL)
304983825b71SWarner Losh 		mii = device_get_softc(sc->xl_miibus);
305083825b71SWarner Losh 
305183825b71SWarner Losh 	XL_SEL_WIN(4);
305283825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
305383825b71SWarner Losh 
305483825b71SWarner Losh 	XL_SEL_WIN(3);
305583825b71SWarner Losh 	icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK;
305683825b71SWarner Losh 	icfg >>= XL_ICFG_CONNECTOR_BITS;
305783825b71SWarner Losh 
305883825b71SWarner Losh 	ifmr->ifm_active = IFM_ETHER;
305983825b71SWarner Losh 	ifmr->ifm_status = IFM_AVALID;
306083825b71SWarner Losh 
306183825b71SWarner Losh 	if ((status & XL_MEDIASTAT_CARRIER) == 0)
306283825b71SWarner Losh 		ifmr->ifm_status |= IFM_ACTIVE;
306383825b71SWarner Losh 
306483825b71SWarner Losh 	switch (icfg) {
306583825b71SWarner Losh 	case XL_XCVR_10BT:
306683825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_T;
306783825b71SWarner Losh 		if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
306883825b71SWarner Losh 			ifmr->ifm_active |= IFM_FDX;
306983825b71SWarner Losh 		else
307083825b71SWarner Losh 			ifmr->ifm_active |= IFM_HDX;
307183825b71SWarner Losh 		break;
307283825b71SWarner Losh 	case XL_XCVR_AUI:
307383825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B &&
307483825b71SWarner Losh 		    sc->xl_media == XL_MEDIAOPT_10FL) {
307583825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_FL;
307683825b71SWarner Losh 			if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
307783825b71SWarner Losh 				ifmr->ifm_active |= IFM_FDX;
307883825b71SWarner Losh 			else
307983825b71SWarner Losh 				ifmr->ifm_active |= IFM_HDX;
308083825b71SWarner Losh 		} else
308183825b71SWarner Losh 			ifmr->ifm_active = IFM_ETHER|IFM_10_5;
308283825b71SWarner Losh 		break;
308383825b71SWarner Losh 	case XL_XCVR_COAX:
308483825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_10_2;
308583825b71SWarner Losh 		break;
308683825b71SWarner Losh 	/*
308783825b71SWarner Losh 	 * XXX MII and BTX/AUTO should be separate cases.
308883825b71SWarner Losh 	 */
308983825b71SWarner Losh 
309083825b71SWarner Losh 	case XL_XCVR_100BTX:
309183825b71SWarner Losh 	case XL_XCVR_AUTO:
309283825b71SWarner Losh 	case XL_XCVR_MII:
309383825b71SWarner Losh 		if (mii != NULL) {
309483825b71SWarner Losh 			mii_pollstat(mii);
309583825b71SWarner Losh 			ifmr->ifm_active = mii->mii_media_active;
309683825b71SWarner Losh 			ifmr->ifm_status = mii->mii_media_status;
309783825b71SWarner Losh 		}
309883825b71SWarner Losh 		break;
309983825b71SWarner Losh 	case XL_XCVR_100BFX:
310083825b71SWarner Losh 		ifmr->ifm_active = IFM_ETHER|IFM_100_FX;
310183825b71SWarner Losh 		break;
310283825b71SWarner Losh 	default:
310383825b71SWarner Losh 		if_printf(ifp, "unknown XCVR type: %d\n", icfg);
310483825b71SWarner Losh 		break;
310583825b71SWarner Losh 	}
310683825b71SWarner Losh 
310783825b71SWarner Losh 	XL_UNLOCK(sc);
310883825b71SWarner Losh }
310983825b71SWarner Losh 
311083825b71SWarner Losh static int
311183825b71SWarner Losh xl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
311283825b71SWarner Losh {
311383825b71SWarner Losh 	struct xl_softc		*sc = ifp->if_softc;
311483825b71SWarner Losh 	struct ifreq		*ifr = (struct ifreq *) data;
3115a3835274SPyun YongHyeon 	int			error = 0, mask;
311683825b71SWarner Losh 	struct mii_data		*mii = NULL;
311783825b71SWarner Losh 
311883825b71SWarner Losh 	switch (command) {
311983825b71SWarner Losh 	case SIOCSIFFLAGS:
312083825b71SWarner Losh 		XL_LOCK(sc);
312183825b71SWarner Losh 		if (ifp->if_flags & IFF_UP) {
312283825b71SWarner Losh 			if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
3123dd0a7688SPyun YongHyeon 			    (ifp->if_flags ^ sc->xl_if_flags) &
3124dd0a7688SPyun YongHyeon 			    (IFF_PROMISC | IFF_ALLMULTI))
3125dd0a7688SPyun YongHyeon 				xl_rxfilter(sc);
3126dd0a7688SPyun YongHyeon 			else
312783825b71SWarner Losh 				xl_init_locked(sc);
312883825b71SWarner Losh 		} else {
312983825b71SWarner Losh 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
313083825b71SWarner Losh 				xl_stop(sc);
313183825b71SWarner Losh 		}
313283825b71SWarner Losh 		sc->xl_if_flags = ifp->if_flags;
313383825b71SWarner Losh 		XL_UNLOCK(sc);
313483825b71SWarner Losh 		break;
313583825b71SWarner Losh 	case SIOCADDMULTI:
313683825b71SWarner Losh 	case SIOCDELMULTI:
313783825b71SWarner Losh 		/* XXX Downcall from if_addmulti() possibly with locks held. */
313883825b71SWarner Losh 		XL_LOCK(sc);
3139dd0a7688SPyun YongHyeon 		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
3140dd0a7688SPyun YongHyeon 			xl_rxfilter(sc);
314183825b71SWarner Losh 		XL_UNLOCK(sc);
314283825b71SWarner Losh 		break;
314383825b71SWarner Losh 	case SIOCGIFMEDIA:
314483825b71SWarner Losh 	case SIOCSIFMEDIA:
314583825b71SWarner Losh 		if (sc->xl_miibus != NULL)
314683825b71SWarner Losh 			mii = device_get_softc(sc->xl_miibus);
314783825b71SWarner Losh 		if (mii == NULL)
314883825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
314983825b71SWarner Losh 			    &sc->ifmedia, command);
315083825b71SWarner Losh 		else
315183825b71SWarner Losh 			error = ifmedia_ioctl(ifp, ifr,
315283825b71SWarner Losh 			    &mii->mii_media, command);
315383825b71SWarner Losh 		break;
315483825b71SWarner Losh 	case SIOCSIFCAP:
3155a3835274SPyun YongHyeon 		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
315683825b71SWarner Losh #ifdef DEVICE_POLLING
3157a3835274SPyun YongHyeon 		if ((mask & IFCAP_POLLING) != 0 &&
3158a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_POLLING) != 0) {
3159a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_POLLING;
3160a3835274SPyun YongHyeon 			if ((ifp->if_capenable & IFCAP_POLLING) != 0) {
316183825b71SWarner Losh 				error = ether_poll_register(xl_poll, ifp);
316283825b71SWarner Losh 				if (error)
3163a3835274SPyun YongHyeon 					break;
316483825b71SWarner Losh 				XL_LOCK(sc);
316583825b71SWarner Losh 				/* Disable interrupts */
316683825b71SWarner Losh 				CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
316783825b71SWarner Losh 				ifp->if_capenable |= IFCAP_POLLING;
316883825b71SWarner Losh 				XL_UNLOCK(sc);
3169a3835274SPyun YongHyeon 			} else {
317083825b71SWarner Losh 				error = ether_poll_deregister(ifp);
317183825b71SWarner Losh 				/* Enable interrupts. */
317283825b71SWarner Losh 				XL_LOCK(sc);
3173a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3174a3835274SPyun YongHyeon 				    XL_CMD_INTR_ACK | 0xFF);
3175a3835274SPyun YongHyeon 				CSR_WRITE_2(sc, XL_COMMAND,
3176a3835274SPyun YongHyeon 				    XL_CMD_INTR_ENB | XL_INTRS);
317783825b71SWarner Losh 				if (sc->xl_flags & XL_FLAG_FUNCREG)
3178a3835274SPyun YongHyeon 					bus_space_write_4(sc->xl_ftag,
3179a3835274SPyun YongHyeon 					    sc->xl_fhandle, 4, 0x8000);
318083825b71SWarner Losh 				XL_UNLOCK(sc);
3181a3835274SPyun YongHyeon 			}
318283825b71SWarner Losh 		}
318383825b71SWarner Losh #endif /* DEVICE_POLLING */
318483825b71SWarner Losh 		XL_LOCK(sc);
3185a3835274SPyun YongHyeon 		if ((mask & IFCAP_TXCSUM) != 0 &&
3186a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
3187a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_TXCSUM;
3188a3835274SPyun YongHyeon 			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
3189a3835274SPyun YongHyeon 				ifp->if_hwassist |= XL905B_CSUM_FEATURES;
319083825b71SWarner Losh 			else
3191a3835274SPyun YongHyeon 				ifp->if_hwassist &= ~XL905B_CSUM_FEATURES;
3192a3835274SPyun YongHyeon 		}
3193a3835274SPyun YongHyeon 		if ((mask & IFCAP_RXCSUM) != 0 &&
3194a3835274SPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0)
3195a3835274SPyun YongHyeon 			ifp->if_capenable ^= IFCAP_RXCSUM;
31969ae11bbaSPyun YongHyeon 		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
31979ae11bbaSPyun YongHyeon 		    (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0)
31989ae11bbaSPyun YongHyeon 			ifp->if_capenable ^= IFCAP_WOL_MAGIC;
319983825b71SWarner Losh 		XL_UNLOCK(sc);
320083825b71SWarner Losh 		break;
320183825b71SWarner Losh 	default:
320283825b71SWarner Losh 		error = ether_ioctl(ifp, command, data);
320383825b71SWarner Losh 		break;
320483825b71SWarner Losh 	}
320583825b71SWarner Losh 
320683825b71SWarner Losh 	return (error);
320783825b71SWarner Losh }
320883825b71SWarner Losh 
320983825b71SWarner Losh static int
321083825b71SWarner Losh xl_watchdog(struct xl_softc *sc)
321183825b71SWarner Losh {
321283825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
321383825b71SWarner Losh 	u_int16_t		status = 0;
32142b574f31SPyun YongHyeon 	int			misintr;
321583825b71SWarner Losh 
321683825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
321783825b71SWarner Losh 
321883825b71SWarner Losh 	if (sc->xl_wdog_timer == 0 || --sc->xl_wdog_timer != 0)
321983825b71SWarner Losh 		return (0);
322083825b71SWarner Losh 
32212b574f31SPyun YongHyeon 	xl_rxeof(sc);
32222b574f31SPyun YongHyeon 	xl_txeoc(sc);
32232b574f31SPyun YongHyeon 	misintr = 0;
32242b574f31SPyun YongHyeon 	if (sc->xl_type == XL_TYPE_905B) {
32252b574f31SPyun YongHyeon 		xl_txeof_90xB(sc);
32262b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_cnt == 0)
32272b574f31SPyun YongHyeon 			misintr++;
32282b574f31SPyun YongHyeon 	} else {
32292b574f31SPyun YongHyeon 		xl_txeof(sc);
32302b574f31SPyun YongHyeon 		if (sc->xl_cdata.xl_tx_head == NULL)
32312b574f31SPyun YongHyeon 			misintr++;
32322b574f31SPyun YongHyeon 	}
32332b574f31SPyun YongHyeon 	if (misintr != 0) {
32342b574f31SPyun YongHyeon 		device_printf(sc->xl_dev,
32352b574f31SPyun YongHyeon 		    "watchdog timeout (missed Tx interrupts) -- recovering\n");
32362b574f31SPyun YongHyeon 		return (0);
32372b574f31SPyun YongHyeon 	}
32382b574f31SPyun YongHyeon 
323983825b71SWarner Losh 	ifp->if_oerrors++;
324083825b71SWarner Losh 	XL_SEL_WIN(4);
324183825b71SWarner Losh 	status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
324283825b71SWarner Losh 	device_printf(sc->xl_dev, "watchdog timeout\n");
324383825b71SWarner Losh 
324483825b71SWarner Losh 	if (status & XL_MEDIASTAT_CARRIER)
324583825b71SWarner Losh 		device_printf(sc->xl_dev,
324683825b71SWarner Losh 		    "no carrier - transceiver cable problem?\n");
324783825b71SWarner Losh 
324827b031a9SPyun YongHyeon 	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
324983825b71SWarner Losh 	xl_init_locked(sc);
325083825b71SWarner Losh 
325183825b71SWarner Losh 	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
325283825b71SWarner Losh 		if (sc->xl_type == XL_TYPE_905B)
325383825b71SWarner Losh 			xl_start_90xB_locked(ifp);
325483825b71SWarner Losh 		else
325583825b71SWarner Losh 			xl_start_locked(ifp);
325683825b71SWarner Losh 	}
325783825b71SWarner Losh 
325883825b71SWarner Losh 	return (EJUSTRETURN);
325983825b71SWarner Losh }
326083825b71SWarner Losh 
326183825b71SWarner Losh /*
326283825b71SWarner Losh  * Stop the adapter and free any mbufs allocated to the
326383825b71SWarner Losh  * RX and TX lists.
326483825b71SWarner Losh  */
326583825b71SWarner Losh static void
326683825b71SWarner Losh xl_stop(struct xl_softc *sc)
326783825b71SWarner Losh {
326883825b71SWarner Losh 	register int		i;
326983825b71SWarner Losh 	struct ifnet		*ifp = sc->xl_ifp;
327083825b71SWarner Losh 
327183825b71SWarner Losh 	XL_LOCK_ASSERT(sc);
327283825b71SWarner Losh 
327383825b71SWarner Losh 	sc->xl_wdog_timer = 0;
327483825b71SWarner Losh 
327583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
327683825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
327783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
327883825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
327983825b71SWarner Losh 	xl_wait(sc);
328083825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
328183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
328283825b71SWarner Losh 	DELAY(800);
328383825b71SWarner Losh 
328483825b71SWarner Losh #ifdef foo
328583825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
328683825b71SWarner Losh 	xl_wait(sc);
328783825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
328883825b71SWarner Losh 	xl_wait(sc);
328983825b71SWarner Losh #endif
329083825b71SWarner Losh 
329183825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
329283825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
329383825b71SWarner Losh 	CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
329483825b71SWarner Losh 	if (sc->xl_flags & XL_FLAG_FUNCREG)
329583825b71SWarner Losh 		bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
329683825b71SWarner Losh 
329783825b71SWarner Losh 	/* Stop the stats updater. */
329848dcbc33SPyun YongHyeon 	callout_stop(&sc->xl_tick_callout);
329983825b71SWarner Losh 
330083825b71SWarner Losh 	/*
330183825b71SWarner Losh 	 * Free data in the RX lists.
330283825b71SWarner Losh 	 */
330383825b71SWarner Losh 	for (i = 0; i < XL_RX_LIST_CNT; i++) {
330483825b71SWarner Losh 		if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) {
330583825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
330683825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
330783825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
330883825b71SWarner Losh 			    sc->xl_cdata.xl_rx_chain[i].xl_map);
330983825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf);
331083825b71SWarner Losh 			sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL;
331183825b71SWarner Losh 		}
331283825b71SWarner Losh 	}
331383825b71SWarner Losh 	if (sc->xl_ldata.xl_rx_list != NULL)
331483825b71SWarner Losh 		bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
331583825b71SWarner Losh 	/*
331683825b71SWarner Losh 	 * Free the TX list buffers.
331783825b71SWarner Losh 	 */
331883825b71SWarner Losh 	for (i = 0; i < XL_TX_LIST_CNT; i++) {
331983825b71SWarner Losh 		if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) {
332083825b71SWarner Losh 			bus_dmamap_unload(sc->xl_mtag,
332183825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
332283825b71SWarner Losh 			bus_dmamap_destroy(sc->xl_mtag,
332383825b71SWarner Losh 			    sc->xl_cdata.xl_tx_chain[i].xl_map);
332483825b71SWarner Losh 			m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf);
332583825b71SWarner Losh 			sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL;
332683825b71SWarner Losh 		}
332783825b71SWarner Losh 	}
332883825b71SWarner Losh 	if (sc->xl_ldata.xl_tx_list != NULL)
332983825b71SWarner Losh 		bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
333083825b71SWarner Losh 
333183825b71SWarner Losh 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
333283825b71SWarner Losh }
333383825b71SWarner Losh 
333483825b71SWarner Losh /*
333583825b71SWarner Losh  * Stop all chip I/O so that the kernel's probe routines don't
333683825b71SWarner Losh  * get confused by errant DMAs when rebooting.
333783825b71SWarner Losh  */
333883825b71SWarner Losh static int
333983825b71SWarner Losh xl_shutdown(device_t dev)
334083825b71SWarner Losh {
334183825b71SWarner Losh 
33429ae11bbaSPyun YongHyeon 	return (xl_suspend(dev));
334383825b71SWarner Losh }
334483825b71SWarner Losh 
334583825b71SWarner Losh static int
334683825b71SWarner Losh xl_suspend(device_t dev)
334783825b71SWarner Losh {
334883825b71SWarner Losh 	struct xl_softc		*sc;
334983825b71SWarner Losh 
335083825b71SWarner Losh 	sc = device_get_softc(dev);
335183825b71SWarner Losh 
335283825b71SWarner Losh 	XL_LOCK(sc);
335383825b71SWarner Losh 	xl_stop(sc);
33549ae11bbaSPyun YongHyeon 	xl_setwol(sc);
335583825b71SWarner Losh 	XL_UNLOCK(sc);
335683825b71SWarner Losh 
335783825b71SWarner Losh 	return (0);
335883825b71SWarner Losh }
335983825b71SWarner Losh 
336083825b71SWarner Losh static int
336183825b71SWarner Losh xl_resume(device_t dev)
336283825b71SWarner Losh {
336383825b71SWarner Losh 	struct xl_softc		*sc;
336483825b71SWarner Losh 	struct ifnet		*ifp;
336583825b71SWarner Losh 
336683825b71SWarner Losh 	sc = device_get_softc(dev);
336783825b71SWarner Losh 	ifp = sc->xl_ifp;
336883825b71SWarner Losh 
336983825b71SWarner Losh 	XL_LOCK(sc);
337083825b71SWarner Losh 
337127b031a9SPyun YongHyeon 	if (ifp->if_flags & IFF_UP) {
337227b031a9SPyun YongHyeon 		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
337383825b71SWarner Losh 		xl_init_locked(sc);
337427b031a9SPyun YongHyeon 	}
337583825b71SWarner Losh 
337683825b71SWarner Losh 	XL_UNLOCK(sc);
337783825b71SWarner Losh 
337883825b71SWarner Losh 	return (0);
337983825b71SWarner Losh }
33809ae11bbaSPyun YongHyeon 
33819ae11bbaSPyun YongHyeon static void
33829ae11bbaSPyun YongHyeon xl_setwol(struct xl_softc *sc)
33839ae11bbaSPyun YongHyeon {
33849ae11bbaSPyun YongHyeon 	struct ifnet		*ifp;
33859ae11bbaSPyun YongHyeon 	u_int16_t		cfg, pmstat;
33869ae11bbaSPyun YongHyeon 
33879ae11bbaSPyun YongHyeon 	if ((sc->xl_flags & XL_FLAG_WOL) == 0)
33889ae11bbaSPyun YongHyeon 		return;
33899ae11bbaSPyun YongHyeon 
33909ae11bbaSPyun YongHyeon 	ifp = sc->xl_ifp;
33919ae11bbaSPyun YongHyeon 	XL_SEL_WIN(7);
33929ae11bbaSPyun YongHyeon 	/* Clear any pending PME events. */
33939ae11bbaSPyun YongHyeon 	CSR_READ_2(sc, XL_W7_BM_PME);
33949ae11bbaSPyun YongHyeon 	cfg = 0;
33959ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
33969ae11bbaSPyun YongHyeon 		cfg |= XL_BM_PME_MAGIC;
33979ae11bbaSPyun YongHyeon 	CSR_WRITE_2(sc, XL_W7_BM_PME, cfg);
33989ae11bbaSPyun YongHyeon 	/* Enable RX. */
33999ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
34009ae11bbaSPyun YongHyeon 		CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
34019ae11bbaSPyun YongHyeon 	/* Request PME. */
34029ae11bbaSPyun YongHyeon 	pmstat = pci_read_config(sc->xl_dev,
34039ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, 2);
34049ae11bbaSPyun YongHyeon 	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
34059ae11bbaSPyun YongHyeon 		pmstat |= PCIM_PSTAT_PMEENABLE;
34069ae11bbaSPyun YongHyeon 	else
34079ae11bbaSPyun YongHyeon 		pmstat &= ~PCIM_PSTAT_PMEENABLE;
34089ae11bbaSPyun YongHyeon 	pci_write_config(sc->xl_dev,
34099ae11bbaSPyun YongHyeon 	    sc->xl_pmcap + PCIR_POWER_STATUS, pmstat, 2);
34109ae11bbaSPyun YongHyeon }
3411