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