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