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