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