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