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