1c8befdd5SWarner Losh /*- 2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause 3df57947fSPedro F. Giffuni * 4c8befdd5SWarner Losh * Copyright (c) 1997, 1998, 1999 5c8befdd5SWarner Losh * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 6c8befdd5SWarner Losh * 7c8befdd5SWarner Losh * Redistribution and use in source and binary forms, with or without 8c8befdd5SWarner Losh * modification, are permitted provided that the following conditions 9c8befdd5SWarner Losh * are met: 10c8befdd5SWarner Losh * 1. Redistributions of source code must retain the above copyright 11c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer. 12c8befdd5SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 13c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer in the 14c8befdd5SWarner Losh * documentation and/or other materials provided with the distribution. 15c8befdd5SWarner Losh * 3. All advertising materials mentioning features or use of this software 16c8befdd5SWarner Losh * must display the following acknowledgement: 17c8befdd5SWarner Losh * This product includes software developed by Bill Paul. 18c8befdd5SWarner Losh * 4. Neither the name of the author nor the names of any co-contributors 19c8befdd5SWarner Losh * may be used to endorse or promote products derived from this software 20c8befdd5SWarner Losh * without specific prior written permission. 21c8befdd5SWarner Losh * 22c8befdd5SWarner Losh * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 23c8befdd5SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24c8befdd5SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25c8befdd5SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 26c8befdd5SWarner Losh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27c8befdd5SWarner Losh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28c8befdd5SWarner Losh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29c8befdd5SWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30c8befdd5SWarner Losh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31c8befdd5SWarner Losh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32c8befdd5SWarner Losh * THE POSSIBILITY OF SUCH DAMAGE. 33c8befdd5SWarner Losh */ 34c8befdd5SWarner Losh 35c8befdd5SWarner Losh #include <sys/cdefs.h> 36c8befdd5SWarner Losh __FBSDID("$FreeBSD$"); 37c8befdd5SWarner Losh 38c8befdd5SWarner Losh #ifdef HAVE_KERNEL_OPTION_HEADERS 39c8befdd5SWarner Losh #include "opt_device_polling.h" 40c8befdd5SWarner Losh #endif 41c8befdd5SWarner Losh 42c8befdd5SWarner Losh #include <sys/param.h> 43c8befdd5SWarner Losh #include <sys/systm.h> 44a1b2c209SPyun YongHyeon #include <sys/bus.h> 45a1b2c209SPyun YongHyeon #include <sys/endian.h> 46c8befdd5SWarner Losh #include <sys/kernel.h> 47a1b2c209SPyun YongHyeon #include <sys/lock.h> 48a1b2c209SPyun YongHyeon #include <sys/malloc.h> 49a1b2c209SPyun YongHyeon #include <sys/mbuf.h> 50c8befdd5SWarner Losh #include <sys/module.h> 51a1b2c209SPyun YongHyeon #include <sys/rman.h> 52c8befdd5SWarner Losh #include <sys/socket.h> 53a1b2c209SPyun YongHyeon #include <sys/sockio.h> 54c8befdd5SWarner Losh #include <sys/sysctl.h> 55c8befdd5SWarner Losh 56a1b2c209SPyun YongHyeon #include <net/bpf.h> 57c8befdd5SWarner Losh #include <net/if.h> 5876039bc8SGleb Smirnoff #include <net/if_var.h> 59c8befdd5SWarner Losh #include <net/if_arp.h> 60c8befdd5SWarner Losh #include <net/ethernet.h> 61c8befdd5SWarner Losh #include <net/if_dl.h> 62c8befdd5SWarner Losh #include <net/if_media.h> 63c8befdd5SWarner Losh #include <net/if_types.h> 64c8befdd5SWarner Losh #include <net/if_vlan_var.h> 65c8befdd5SWarner Losh 66c8befdd5SWarner Losh #include <machine/bus.h> 67c8befdd5SWarner Losh #include <machine/resource.h> 68c8befdd5SWarner Losh 69c8befdd5SWarner Losh #include <dev/mii/mii.h> 708c1093fcSMarius Strobl #include <dev/mii/mii_bitbang.h> 71c8befdd5SWarner Losh #include <dev/mii/miivar.h> 72c8befdd5SWarner Losh 73c8befdd5SWarner Losh #include <dev/pci/pcireg.h> 74c8befdd5SWarner Losh #include <dev/pci/pcivar.h> 75c8befdd5SWarner Losh 76a1b2c209SPyun YongHyeon #include <dev/ste/if_stereg.h> 77a1b2c209SPyun YongHyeon 78c8befdd5SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 79c8befdd5SWarner Losh #include "miibus_if.h" 80c8befdd5SWarner Losh 81c8befdd5SWarner Losh MODULE_DEPEND(ste, pci, 1, 1, 1); 82c8befdd5SWarner Losh MODULE_DEPEND(ste, ether, 1, 1, 1); 83c8befdd5SWarner Losh MODULE_DEPEND(ste, miibus, 1, 1, 1); 84c8befdd5SWarner Losh 8581598b3eSPyun YongHyeon /* Define to show Tx error status. */ 8681598b3eSPyun YongHyeon #define STE_SHOW_TXERRORS 8781598b3eSPyun YongHyeon 88c8befdd5SWarner Losh /* 89c8befdd5SWarner Losh * Various supported device vendors/types and their names. 90c8befdd5SWarner Losh */ 9129658c96SDimitry Andric static const struct ste_type ste_devs[] = { 92c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_1, "Sundance ST201 10/100BaseTX" }, 93c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_2, "Sundance ST201 10/100BaseTX" }, 94c8befdd5SWarner Losh { DL_VENDORID, DL_DEVICEID_DL10050, "D-Link DL10050 10/100BaseTX" }, 95c8befdd5SWarner Losh { 0, 0, NULL } 96c8befdd5SWarner Losh }; 97c8befdd5SWarner Losh 98c8befdd5SWarner Losh static int ste_attach(device_t); 99c8befdd5SWarner Losh static int ste_detach(device_t); 100084dc54bSPyun YongHyeon static int ste_probe(device_t); 101b4c170e1SPyun YongHyeon static int ste_resume(device_t); 102c8befdd5SWarner Losh static int ste_shutdown(device_t); 103b4c170e1SPyun YongHyeon static int ste_suspend(device_t); 104084dc54bSPyun YongHyeon 105a1b2c209SPyun YongHyeon static int ste_dma_alloc(struct ste_softc *); 106a1b2c209SPyun YongHyeon static void ste_dma_free(struct ste_softc *); 107a1b2c209SPyun YongHyeon static void ste_dmamap_cb(void *, bus_dma_segment_t *, int, int); 108084dc54bSPyun YongHyeon static int ste_eeprom_wait(struct ste_softc *); 109a1b2c209SPyun YongHyeon static int ste_encap(struct ste_softc *, struct mbuf **, 110a1b2c209SPyun YongHyeon struct ste_chain *); 111c8befdd5SWarner Losh static int ste_ifmedia_upd(struct ifnet *); 112c8befdd5SWarner Losh static void ste_ifmedia_sts(struct ifnet *, struct ifmediareq *); 113084dc54bSPyun YongHyeon static void ste_init(void *); 114084dc54bSPyun YongHyeon static void ste_init_locked(struct ste_softc *); 115c8befdd5SWarner Losh static int ste_init_rx_list(struct ste_softc *); 116c8befdd5SWarner Losh static void ste_init_tx_list(struct ste_softc *); 117084dc54bSPyun YongHyeon static void ste_intr(void *); 118084dc54bSPyun YongHyeon static int ste_ioctl(struct ifnet *, u_long, caddr_t); 1198c1093fcSMarius Strobl static uint32_t ste_mii_bitbang_read(device_t); 1208c1093fcSMarius Strobl static void ste_mii_bitbang_write(device_t, uint32_t); 121084dc54bSPyun YongHyeon static int ste_miibus_readreg(device_t, int, int); 122084dc54bSPyun YongHyeon static void ste_miibus_statchg(device_t); 123084dc54bSPyun YongHyeon static int ste_miibus_writereg(device_t, int, int, int); 124a1b2c209SPyun YongHyeon static int ste_newbuf(struct ste_softc *, struct ste_chain_onefrag *); 125fcd8385eSPyun YongHyeon static int ste_read_eeprom(struct ste_softc *, uint16_t *, int, int); 126084dc54bSPyun YongHyeon static void ste_reset(struct ste_softc *); 12781598b3eSPyun YongHyeon static void ste_restart_tx(struct ste_softc *); 128a1b2c209SPyun YongHyeon static int ste_rxeof(struct ste_softc *, int); 129931ec15aSPyun YongHyeon static void ste_rxfilter(struct ste_softc *); 130b4c170e1SPyun YongHyeon static void ste_setwol(struct ste_softc *); 131084dc54bSPyun YongHyeon static void ste_start(struct ifnet *); 132084dc54bSPyun YongHyeon static void ste_start_locked(struct ifnet *); 1338657caa6SPyun YongHyeon static void ste_stats_clear(struct ste_softc *); 13410f695eeSPyun YongHyeon static void ste_stats_update(struct ste_softc *); 135084dc54bSPyun YongHyeon static void ste_stop(struct ste_softc *); 1368657caa6SPyun YongHyeon static void ste_sysctl_node(struct ste_softc *); 13710f695eeSPyun YongHyeon static void ste_tick(void *); 138084dc54bSPyun YongHyeon static void ste_txeoc(struct ste_softc *); 139084dc54bSPyun YongHyeon static void ste_txeof(struct ste_softc *); 140084dc54bSPyun YongHyeon static void ste_wait(struct ste_softc *); 141084dc54bSPyun YongHyeon static void ste_watchdog(struct ste_softc *); 142c8befdd5SWarner Losh 1438c1093fcSMarius Strobl /* 1448c1093fcSMarius Strobl * MII bit-bang glue 1458c1093fcSMarius Strobl */ 1468c1093fcSMarius Strobl static const struct mii_bitbang_ops ste_mii_bitbang_ops = { 1478c1093fcSMarius Strobl ste_mii_bitbang_read, 1488c1093fcSMarius Strobl ste_mii_bitbang_write, 1498c1093fcSMarius Strobl { 1508c1093fcSMarius Strobl STE_PHYCTL_MDATA, /* MII_BIT_MDO */ 1518c1093fcSMarius Strobl STE_PHYCTL_MDATA, /* MII_BIT_MDI */ 1528c1093fcSMarius Strobl STE_PHYCTL_MCLK, /* MII_BIT_MDC */ 1538c1093fcSMarius Strobl STE_PHYCTL_MDIR, /* MII_BIT_DIR_HOST_PHY */ 1548c1093fcSMarius Strobl 0, /* MII_BIT_DIR_PHY_HOST */ 1558c1093fcSMarius Strobl } 1568c1093fcSMarius Strobl }; 1578c1093fcSMarius Strobl 158c8befdd5SWarner Losh static device_method_t ste_methods[] = { 159c8befdd5SWarner Losh /* Device interface */ 160c8befdd5SWarner Losh DEVMETHOD(device_probe, ste_probe), 161c8befdd5SWarner Losh DEVMETHOD(device_attach, ste_attach), 162c8befdd5SWarner Losh DEVMETHOD(device_detach, ste_detach), 163c8befdd5SWarner Losh DEVMETHOD(device_shutdown, ste_shutdown), 164b4c170e1SPyun YongHyeon DEVMETHOD(device_suspend, ste_suspend), 165b4c170e1SPyun YongHyeon DEVMETHOD(device_resume, ste_resume), 166c8befdd5SWarner Losh 167c8befdd5SWarner Losh /* MII interface */ 168c8befdd5SWarner Losh DEVMETHOD(miibus_readreg, ste_miibus_readreg), 169c8befdd5SWarner Losh DEVMETHOD(miibus_writereg, ste_miibus_writereg), 170c8befdd5SWarner Losh DEVMETHOD(miibus_statchg, ste_miibus_statchg), 171c8befdd5SWarner Losh 1724b7ec270SMarius Strobl DEVMETHOD_END 173c8befdd5SWarner Losh }; 174c8befdd5SWarner Losh 175c8befdd5SWarner Losh static driver_t ste_driver = { 176c8befdd5SWarner Losh "ste", 177c8befdd5SWarner Losh ste_methods, 178c8befdd5SWarner Losh sizeof(struct ste_softc) 179c8befdd5SWarner Losh }; 180c8befdd5SWarner Losh 181c8befdd5SWarner Losh static devclass_t ste_devclass; 182c8befdd5SWarner Losh 183c8befdd5SWarner Losh DRIVER_MODULE(ste, pci, ste_driver, ste_devclass, 0, 0); 184c8befdd5SWarner Losh DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); 185c8befdd5SWarner Losh 186c8befdd5SWarner Losh #define STE_SETBIT4(sc, reg, x) \ 187c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) 188c8befdd5SWarner Losh 189c8befdd5SWarner Losh #define STE_CLRBIT4(sc, reg, x) \ 190c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) 191c8befdd5SWarner Losh 192c8befdd5SWarner Losh #define STE_SETBIT2(sc, reg, x) \ 193c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x)) 194c8befdd5SWarner Losh 195c8befdd5SWarner Losh #define STE_CLRBIT2(sc, reg, x) \ 196c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x)) 197c8befdd5SWarner Losh 198c8befdd5SWarner Losh #define STE_SETBIT1(sc, reg, x) \ 199c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) 200c8befdd5SWarner Losh 201c8befdd5SWarner Losh #define STE_CLRBIT1(sc, reg, x) \ 202c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x)) 203c8befdd5SWarner Losh 2048c1093fcSMarius Strobl /* 2058c1093fcSMarius Strobl * Read the MII serial port for the MII bit-bang module. 2068c1093fcSMarius Strobl */ 2078c1093fcSMarius Strobl static uint32_t 2088c1093fcSMarius Strobl ste_mii_bitbang_read(device_t dev) 2098c1093fcSMarius Strobl { 2108c1093fcSMarius Strobl struct ste_softc *sc; 2118c1093fcSMarius Strobl uint32_t val; 212c8befdd5SWarner Losh 2138c1093fcSMarius Strobl sc = device_get_softc(dev); 2148c1093fcSMarius Strobl 2158c1093fcSMarius Strobl val = CSR_READ_1(sc, STE_PHYCTL); 2168c1093fcSMarius Strobl CSR_BARRIER(sc, STE_PHYCTL, 1, 2178c1093fcSMarius Strobl BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 2188c1093fcSMarius Strobl 2198c1093fcSMarius Strobl return (val); 2208c1093fcSMarius Strobl } 221c8befdd5SWarner Losh 222c8befdd5SWarner Losh /* 2238c1093fcSMarius Strobl * Write the MII serial port for the MII bit-bang module. 224c8befdd5SWarner Losh */ 225c8befdd5SWarner Losh static void 2268c1093fcSMarius Strobl ste_mii_bitbang_write(device_t dev, uint32_t val) 227c8befdd5SWarner Losh { 2288c1093fcSMarius Strobl struct ste_softc *sc; 229c8befdd5SWarner Losh 2308c1093fcSMarius Strobl sc = device_get_softc(dev); 231c8befdd5SWarner Losh 2328c1093fcSMarius Strobl CSR_WRITE_1(sc, STE_PHYCTL, val); 2338c1093fcSMarius Strobl CSR_BARRIER(sc, STE_PHYCTL, 1, 2348c1093fcSMarius Strobl BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 235c8befdd5SWarner Losh } 236c8befdd5SWarner Losh 237c8befdd5SWarner Losh static int 23860270842SPyun YongHyeon ste_miibus_readreg(device_t dev, int phy, int reg) 239c8befdd5SWarner Losh { 240c8befdd5SWarner Losh 2418c1093fcSMarius Strobl return (mii_bitbang_readreg(dev, &ste_mii_bitbang_ops, phy, reg)); 242c8befdd5SWarner Losh } 243c8befdd5SWarner Losh 244c8befdd5SWarner Losh static int 24560270842SPyun YongHyeon ste_miibus_writereg(device_t dev, int phy, int reg, int data) 246c8befdd5SWarner Losh { 247c8befdd5SWarner Losh 2488c1093fcSMarius Strobl mii_bitbang_writereg(dev, &ste_mii_bitbang_ops, phy, reg, data); 249c8befdd5SWarner Losh 250c8befdd5SWarner Losh return (0); 251c8befdd5SWarner Losh } 252c8befdd5SWarner Losh 253c8befdd5SWarner Losh static void 25460270842SPyun YongHyeon ste_miibus_statchg(device_t dev) 255c8befdd5SWarner Losh { 256c8befdd5SWarner Losh struct ste_softc *sc; 257c8befdd5SWarner Losh struct mii_data *mii; 25810f695eeSPyun YongHyeon struct ifnet *ifp; 25910f695eeSPyun YongHyeon uint16_t cfg; 260c8befdd5SWarner Losh 261c8befdd5SWarner Losh sc = device_get_softc(dev); 262c8befdd5SWarner Losh 263c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 26410f695eeSPyun YongHyeon ifp = sc->ste_ifp; 26510f695eeSPyun YongHyeon if (mii == NULL || ifp == NULL || 26610f695eeSPyun YongHyeon (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 26710f695eeSPyun YongHyeon return; 268c8befdd5SWarner Losh 26910f695eeSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK; 27010f695eeSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 27110f695eeSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) { 27210f695eeSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) { 27310f695eeSPyun YongHyeon case IFM_10_T: 27410f695eeSPyun YongHyeon case IFM_100_TX: 27510f695eeSPyun YongHyeon case IFM_100_FX: 27610f695eeSPyun YongHyeon case IFM_100_T4: 27710f695eeSPyun YongHyeon sc->ste_flags |= STE_FLAG_LINK; 27810f695eeSPyun YongHyeon default: 27910f695eeSPyun YongHyeon break; 28010f695eeSPyun YongHyeon } 28110f695eeSPyun YongHyeon } 28210f695eeSPyun YongHyeon 28310f695eeSPyun YongHyeon /* Program MACs with resolved speed/duplex/flow-control. */ 28410f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) != 0) { 28510f695eeSPyun YongHyeon cfg = CSR_READ_2(sc, STE_MACCTL0); 28610f695eeSPyun YongHyeon cfg &= ~(STE_MACCTL0_FLOWCTL_ENABLE | STE_MACCTL0_FULLDUPLEX); 28710f695eeSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 28810f695eeSPyun YongHyeon /* 28910f695eeSPyun YongHyeon * ST201 data sheet says driver should enable receiving 29010f695eeSPyun YongHyeon * MAC control frames bit of receive mode register to 29110f695eeSPyun YongHyeon * receive flow-control frames but the register has no 29210f695eeSPyun YongHyeon * such bits. In addition the controller has no ability 29310f695eeSPyun YongHyeon * to send pause frames so it should be handled in 29410f695eeSPyun YongHyeon * driver. Implementing pause timer handling in driver 29510f695eeSPyun YongHyeon * layer is not trivial, so don't enable flow-control 29610f695eeSPyun YongHyeon * here. 29710f695eeSPyun YongHyeon */ 29810f695eeSPyun YongHyeon cfg |= STE_MACCTL0_FULLDUPLEX; 29910f695eeSPyun YongHyeon } 30010f695eeSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL0, cfg); 301c8befdd5SWarner Losh } 302c8befdd5SWarner Losh } 303c8befdd5SWarner Losh 304c8befdd5SWarner Losh static int 30560270842SPyun YongHyeon ste_ifmedia_upd(struct ifnet *ifp) 306c8befdd5SWarner Losh { 307c8befdd5SWarner Losh struct ste_softc *sc; 308bfe051bdSPyun YongHyeon struct mii_data *mii; 309bfe051bdSPyun YongHyeon struct mii_softc *miisc; 310bfe051bdSPyun YongHyeon int error; 311c8befdd5SWarner Losh 312c8befdd5SWarner Losh sc = ifp->if_softc; 313c8befdd5SWarner Losh STE_LOCK(sc); 314c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 315c8befdd5SWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 3163fcb7a53SMarius Strobl PHY_RESET(miisc); 317bfe051bdSPyun YongHyeon error = mii_mediachg(mii); 318bfe051bdSPyun YongHyeon STE_UNLOCK(sc); 319bfe051bdSPyun YongHyeon 320bfe051bdSPyun YongHyeon return (error); 321c8befdd5SWarner Losh } 322c8befdd5SWarner Losh 323c8befdd5SWarner Losh static void 32460270842SPyun YongHyeon ste_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 325c8befdd5SWarner Losh { 326c8befdd5SWarner Losh struct ste_softc *sc; 327c8befdd5SWarner Losh struct mii_data *mii; 328c8befdd5SWarner Losh 329c8befdd5SWarner Losh sc = ifp->if_softc; 330c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 331c8befdd5SWarner Losh 332c8befdd5SWarner Losh STE_LOCK(sc); 3335b7e3118SPyun YongHyeon if ((ifp->if_flags & IFF_UP) == 0) { 3345b7e3118SPyun YongHyeon STE_UNLOCK(sc); 3355b7e3118SPyun YongHyeon return; 3365b7e3118SPyun YongHyeon } 337c8befdd5SWarner Losh mii_pollstat(mii); 338c8befdd5SWarner Losh ifmr->ifm_active = mii->mii_media_active; 339c8befdd5SWarner Losh ifmr->ifm_status = mii->mii_media_status; 340c8befdd5SWarner Losh STE_UNLOCK(sc); 341c8befdd5SWarner Losh } 342c8befdd5SWarner Losh 343c8befdd5SWarner Losh static void 34460270842SPyun YongHyeon ste_wait(struct ste_softc *sc) 345c8befdd5SWarner Losh { 34642306cb0SPyun YongHyeon int i; 347c8befdd5SWarner Losh 348c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 349c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) 350c8befdd5SWarner Losh break; 3511bf71544SPyun YongHyeon DELAY(1); 352c8befdd5SWarner Losh } 353c8befdd5SWarner Losh 354c8befdd5SWarner Losh if (i == STE_TIMEOUT) 355c8befdd5SWarner Losh device_printf(sc->ste_dev, "command never completed!\n"); 356c8befdd5SWarner Losh } 357c8befdd5SWarner Losh 358c8befdd5SWarner Losh /* 359c8befdd5SWarner Losh * The EEPROM is slow: give it time to come ready after issuing 360c8befdd5SWarner Losh * it a command. 361c8befdd5SWarner Losh */ 362c8befdd5SWarner Losh static int 36360270842SPyun YongHyeon ste_eeprom_wait(struct ste_softc *sc) 364c8befdd5SWarner Losh { 365c8befdd5SWarner Losh int i; 366c8befdd5SWarner Losh 367c8befdd5SWarner Losh DELAY(1000); 368c8befdd5SWarner Losh 369c8befdd5SWarner Losh for (i = 0; i < 100; i++) { 370c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) 371c8befdd5SWarner Losh DELAY(1000); 372c8befdd5SWarner Losh else 373c8befdd5SWarner Losh break; 374c8befdd5SWarner Losh } 375c8befdd5SWarner Losh 376c8befdd5SWarner Losh if (i == 100) { 377c8befdd5SWarner Losh device_printf(sc->ste_dev, "eeprom failed to come ready\n"); 378c8befdd5SWarner Losh return (1); 379c8befdd5SWarner Losh } 380c8befdd5SWarner Losh 381c8befdd5SWarner Losh return (0); 382c8befdd5SWarner Losh } 383c8befdd5SWarner Losh 384c8befdd5SWarner Losh /* 385c8befdd5SWarner Losh * Read a sequence of words from the EEPROM. Note that ethernet address 386c8befdd5SWarner Losh * data is stored in the EEPROM in network byte order. 387c8befdd5SWarner Losh */ 388c8befdd5SWarner Losh static int 389fcd8385eSPyun YongHyeon ste_read_eeprom(struct ste_softc *sc, uint16_t *dest, int off, int cnt) 390c8befdd5SWarner Losh { 391c8befdd5SWarner Losh int err = 0, i; 392c8befdd5SWarner Losh 393c8befdd5SWarner Losh if (ste_eeprom_wait(sc)) 394c8befdd5SWarner Losh return (1); 395c8befdd5SWarner Losh 396c8befdd5SWarner Losh for (i = 0; i < cnt; i++) { 397c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); 398c8befdd5SWarner Losh err = ste_eeprom_wait(sc); 399c8befdd5SWarner Losh if (err) 400c8befdd5SWarner Losh break; 401fcd8385eSPyun YongHyeon *dest = le16toh(CSR_READ_2(sc, STE_EEPROM_DATA)); 402fcd8385eSPyun YongHyeon dest++; 403c8befdd5SWarner Losh } 404c8befdd5SWarner Losh 405c8befdd5SWarner Losh return (err ? 1 : 0); 406c8befdd5SWarner Losh } 407c8befdd5SWarner Losh 408f2921d4cSGleb Smirnoff static u_int 409f2921d4cSGleb Smirnoff ste_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 410f2921d4cSGleb Smirnoff { 411f2921d4cSGleb Smirnoff uint32_t *hashes = arg; 412f2921d4cSGleb Smirnoff int h; 413f2921d4cSGleb Smirnoff 414f2921d4cSGleb Smirnoff h = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) & 0x3F; 415f2921d4cSGleb Smirnoff if (h < 32) 416f2921d4cSGleb Smirnoff hashes[0] |= (1 << h); 417f2921d4cSGleb Smirnoff else 418f2921d4cSGleb Smirnoff hashes[1] |= (1 << (h - 32)); 419f2921d4cSGleb Smirnoff 420f2921d4cSGleb Smirnoff return (1); 421f2921d4cSGleb Smirnoff } 422f2921d4cSGleb Smirnoff 423c8befdd5SWarner Losh static void 424931ec15aSPyun YongHyeon ste_rxfilter(struct ste_softc *sc) 425c8befdd5SWarner Losh { 426c8befdd5SWarner Losh struct ifnet *ifp; 427f2632c3bSPyun YongHyeon uint32_t hashes[2] = { 0, 0 }; 428931ec15aSPyun YongHyeon uint8_t rxcfg; 429c8befdd5SWarner Losh 430931ec15aSPyun YongHyeon STE_LOCK_ASSERT(sc); 431931ec15aSPyun YongHyeon 432c8befdd5SWarner Losh ifp = sc->ste_ifp; 433931ec15aSPyun YongHyeon rxcfg = CSR_READ_1(sc, STE_RX_MODE); 434931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_UNICAST; 435931ec15aSPyun YongHyeon rxcfg &= ~(STE_RXMODE_ALLMULTI | STE_RXMODE_MULTIHASH | 436931ec15aSPyun YongHyeon STE_RXMODE_BROADCAST | STE_RXMODE_PROMISC); 437931ec15aSPyun YongHyeon if (ifp->if_flags & IFF_BROADCAST) 438931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_BROADCAST; 439931ec15aSPyun YongHyeon if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) { 440931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_ALLMULTI) != 0) 441931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_ALLMULTI; 442931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_PROMISC) != 0) 443931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_PROMISC; 444931ec15aSPyun YongHyeon goto chipit; 445c8befdd5SWarner Losh } 446c8befdd5SWarner Losh 447931ec15aSPyun YongHyeon rxcfg |= STE_RXMODE_MULTIHASH; 448931ec15aSPyun YongHyeon /* Now program new ones. */ 449f2921d4cSGleb Smirnoff if_foreach_llmaddr(ifp, ste_hash_maddr, hashes); 450c8befdd5SWarner Losh 451931ec15aSPyun YongHyeon chipit: 452c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); 453c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); 454c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); 455c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); 456931ec15aSPyun YongHyeon CSR_WRITE_1(sc, STE_RX_MODE, rxcfg); 457931ec15aSPyun YongHyeon CSR_READ_1(sc, STE_RX_MODE); 458c8befdd5SWarner Losh } 459c8befdd5SWarner Losh 460c8befdd5SWarner Losh #ifdef DEVICE_POLLING 461c8befdd5SWarner Losh static poll_handler_t ste_poll, ste_poll_locked; 462c8befdd5SWarner Losh 4631abcdbd1SAttilio Rao static int 464c8befdd5SWarner Losh ste_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 465c8befdd5SWarner Losh { 466c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 4671abcdbd1SAttilio Rao int rx_npkts = 0; 468c8befdd5SWarner Losh 469c8befdd5SWarner Losh STE_LOCK(sc); 470c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 4711abcdbd1SAttilio Rao rx_npkts = ste_poll_locked(ifp, cmd, count); 472c8befdd5SWarner Losh STE_UNLOCK(sc); 4731abcdbd1SAttilio Rao return (rx_npkts); 474c8befdd5SWarner Losh } 475c8befdd5SWarner Losh 4761abcdbd1SAttilio Rao static int 477c8befdd5SWarner Losh ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) 478c8befdd5SWarner Losh { 479c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 4801abcdbd1SAttilio Rao int rx_npkts; 481c8befdd5SWarner Losh 482c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 483c8befdd5SWarner Losh 484a1b2c209SPyun YongHyeon rx_npkts = ste_rxeof(sc, count); 485c8befdd5SWarner Losh ste_txeof(sc); 48681598b3eSPyun YongHyeon ste_txeoc(sc); 487c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 488c8befdd5SWarner Losh ste_start_locked(ifp); 489c8befdd5SWarner Losh 490c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) { 49156af54f2SPyun YongHyeon uint16_t status; 492c8befdd5SWarner Losh 493c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 494c8befdd5SWarner Losh 49510f695eeSPyun YongHyeon if (status & STE_ISR_STATS_OFLOW) 496c8befdd5SWarner Losh ste_stats_update(sc); 497c8befdd5SWarner Losh 49855d7003eSPyun YongHyeon if (status & STE_ISR_HOSTERR) { 49955d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 500c8befdd5SWarner Losh ste_init_locked(sc); 501c8befdd5SWarner Losh } 50255d7003eSPyun YongHyeon } 5031abcdbd1SAttilio Rao return (rx_npkts); 504c8befdd5SWarner Losh } 505c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 506c8befdd5SWarner Losh 507c8befdd5SWarner Losh static void 50860270842SPyun YongHyeon ste_intr(void *xsc) 509c8befdd5SWarner Losh { 510c8befdd5SWarner Losh struct ste_softc *sc; 511c8befdd5SWarner Losh struct ifnet *ifp; 512fabbaac5SPyun YongHyeon uint16_t intrs, status; 513c8befdd5SWarner Losh 514c8befdd5SWarner Losh sc = xsc; 515c8befdd5SWarner Losh STE_LOCK(sc); 516c8befdd5SWarner Losh ifp = sc->ste_ifp; 517c8befdd5SWarner Losh 518c8befdd5SWarner Losh #ifdef DEVICE_POLLING 519c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 520c8befdd5SWarner Losh STE_UNLOCK(sc); 521c8befdd5SWarner Losh return; 522c8befdd5SWarner Losh } 523c8befdd5SWarner Losh #endif 524fabbaac5SPyun YongHyeon /* Reading STE_ISR_ACK clears STE_IMR register. */ 525fabbaac5SPyun YongHyeon status = CSR_READ_2(sc, STE_ISR_ACK); 526fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 527c8befdd5SWarner Losh STE_UNLOCK(sc); 528c8befdd5SWarner Losh return; 529c8befdd5SWarner Losh } 530c8befdd5SWarner Losh 531fabbaac5SPyun YongHyeon intrs = STE_INTRS; 532fabbaac5SPyun YongHyeon if (status == 0xFFFF || (status & intrs) == 0) 533fabbaac5SPyun YongHyeon goto done; 534c8befdd5SWarner Losh 535fabbaac5SPyun YongHyeon if (sc->ste_int_rx_act > 0) { 536fabbaac5SPyun YongHyeon status &= ~STE_ISR_RX_DMADONE; 537fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE; 538fabbaac5SPyun YongHyeon } 539c8befdd5SWarner Losh 540fabbaac5SPyun YongHyeon if ((status & (STE_ISR_SOFTINTR | STE_ISR_RX_DMADONE)) != 0) { 541a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 542fabbaac5SPyun YongHyeon /* 543fabbaac5SPyun YongHyeon * The controller has no ability to Rx interrupt 544fabbaac5SPyun YongHyeon * moderation feature. Receiving 64 bytes frames 545fabbaac5SPyun YongHyeon * from wire generates too many interrupts which in 546fabbaac5SPyun YongHyeon * turn make system useless to process other useful 547fabbaac5SPyun YongHyeon * things. Fortunately ST201 supports single shot 548fabbaac5SPyun YongHyeon * timer so use the timer to implement Rx interrupt 549fabbaac5SPyun YongHyeon * moderation in driver. This adds more register 550fabbaac5SPyun YongHyeon * access but it greatly reduces number of Rx 551fabbaac5SPyun YongHyeon * interrupts under high network load. 552fabbaac5SPyun YongHyeon */ 553fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 554fabbaac5SPyun YongHyeon (sc->ste_int_rx_mod != 0)) { 555fabbaac5SPyun YongHyeon if ((status & STE_ISR_RX_DMADONE) != 0) { 556fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 557fabbaac5SPyun YongHyeon STE_TIMER_USECS(sc->ste_int_rx_mod)); 558fabbaac5SPyun YongHyeon intrs &= ~STE_IMR_RX_DMADONE; 559fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 1; 560fabbaac5SPyun YongHyeon } else { 561fabbaac5SPyun YongHyeon intrs |= STE_IMR_RX_DMADONE; 562fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0; 563fabbaac5SPyun YongHyeon } 564fabbaac5SPyun YongHyeon } 565fabbaac5SPyun YongHyeon } 566fabbaac5SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 567fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DMADONE) != 0) 568c8befdd5SWarner Losh ste_txeof(sc); 569fabbaac5SPyun YongHyeon if ((status & STE_ISR_TX_DONE) != 0) 570c8befdd5SWarner Losh ste_txeoc(sc); 571fabbaac5SPyun YongHyeon if ((status & STE_ISR_STATS_OFLOW) != 0) 572c8befdd5SWarner Losh ste_stats_update(sc); 573fabbaac5SPyun YongHyeon if ((status & STE_ISR_HOSTERR) != 0) { 57455d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 575fabbaac5SPyun YongHyeon ste_init_locked(sc); 576fabbaac5SPyun YongHyeon STE_UNLOCK(sc); 577fabbaac5SPyun YongHyeon return; 57855d7003eSPyun YongHyeon } 579c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 580c8befdd5SWarner Losh ste_start_locked(ifp); 581fabbaac5SPyun YongHyeon done: 582fabbaac5SPyun YongHyeon /* Re-enable interrupts */ 583fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, intrs); 584fabbaac5SPyun YongHyeon } 585c8befdd5SWarner Losh STE_UNLOCK(sc); 586c8befdd5SWarner Losh } 587c8befdd5SWarner Losh 588c8befdd5SWarner Losh /* 589c8befdd5SWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 590c8befdd5SWarner Losh * the higher level protocols. 591c8befdd5SWarner Losh */ 5921abcdbd1SAttilio Rao static int 593a1b2c209SPyun YongHyeon ste_rxeof(struct ste_softc *sc, int count) 594c8befdd5SWarner Losh { 595c8befdd5SWarner Losh struct mbuf *m; 596c8befdd5SWarner Losh struct ifnet *ifp; 597c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx; 59856af54f2SPyun YongHyeon uint32_t rxstat; 599a1b2c209SPyun YongHyeon int total_len, rx_npkts; 600c8befdd5SWarner Losh 601c8befdd5SWarner Losh ifp = sc->ste_ifp; 602c8befdd5SWarner Losh 603a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 604a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 605a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 606c8befdd5SWarner Losh 607c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head; 608a1b2c209SPyun YongHyeon for (rx_npkts = 0; rx_npkts < STE_RX_LIST_CNT; rx_npkts++, 609a1b2c209SPyun YongHyeon cur_rx = cur_rx->ste_next) { 610a1b2c209SPyun YongHyeon rxstat = le32toh(cur_rx->ste_ptr->ste_status); 611a1b2c209SPyun YongHyeon if ((rxstat & STE_RXSTAT_DMADONE) == 0) 612a1b2c209SPyun YongHyeon break; 613a1b2c209SPyun YongHyeon #ifdef DEVICE_POLLING 614a1b2c209SPyun YongHyeon if (ifp->if_capenable & IFCAP_POLLING) { 615a1b2c209SPyun YongHyeon if (count == 0) 616a1b2c209SPyun YongHyeon break; 617a1b2c209SPyun YongHyeon count--; 618a1b2c209SPyun YongHyeon } 619a1b2c209SPyun YongHyeon #endif 620a1b2c209SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 621a1b2c209SPyun YongHyeon break; 622c8befdd5SWarner Losh /* 623c8befdd5SWarner Losh * If an error occurs, update stats, clear the 624c8befdd5SWarner Losh * status word and leave the mbuf cluster in place: 625c8befdd5SWarner Losh * it should simply get re-used next time this descriptor 626c8befdd5SWarner Losh * comes up in the ring. 627c8befdd5SWarner Losh */ 628c8befdd5SWarner Losh if (rxstat & STE_RXSTAT_FRAME_ERR) { 629c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 630c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 631c8befdd5SWarner Losh continue; 632c8befdd5SWarner Losh } 633c8befdd5SWarner Losh 634c8befdd5SWarner Losh /* No errors; receive the packet. */ 635c8befdd5SWarner Losh m = cur_rx->ste_mbuf; 636a1b2c209SPyun YongHyeon total_len = STE_RX_BYTES(rxstat); 637c8befdd5SWarner Losh 638c8befdd5SWarner Losh /* 639c8befdd5SWarner Losh * Try to conjure up a new mbuf cluster. If that 640c8befdd5SWarner Losh * fails, it means we have an out of memory condition and 641c8befdd5SWarner Losh * should leave the buffer in place and continue. This will 642c8befdd5SWarner Losh * result in a lost packet, but there's little else we 643c8befdd5SWarner Losh * can do in this situation. 644c8befdd5SWarner Losh */ 645a1b2c209SPyun YongHyeon if (ste_newbuf(sc, cur_rx) != 0) { 646c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 647c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 648c8befdd5SWarner Losh continue; 649c8befdd5SWarner Losh } 650c8befdd5SWarner Losh 651c8befdd5SWarner Losh m->m_pkthdr.rcvif = ifp; 652c8befdd5SWarner Losh m->m_pkthdr.len = m->m_len = total_len; 653c8befdd5SWarner Losh 654c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 655c8befdd5SWarner Losh STE_UNLOCK(sc); 656c8befdd5SWarner Losh (*ifp->if_input)(ifp, m); 657c8befdd5SWarner Losh STE_LOCK(sc); 658a1b2c209SPyun YongHyeon } 659c8befdd5SWarner Losh 660a1b2c209SPyun YongHyeon if (rx_npkts > 0) { 661a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_head = cur_rx; 662a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 663a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 664a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 665c8befdd5SWarner Losh } 666c8befdd5SWarner Losh 6671abcdbd1SAttilio Rao return (rx_npkts); 668c8befdd5SWarner Losh } 669c8befdd5SWarner Losh 670c8befdd5SWarner Losh static void 67160270842SPyun YongHyeon ste_txeoc(struct ste_softc *sc) 672c8befdd5SWarner Losh { 67381598b3eSPyun YongHyeon uint16_t txstat; 674c8befdd5SWarner Losh struct ifnet *ifp; 67581598b3eSPyun YongHyeon 67681598b3eSPyun YongHyeon STE_LOCK_ASSERT(sc); 677c8befdd5SWarner Losh 678c8befdd5SWarner Losh ifp = sc->ste_ifp; 679c8befdd5SWarner Losh 68081598b3eSPyun YongHyeon /* 68181598b3eSPyun YongHyeon * STE_TX_STATUS register implements a queue of up to 31 68281598b3eSPyun YongHyeon * transmit status byte. Writing an arbitrary value to the 68381598b3eSPyun YongHyeon * register will advance the queue to the next transmit 68481598b3eSPyun YongHyeon * status byte. This means if driver does not read 68581598b3eSPyun YongHyeon * STE_TX_STATUS register after completing sending more 68681598b3eSPyun YongHyeon * than 31 frames the controller would be stalled so driver 68781598b3eSPyun YongHyeon * should re-wake the Tx MAC. This is the most severe 68881598b3eSPyun YongHyeon * limitation of ST201 based controller. 68981598b3eSPyun YongHyeon */ 69081598b3eSPyun YongHyeon for (;;) { 69181598b3eSPyun YongHyeon txstat = CSR_READ_2(sc, STE_TX_STATUS); 69281598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_TXDONE) == 0) 69381598b3eSPyun YongHyeon break; 69481598b3eSPyun YongHyeon if ((txstat & (STE_TXSTATUS_UNDERRUN | 69581598b3eSPyun YongHyeon STE_TXSTATUS_EXCESSCOLLS | STE_TXSTATUS_RECLAIMERR | 69681598b3eSPyun YongHyeon STE_TXSTATUS_STATSOFLOW)) != 0) { 697c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 69881598b3eSPyun YongHyeon #ifdef STE_SHOW_TXERRORS 69981598b3eSPyun YongHyeon device_printf(sc->ste_dev, "TX error : 0x%b\n", 70081598b3eSPyun YongHyeon txstat & 0xFF, STE_ERR_BITS); 70181598b3eSPyun YongHyeon #endif 70281598b3eSPyun YongHyeon if ((txstat & STE_TXSTATUS_UNDERRUN) != 0 && 703c8befdd5SWarner Losh sc->ste_tx_thresh < STE_PACKET_SIZE) { 704c8befdd5SWarner Losh sc->ste_tx_thresh += STE_MIN_FRAMELEN; 70581598b3eSPyun YongHyeon if (sc->ste_tx_thresh > STE_PACKET_SIZE) 70681598b3eSPyun YongHyeon sc->ste_tx_thresh = STE_PACKET_SIZE; 707c8befdd5SWarner Losh device_printf(sc->ste_dev, 70881598b3eSPyun YongHyeon "TX underrun, increasing TX" 709c8befdd5SWarner Losh " start threshold to %d bytes\n", 710c8befdd5SWarner Losh sc->ste_tx_thresh); 71181598b3eSPyun YongHyeon /* Make sure to disable active DMA cycles. */ 71281598b3eSPyun YongHyeon STE_SETBIT4(sc, STE_DMACTL, 71381598b3eSPyun YongHyeon STE_DMACTL_TXDMA_STALL); 71481598b3eSPyun YongHyeon ste_wait(sc); 71555d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 716c8befdd5SWarner Losh ste_init_locked(sc); 71781598b3eSPyun YongHyeon break; 71881598b3eSPyun YongHyeon } 71981598b3eSPyun YongHyeon /* Restart Tx. */ 72081598b3eSPyun YongHyeon ste_restart_tx(sc); 72181598b3eSPyun YongHyeon } 72281598b3eSPyun YongHyeon /* 72381598b3eSPyun YongHyeon * Advance to next status and ACK TxComplete 72481598b3eSPyun YongHyeon * interrupt. ST201 data sheet was wrong here, to 72581598b3eSPyun YongHyeon * get next Tx status, we have to write both 72681598b3eSPyun YongHyeon * STE_TX_STATUS and STE_TX_FRAMEID register. 72781598b3eSPyun YongHyeon * Otherwise controller returns the same status 72881598b3eSPyun YongHyeon * as well as not acknowledge Tx completion 72981598b3eSPyun YongHyeon * interrupt. 73081598b3eSPyun YongHyeon */ 731c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STATUS, txstat); 732c8befdd5SWarner Losh } 733c8befdd5SWarner Losh } 734c8befdd5SWarner Losh 735c8befdd5SWarner Losh static void 73610f695eeSPyun YongHyeon ste_tick(void *arg) 73710f695eeSPyun YongHyeon { 73810f695eeSPyun YongHyeon struct ste_softc *sc; 73910f695eeSPyun YongHyeon struct mii_data *mii; 74010f695eeSPyun YongHyeon 74110f695eeSPyun YongHyeon sc = (struct ste_softc *)arg; 74210f695eeSPyun YongHyeon 74310f695eeSPyun YongHyeon STE_LOCK_ASSERT(sc); 74410f695eeSPyun YongHyeon 74510f695eeSPyun YongHyeon mii = device_get_softc(sc->ste_miibus); 74610f695eeSPyun YongHyeon mii_tick(mii); 74710f695eeSPyun YongHyeon /* 74810f695eeSPyun YongHyeon * ukphy(4) does not seem to generate CB that reports 74910f695eeSPyun YongHyeon * resolved link state so if we know we lost a link, 75010f695eeSPyun YongHyeon * explicitly check the link state. 75110f695eeSPyun YongHyeon */ 75210f695eeSPyun YongHyeon if ((sc->ste_flags & STE_FLAG_LINK) == 0) 75310f695eeSPyun YongHyeon ste_miibus_statchg(sc->ste_dev); 754ae49e7a6SPyun YongHyeon /* 755ae49e7a6SPyun YongHyeon * Because we are not generating Tx completion 756ae49e7a6SPyun YongHyeon * interrupt for every frame, reclaim transmitted 757ae49e7a6SPyun YongHyeon * buffers here. 758ae49e7a6SPyun YongHyeon */ 759ae49e7a6SPyun YongHyeon ste_txeof(sc); 760ae49e7a6SPyun YongHyeon ste_txeoc(sc); 76110f695eeSPyun YongHyeon ste_stats_update(sc); 76210f695eeSPyun YongHyeon ste_watchdog(sc); 76310f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc); 76410f695eeSPyun YongHyeon } 76510f695eeSPyun YongHyeon 76610f695eeSPyun YongHyeon static void 76760270842SPyun YongHyeon ste_txeof(struct ste_softc *sc) 768c8befdd5SWarner Losh { 769c8befdd5SWarner Losh struct ifnet *ifp; 770f2632c3bSPyun YongHyeon struct ste_chain *cur_tx; 771a1b2c209SPyun YongHyeon uint32_t txstat; 772c8befdd5SWarner Losh int idx; 773c8befdd5SWarner Losh 774a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 775c8befdd5SWarner Losh 776a1b2c209SPyun YongHyeon ifp = sc->ste_ifp; 777c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_cons; 778a1b2c209SPyun YongHyeon if (idx == sc->ste_cdata.ste_tx_prod) 779a1b2c209SPyun YongHyeon return; 780a1b2c209SPyun YongHyeon 781a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 782a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 783a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 784a1b2c209SPyun YongHyeon 785c8befdd5SWarner Losh while (idx != sc->ste_cdata.ste_tx_prod) { 786c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; 787a1b2c209SPyun YongHyeon txstat = le32toh(cur_tx->ste_ptr->ste_ctl); 788a1b2c209SPyun YongHyeon if ((txstat & STE_TXCTL_DMADONE) == 0) 789c8befdd5SWarner Losh break; 790a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map, 791a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTWRITE); 792a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map); 793a1b2c209SPyun YongHyeon KASSERT(cur_tx->ste_mbuf != NULL, 794a1b2c209SPyun YongHyeon ("%s: freeing NULL mbuf!\n", __func__)); 795c8befdd5SWarner Losh m_freem(cur_tx->ste_mbuf); 796c8befdd5SWarner Losh cur_tx->ste_mbuf = NULL; 797c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 798c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 799a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt--; 800c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT); 801c8befdd5SWarner Losh } 802c8befdd5SWarner Losh 803c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons = idx; 804a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == 0) 8057cf545d0SJohn Baldwin sc->ste_timer = 0; 806c8befdd5SWarner Losh } 807c8befdd5SWarner Losh 808c8befdd5SWarner Losh static void 8098657caa6SPyun YongHyeon ste_stats_clear(struct ste_softc *sc) 8108657caa6SPyun YongHyeon { 8118657caa6SPyun YongHyeon 8128657caa6SPyun YongHyeon STE_LOCK_ASSERT(sc); 8138657caa6SPyun YongHyeon 8148657caa6SPyun YongHyeon /* Rx stats. */ 8158657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO); 8168657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI); 8178657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_RX_FRAMES); 8188657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_BCAST); 8198657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_MCAST); 8208657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_RX_LOST); 8218657caa6SPyun YongHyeon /* Tx stats. */ 8228657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO); 8238657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI); 8248657caa6SPyun YongHyeon CSR_READ_2(sc, STE_STAT_TX_FRAMES); 8258657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_BCAST); 8268657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_MCAST); 8278657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_CARRIER_ERR); 8288657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_SINGLE_COLLS); 8298657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_MULTI_COLLS); 8308657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_LATE_COLLS); 8318657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_DEFER); 8328657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_EXDEFER); 8338657caa6SPyun YongHyeon CSR_READ_1(sc, STE_STAT_TX_ABORT); 8348657caa6SPyun YongHyeon } 8358657caa6SPyun YongHyeon 8368657caa6SPyun YongHyeon static void 83710f695eeSPyun YongHyeon ste_stats_update(struct ste_softc *sc) 838c8befdd5SWarner Losh { 839c8befdd5SWarner Losh struct ifnet *ifp; 8408657caa6SPyun YongHyeon struct ste_hw_stats *stats; 8418657caa6SPyun YongHyeon uint32_t val; 842c8befdd5SWarner Losh 843c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 844c8befdd5SWarner Losh 845c8befdd5SWarner Losh ifp = sc->ste_ifp; 8468657caa6SPyun YongHyeon stats = &sc->ste_stats; 8478657caa6SPyun YongHyeon /* Rx stats. */ 8488657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_LO) | 8498657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_RX_OCTETS_HI)) << 16; 8508657caa6SPyun YongHyeon val &= 0x000FFFFF; 8518657caa6SPyun YongHyeon stats->rx_bytes += val; 8528657caa6SPyun YongHyeon stats->rx_frames += CSR_READ_2(sc, STE_STAT_RX_FRAMES); 8538657caa6SPyun YongHyeon stats->rx_bcast_frames += CSR_READ_1(sc, STE_STAT_RX_BCAST); 8548657caa6SPyun YongHyeon stats->rx_mcast_frames += CSR_READ_1(sc, STE_STAT_RX_MCAST); 8558657caa6SPyun YongHyeon stats->rx_lost_frames += CSR_READ_1(sc, STE_STAT_RX_LOST); 8568657caa6SPyun YongHyeon /* Tx stats. */ 8578657caa6SPyun YongHyeon val = (uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_LO) | 8588657caa6SPyun YongHyeon ((uint32_t)CSR_READ_2(sc, STE_STAT_TX_OCTETS_HI)) << 16; 8598657caa6SPyun YongHyeon val &= 0x000FFFFF; 8608657caa6SPyun YongHyeon stats->tx_bytes += val; 8618657caa6SPyun YongHyeon stats->tx_frames += CSR_READ_2(sc, STE_STAT_TX_FRAMES); 8628657caa6SPyun YongHyeon stats->tx_bcast_frames += CSR_READ_1(sc, STE_STAT_TX_BCAST); 8638657caa6SPyun YongHyeon stats->tx_mcast_frames += CSR_READ_1(sc, STE_STAT_TX_MCAST); 8648657caa6SPyun YongHyeon stats->tx_carrsense_errs += CSR_READ_1(sc, STE_STAT_CARRIER_ERR); 8658657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_SINGLE_COLLS); 8668657caa6SPyun YongHyeon stats->tx_single_colls += val; 867c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val); 8688657caa6SPyun YongHyeon val = CSR_READ_1(sc, STE_STAT_MULTI_COLLS); 8698657caa6SPyun YongHyeon stats->tx_multi_colls += val; 870c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val); 8718657caa6SPyun YongHyeon val += CSR_READ_1(sc, STE_STAT_LATE_COLLS); 8728657caa6SPyun YongHyeon stats->tx_late_colls += val; 873c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val); 8748657caa6SPyun YongHyeon stats->tx_frames_defered += CSR_READ_1(sc, STE_STAT_TX_DEFER); 8758657caa6SPyun YongHyeon stats->tx_excess_defers += CSR_READ_1(sc, STE_STAT_TX_EXDEFER); 8768657caa6SPyun YongHyeon stats->tx_abort += CSR_READ_1(sc, STE_STAT_TX_ABORT); 877c8befdd5SWarner Losh } 878c8befdd5SWarner Losh 879c8befdd5SWarner Losh /* 880c8befdd5SWarner Losh * Probe for a Sundance ST201 chip. Check the PCI vendor and device 881c8befdd5SWarner Losh * IDs against our list and return a device name if we find a match. 882c8befdd5SWarner Losh */ 883c8befdd5SWarner Losh static int 88460270842SPyun YongHyeon ste_probe(device_t dev) 885c8befdd5SWarner Losh { 8868c1093fcSMarius Strobl const struct ste_type *t; 887c8befdd5SWarner Losh 888c8befdd5SWarner Losh t = ste_devs; 889c8befdd5SWarner Losh 890c8befdd5SWarner Losh while (t->ste_name != NULL) { 891c8befdd5SWarner Losh if ((pci_get_vendor(dev) == t->ste_vid) && 892c8befdd5SWarner Losh (pci_get_device(dev) == t->ste_did)) { 893c8befdd5SWarner Losh device_set_desc(dev, t->ste_name); 894c8befdd5SWarner Losh return (BUS_PROBE_DEFAULT); 895c8befdd5SWarner Losh } 896c8befdd5SWarner Losh t++; 897c8befdd5SWarner Losh } 898c8befdd5SWarner Losh 899c8befdd5SWarner Losh return (ENXIO); 900c8befdd5SWarner Losh } 901c8befdd5SWarner Losh 902c8befdd5SWarner Losh /* 903c8befdd5SWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 904c8befdd5SWarner Losh * setup and ethernet/BPF attach. 905c8befdd5SWarner Losh */ 906c8befdd5SWarner Losh static int 90760270842SPyun YongHyeon ste_attach(device_t dev) 908c8befdd5SWarner Losh { 909c8befdd5SWarner Losh struct ste_softc *sc; 910c8befdd5SWarner Losh struct ifnet *ifp; 911fcd8385eSPyun YongHyeon uint16_t eaddr[ETHER_ADDR_LEN / 2]; 9128e5d93dbSMarius Strobl int error = 0, phy, pmc, prefer_iomap, rid; 913c8befdd5SWarner Losh 914c8befdd5SWarner Losh sc = device_get_softc(dev); 915c8befdd5SWarner Losh sc->ste_dev = dev; 916c8befdd5SWarner Losh 917c8befdd5SWarner Losh /* 918c8befdd5SWarner Losh * Only use one PHY since this chip reports multiple 919c8befdd5SWarner Losh * Note on the DFE-550 the PHY is at 1 on the DFE-580 920c8befdd5SWarner Losh * it is at 0 & 1. It is rev 0x12. 921c8befdd5SWarner Losh */ 922c8befdd5SWarner Losh if (pci_get_vendor(dev) == DL_VENDORID && 923c8befdd5SWarner Losh pci_get_device(dev) == DL_DEVICEID_DL10050 && 924c8befdd5SWarner Losh pci_get_revid(dev) == 0x12 ) 9254465097bSPyun YongHyeon sc->ste_flags |= STE_FLAG_ONE_PHY; 926c8befdd5SWarner Losh 927c8befdd5SWarner Losh mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 928c8befdd5SWarner Losh MTX_DEF); 929c8befdd5SWarner Losh /* 930c8befdd5SWarner Losh * Map control/status registers. 931c8befdd5SWarner Losh */ 932c8befdd5SWarner Losh pci_enable_busmaster(dev); 933c8befdd5SWarner Losh 934497ffa52SPyun YongHyeon /* 935497ffa52SPyun YongHyeon * Prefer memory space register mapping over IO space but use 936497ffa52SPyun YongHyeon * IO space for a device that is known to have issues on memory 937497ffa52SPyun YongHyeon * mapping. 938497ffa52SPyun YongHyeon */ 939497ffa52SPyun YongHyeon prefer_iomap = 0; 940497ffa52SPyun YongHyeon if (pci_get_device(dev) == ST_DEVICEID_ST201_1) 941497ffa52SPyun YongHyeon prefer_iomap = 1; 942497ffa52SPyun YongHyeon else 943497ffa52SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev), 944497ffa52SPyun YongHyeon device_get_unit(sc->ste_dev), "prefer_iomap", 945497ffa52SPyun YongHyeon &prefer_iomap); 946497ffa52SPyun YongHyeon if (prefer_iomap == 0) { 947c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(1); 948c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_MEMORY; 949c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type, 950c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE); 951497ffa52SPyun YongHyeon } 952497ffa52SPyun YongHyeon if (prefer_iomap || sc->ste_res == NULL) { 953c0270e60SPyun YongHyeon sc->ste_res_id = PCIR_BAR(0); 954c0270e60SPyun YongHyeon sc->ste_res_type = SYS_RES_IOPORT; 955c0270e60SPyun YongHyeon sc->ste_res = bus_alloc_resource_any(dev, sc->ste_res_type, 956c0270e60SPyun YongHyeon &sc->ste_res_id, RF_ACTIVE); 957c0270e60SPyun YongHyeon } 958c8befdd5SWarner Losh if (sc->ste_res == NULL) { 959c8befdd5SWarner Losh device_printf(dev, "couldn't map ports/memory\n"); 960c8befdd5SWarner Losh error = ENXIO; 961c8befdd5SWarner Losh goto fail; 962c8befdd5SWarner Losh } 963c8befdd5SWarner Losh 964c8befdd5SWarner Losh /* Allocate interrupt */ 965c8befdd5SWarner Losh rid = 0; 966c8befdd5SWarner Losh sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 967c8befdd5SWarner Losh RF_SHAREABLE | RF_ACTIVE); 968c8befdd5SWarner Losh 969c8befdd5SWarner Losh if (sc->ste_irq == NULL) { 970c8befdd5SWarner Losh device_printf(dev, "couldn't map interrupt\n"); 971c8befdd5SWarner Losh error = ENXIO; 972c8befdd5SWarner Losh goto fail; 973c8befdd5SWarner Losh } 974c8befdd5SWarner Losh 97510f695eeSPyun YongHyeon callout_init_mtx(&sc->ste_callout, &sc->ste_mtx, 0); 976c8befdd5SWarner Losh 977c8befdd5SWarner Losh /* Reset the adapter. */ 978c8befdd5SWarner Losh ste_reset(sc); 979c8befdd5SWarner Losh 980c8befdd5SWarner Losh /* 981c8befdd5SWarner Losh * Get station address from the EEPROM. 982c8befdd5SWarner Losh */ 983fcd8385eSPyun YongHyeon if (ste_read_eeprom(sc, eaddr, STE_EEADDR_NODE0, ETHER_ADDR_LEN / 2)) { 984c8befdd5SWarner Losh device_printf(dev, "failed to read station address\n"); 985c2ede4b3SMartin Blapp error = ENXIO; 986c8befdd5SWarner Losh goto fail; 987c8befdd5SWarner Losh } 9888657caa6SPyun YongHyeon ste_sysctl_node(sc); 989c8befdd5SWarner Losh 990a1b2c209SPyun YongHyeon if ((error = ste_dma_alloc(sc)) != 0) 991c8befdd5SWarner Losh goto fail; 992c8befdd5SWarner Losh 993c8befdd5SWarner Losh ifp = sc->ste_ifp = if_alloc(IFT_ETHER); 994c8befdd5SWarner Losh if (ifp == NULL) { 995c8befdd5SWarner Losh device_printf(dev, "can not if_alloc()\n"); 996c8befdd5SWarner Losh error = ENOSPC; 997c8befdd5SWarner Losh goto fail; 998c8befdd5SWarner Losh } 999c8befdd5SWarner Losh 1000c8befdd5SWarner Losh /* Do MII setup. */ 10018e5d93dbSMarius Strobl phy = MII_PHY_ANY; 10028e5d93dbSMarius Strobl if ((sc->ste_flags & STE_FLAG_ONE_PHY) != 0) 10038e5d93dbSMarius Strobl phy = 0; 10048e5d93dbSMarius Strobl error = mii_attach(dev, &sc->ste_miibus, ifp, ste_ifmedia_upd, 10058e5d93dbSMarius Strobl ste_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 10068e5d93dbSMarius Strobl if (error != 0) { 10078e5d93dbSMarius Strobl device_printf(dev, "attaching PHYs failed\n"); 1008c8befdd5SWarner Losh goto fail; 1009c8befdd5SWarner Losh } 1010c8befdd5SWarner Losh 1011c8befdd5SWarner Losh ifp->if_softc = sc; 1012c8befdd5SWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1013c8befdd5SWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1014c8befdd5SWarner Losh ifp->if_ioctl = ste_ioctl; 1015c8befdd5SWarner Losh ifp->if_start = ste_start; 1016c8befdd5SWarner Losh ifp->if_init = ste_init; 1017c8befdd5SWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, STE_TX_LIST_CNT - 1); 1018c8befdd5SWarner Losh ifp->if_snd.ifq_drv_maxlen = STE_TX_LIST_CNT - 1; 1019c8befdd5SWarner Losh IFQ_SET_READY(&ifp->if_snd); 1020c8befdd5SWarner Losh 1021c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1022c8befdd5SWarner Losh 1023c8befdd5SWarner Losh /* 1024c8befdd5SWarner Losh * Call MI attach routine. 1025c8befdd5SWarner Losh */ 1026fcd8385eSPyun YongHyeon ether_ifattach(ifp, (uint8_t *)eaddr); 1027c8befdd5SWarner Losh 1028c8befdd5SWarner Losh /* 1029c8befdd5SWarner Losh * Tell the upper layer(s) we support long frames. 1030c8befdd5SWarner Losh */ 10311bffa951SGleb Smirnoff ifp->if_hdrlen = sizeof(struct ether_vlan_header); 1032c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 10333b0a4aefSJohn Baldwin if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0) 1034b4c170e1SPyun YongHyeon ifp->if_capabilities |= IFCAP_WOL_MAGIC; 1035c8befdd5SWarner Losh ifp->if_capenable = ifp->if_capabilities; 1036c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1037c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1038c8befdd5SWarner Losh #endif 1039c8befdd5SWarner Losh 1040c8befdd5SWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1041c8befdd5SWarner Losh error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET | INTR_MPSAFE, 1042c8befdd5SWarner Losh NULL, ste_intr, sc, &sc->ste_intrhand); 1043c8befdd5SWarner Losh 1044c8befdd5SWarner Losh if (error) { 1045c8befdd5SWarner Losh device_printf(dev, "couldn't set up irq\n"); 1046c8befdd5SWarner Losh ether_ifdetach(ifp); 1047c8befdd5SWarner Losh goto fail; 1048c8befdd5SWarner Losh } 1049c8befdd5SWarner Losh 1050c8befdd5SWarner Losh fail: 1051c8befdd5SWarner Losh if (error) 1052c8befdd5SWarner Losh ste_detach(dev); 1053c8befdd5SWarner Losh 1054c8befdd5SWarner Losh return (error); 1055c8befdd5SWarner Losh } 1056c8befdd5SWarner Losh 1057c8befdd5SWarner Losh /* 1058c8befdd5SWarner Losh * Shutdown hardware and free up resources. This can be called any 1059c8befdd5SWarner Losh * time after the mutex has been initialized. It is called in both 1060c8befdd5SWarner Losh * the error case in attach and the normal detach case so it needs 1061c8befdd5SWarner Losh * to be careful about only freeing resources that have actually been 1062c8befdd5SWarner Losh * allocated. 1063c8befdd5SWarner Losh */ 1064c8befdd5SWarner Losh static int 106560270842SPyun YongHyeon ste_detach(device_t dev) 1066c8befdd5SWarner Losh { 1067c8befdd5SWarner Losh struct ste_softc *sc; 1068c8befdd5SWarner Losh struct ifnet *ifp; 1069c8befdd5SWarner Losh 1070c8befdd5SWarner Losh sc = device_get_softc(dev); 1071c8befdd5SWarner Losh KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized")); 1072c8befdd5SWarner Losh ifp = sc->ste_ifp; 1073c8befdd5SWarner Losh 1074c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1075c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1076c8befdd5SWarner Losh ether_poll_deregister(ifp); 1077c8befdd5SWarner Losh #endif 1078c8befdd5SWarner Losh 1079c8befdd5SWarner Losh /* These should only be active if attach succeeded */ 1080c8befdd5SWarner Losh if (device_is_attached(dev)) { 10817cf545d0SJohn Baldwin ether_ifdetach(ifp); 1082c8befdd5SWarner Losh STE_LOCK(sc); 1083c8befdd5SWarner Losh ste_stop(sc); 1084c8befdd5SWarner Losh STE_UNLOCK(sc); 108510f695eeSPyun YongHyeon callout_drain(&sc->ste_callout); 1086c8befdd5SWarner Losh } 1087c8befdd5SWarner Losh if (sc->ste_miibus) 1088c8befdd5SWarner Losh device_delete_child(dev, sc->ste_miibus); 1089c8befdd5SWarner Losh bus_generic_detach(dev); 1090c8befdd5SWarner Losh 1091c8befdd5SWarner Losh if (sc->ste_intrhand) 1092c8befdd5SWarner Losh bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); 1093c8befdd5SWarner Losh if (sc->ste_irq) 1094c8befdd5SWarner Losh bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); 1095c8befdd5SWarner Losh if (sc->ste_res) 1096c0270e60SPyun YongHyeon bus_release_resource(dev, sc->ste_res_type, sc->ste_res_id, 1097c0270e60SPyun YongHyeon sc->ste_res); 1098c8befdd5SWarner Losh 1099c8befdd5SWarner Losh if (ifp) 1100c8befdd5SWarner Losh if_free(ifp); 1101c8befdd5SWarner Losh 1102a1b2c209SPyun YongHyeon ste_dma_free(sc); 1103c8befdd5SWarner Losh mtx_destroy(&sc->ste_mtx); 1104c8befdd5SWarner Losh 1105c8befdd5SWarner Losh return (0); 1106c8befdd5SWarner Losh } 1107c8befdd5SWarner Losh 1108a1b2c209SPyun YongHyeon struct ste_dmamap_arg { 1109a1b2c209SPyun YongHyeon bus_addr_t ste_busaddr; 1110a1b2c209SPyun YongHyeon }; 1111a1b2c209SPyun YongHyeon 1112a1b2c209SPyun YongHyeon static void 1113a1b2c209SPyun YongHyeon ste_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1114c8befdd5SWarner Losh { 1115a1b2c209SPyun YongHyeon struct ste_dmamap_arg *ctx; 1116c8befdd5SWarner Losh 1117a1b2c209SPyun YongHyeon if (error != 0) 1118a1b2c209SPyun YongHyeon return; 1119a1b2c209SPyun YongHyeon 1120a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1121a1b2c209SPyun YongHyeon 1122a1b2c209SPyun YongHyeon ctx = (struct ste_dmamap_arg *)arg; 1123a1b2c209SPyun YongHyeon ctx->ste_busaddr = segs[0].ds_addr; 1124c8befdd5SWarner Losh } 1125c8befdd5SWarner Losh 1126a1b2c209SPyun YongHyeon static int 1127a1b2c209SPyun YongHyeon ste_dma_alloc(struct ste_softc *sc) 1128a1b2c209SPyun YongHyeon { 1129a1b2c209SPyun YongHyeon struct ste_chain *txc; 1130a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1131a1b2c209SPyun YongHyeon struct ste_dmamap_arg ctx; 1132a1b2c209SPyun YongHyeon int error, i; 1133c8befdd5SWarner Losh 1134a1b2c209SPyun YongHyeon /* Create parent DMA tag. */ 1135a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1136a1b2c209SPyun YongHyeon bus_get_dma_tag(sc->ste_dev), /* parent */ 1137a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1138a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1139a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1140a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1141a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 1142a1b2c209SPyun YongHyeon 0, /* nsegments */ 1143a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1144a1b2c209SPyun YongHyeon 0, /* flags */ 1145a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1146a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_parent_tag); 1147a1b2c209SPyun YongHyeon if (error != 0) { 1148a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1149a1b2c209SPyun YongHyeon "could not create parent DMA tag.\n"); 1150a1b2c209SPyun YongHyeon goto fail; 1151a1b2c209SPyun YongHyeon } 1152c8befdd5SWarner Losh 1153a1b2c209SPyun YongHyeon /* Create DMA tag for Tx descriptor list. */ 1154a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1155a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1156a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1157a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1158a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1159a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1160a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsize */ 1161a1b2c209SPyun YongHyeon 1, /* nsegments */ 1162a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsegsize */ 1163a1b2c209SPyun YongHyeon 0, /* flags */ 1164a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1165a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_tag); 1166a1b2c209SPyun YongHyeon if (error != 0) { 1167a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1168a1b2c209SPyun YongHyeon "could not create Tx list DMA tag.\n"); 1169a1b2c209SPyun YongHyeon goto fail; 1170a1b2c209SPyun YongHyeon } 1171a1b2c209SPyun YongHyeon 1172a1b2c209SPyun YongHyeon /* Create DMA tag for Rx descriptor list. */ 1173a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1174a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1175a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1176a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1177a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1178a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1179a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsize */ 1180a1b2c209SPyun YongHyeon 1, /* nsegments */ 1181a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsegsize */ 1182a1b2c209SPyun YongHyeon 0, /* flags */ 1183a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1184a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_tag); 1185a1b2c209SPyun YongHyeon if (error != 0) { 1186a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1187a1b2c209SPyun YongHyeon "could not create Rx list DMA tag.\n"); 1188a1b2c209SPyun YongHyeon goto fail; 1189a1b2c209SPyun YongHyeon } 1190a1b2c209SPyun YongHyeon 1191a1b2c209SPyun YongHyeon /* Create DMA tag for Tx buffers. */ 1192a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1193a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1194a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1195a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1196a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1197a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1198a1b2c209SPyun YongHyeon MCLBYTES * STE_MAXFRAGS, /* maxsize */ 1199a1b2c209SPyun YongHyeon STE_MAXFRAGS, /* nsegments */ 1200a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1201a1b2c209SPyun YongHyeon 0, /* flags */ 1202a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1203a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_tag); 1204a1b2c209SPyun YongHyeon if (error != 0) { 1205a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Tx DMA tag.\n"); 1206a1b2c209SPyun YongHyeon goto fail; 1207a1b2c209SPyun YongHyeon } 1208a1b2c209SPyun YongHyeon 1209a1b2c209SPyun YongHyeon /* Create DMA tag for Rx buffers. */ 1210a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1211a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1212a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1213a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1214a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1215a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1216a1b2c209SPyun YongHyeon MCLBYTES, /* maxsize */ 1217a1b2c209SPyun YongHyeon 1, /* nsegments */ 1218a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1219a1b2c209SPyun YongHyeon 0, /* flags */ 1220a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1221a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_tag); 1222a1b2c209SPyun YongHyeon if (error != 0) { 1223a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Rx DMA tag.\n"); 1224a1b2c209SPyun YongHyeon goto fail; 1225a1b2c209SPyun YongHyeon } 1226a1b2c209SPyun YongHyeon 1227a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Tx list. */ 1228a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_tx_list_tag, 1229a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_tx_list, 1230a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1231a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_map); 1232a1b2c209SPyun YongHyeon if (error != 0) { 1233a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1234a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Tx list.\n"); 1235a1b2c209SPyun YongHyeon goto fail; 1236a1b2c209SPyun YongHyeon } 1237a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1238a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_tx_list_tag, 1239a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, sc->ste_ldata.ste_tx_list, 1240a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1241a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1242a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1243a1b2c209SPyun YongHyeon "could not load DMA'able memory for Tx list.\n"); 1244a1b2c209SPyun YongHyeon goto fail; 1245a1b2c209SPyun YongHyeon } 1246a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list_paddr = ctx.ste_busaddr; 1247a1b2c209SPyun YongHyeon 1248a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Rx list. */ 1249a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_rx_list_tag, 1250a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_rx_list, 1251a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1252a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_map); 1253a1b2c209SPyun YongHyeon if (error != 0) { 1254a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1255a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Rx list.\n"); 1256a1b2c209SPyun YongHyeon goto fail; 1257a1b2c209SPyun YongHyeon } 1258a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1259a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_rx_list_tag, 1260a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, sc->ste_ldata.ste_rx_list, 1261a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1262a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1263a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1264a1b2c209SPyun YongHyeon "could not load DMA'able memory for Rx list.\n"); 1265a1b2c209SPyun YongHyeon goto fail; 1266a1b2c209SPyun YongHyeon } 1267a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list_paddr = ctx.ste_busaddr; 1268a1b2c209SPyun YongHyeon 1269a1b2c209SPyun YongHyeon /* Create DMA maps for Tx buffers. */ 1270a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1271a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1272a1b2c209SPyun YongHyeon txc->ste_ptr = NULL; 1273a1b2c209SPyun YongHyeon txc->ste_mbuf = NULL; 1274a1b2c209SPyun YongHyeon txc->ste_next = NULL; 1275a1b2c209SPyun YongHyeon txc->ste_phys = 0; 1276a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1277a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_tx_tag, 0, 1278a1b2c209SPyun YongHyeon &txc->ste_map); 1279a1b2c209SPyun YongHyeon if (error != 0) { 1280a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1281a1b2c209SPyun YongHyeon "could not create Tx dmamap.\n"); 1282a1b2c209SPyun YongHyeon goto fail; 1283a1b2c209SPyun YongHyeon } 1284a1b2c209SPyun YongHyeon } 1285a1b2c209SPyun YongHyeon /* Create DMA maps for Rx buffers. */ 1286a1b2c209SPyun YongHyeon if ((error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1287a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_sparemap)) != 0) { 1288a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1289a1b2c209SPyun YongHyeon "could not create spare Rx dmamap.\n"); 1290a1b2c209SPyun YongHyeon goto fail; 1291a1b2c209SPyun YongHyeon } 1292a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1293a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1294a1b2c209SPyun YongHyeon rxc->ste_ptr = NULL; 1295a1b2c209SPyun YongHyeon rxc->ste_mbuf = NULL; 1296a1b2c209SPyun YongHyeon rxc->ste_next = NULL; 1297a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1298a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1299a1b2c209SPyun YongHyeon &rxc->ste_map); 1300a1b2c209SPyun YongHyeon if (error != 0) { 1301a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1302a1b2c209SPyun YongHyeon "could not create Rx dmamap.\n"); 1303a1b2c209SPyun YongHyeon goto fail; 1304a1b2c209SPyun YongHyeon } 1305a1b2c209SPyun YongHyeon } 1306a1b2c209SPyun YongHyeon 1307a1b2c209SPyun YongHyeon fail: 1308a1b2c209SPyun YongHyeon return (error); 1309a1b2c209SPyun YongHyeon } 1310a1b2c209SPyun YongHyeon 1311a1b2c209SPyun YongHyeon static void 1312a1b2c209SPyun YongHyeon ste_dma_free(struct ste_softc *sc) 1313a1b2c209SPyun YongHyeon { 1314a1b2c209SPyun YongHyeon struct ste_chain *txc; 1315a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1316a1b2c209SPyun YongHyeon int i; 1317a1b2c209SPyun YongHyeon 1318a1b2c209SPyun YongHyeon /* Tx buffers. */ 1319a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_tag != NULL) { 1320a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1321a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1322a1b2c209SPyun YongHyeon if (txc->ste_map != NULL) { 1323a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_tx_tag, 1324a1b2c209SPyun YongHyeon txc->ste_map); 1325a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1326a1b2c209SPyun YongHyeon } 1327a1b2c209SPyun YongHyeon } 1328a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_tag); 1329a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_tag = NULL; 1330a1b2c209SPyun YongHyeon } 1331a1b2c209SPyun YongHyeon /* Rx buffers. */ 1332a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_tag != NULL) { 1333a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1334a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1335a1b2c209SPyun YongHyeon if (rxc->ste_map != NULL) { 1336a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1337a1b2c209SPyun YongHyeon rxc->ste_map); 1338a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1339a1b2c209SPyun YongHyeon } 1340a1b2c209SPyun YongHyeon } 1341a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_sparemap != NULL) { 1342a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1343a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap); 1344a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = NULL; 1345a1b2c209SPyun YongHyeon } 1346a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_tag); 1347a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_tag = NULL; 1348a1b2c209SPyun YongHyeon } 1349a1b2c209SPyun YongHyeon /* Tx descriptor list. */ 1350a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_tag != NULL) { 1351068d8643SJohn Baldwin if (sc->ste_ldata.ste_tx_list_paddr != 0) 1352a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_list_tag, 1353a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1354068d8643SJohn Baldwin if (sc->ste_ldata.ste_tx_list != NULL) 1355a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_tx_list_tag, 1356a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list, 1357a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1358a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list = NULL; 1359068d8643SJohn Baldwin sc->ste_ldata.ste_tx_list_paddr = 0; 1360a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_list_tag); 1361a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_tag = NULL; 1362a1b2c209SPyun YongHyeon } 1363a1b2c209SPyun YongHyeon /* Rx descriptor list. */ 1364a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_tag != NULL) { 1365068d8643SJohn Baldwin if (sc->ste_ldata.ste_rx_list_paddr != 0) 1366a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_list_tag, 1367a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1368068d8643SJohn Baldwin if (sc->ste_ldata.ste_rx_list != NULL) 1369a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_rx_list_tag, 1370a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list, 1371a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1372a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list = NULL; 1373068d8643SJohn Baldwin sc->ste_ldata.ste_rx_list_paddr = 0; 1374a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_list_tag); 1375a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_tag = NULL; 1376a1b2c209SPyun YongHyeon } 1377a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_parent_tag != NULL) { 1378a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_parent_tag); 1379a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag = NULL; 1380a1b2c209SPyun YongHyeon } 1381a1b2c209SPyun YongHyeon } 1382a1b2c209SPyun YongHyeon 1383a1b2c209SPyun YongHyeon static int 1384a1b2c209SPyun YongHyeon ste_newbuf(struct ste_softc *sc, struct ste_chain_onefrag *rxc) 1385a1b2c209SPyun YongHyeon { 1386a1b2c209SPyun YongHyeon struct mbuf *m; 1387a1b2c209SPyun YongHyeon bus_dma_segment_t segs[1]; 1388a1b2c209SPyun YongHyeon bus_dmamap_t map; 1389a1b2c209SPyun YongHyeon int error, nsegs; 1390a1b2c209SPyun YongHyeon 1391c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1392a1b2c209SPyun YongHyeon if (m == NULL) 1393a1b2c209SPyun YongHyeon return (ENOBUFS); 1394a1b2c209SPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES; 1395a1b2c209SPyun YongHyeon m_adj(m, ETHER_ALIGN); 1396a1b2c209SPyun YongHyeon 1397a1b2c209SPyun YongHyeon if ((error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_rx_tag, 1398a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap, m, segs, &nsegs, 0)) != 0) { 1399a1b2c209SPyun YongHyeon m_freem(m); 1400a1b2c209SPyun YongHyeon return (error); 1401a1b2c209SPyun YongHyeon } 1402a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1403a1b2c209SPyun YongHyeon 1404a1b2c209SPyun YongHyeon if (rxc->ste_mbuf != NULL) { 1405a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1406a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD); 1407a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, rxc->ste_map); 1408a1b2c209SPyun YongHyeon } 1409a1b2c209SPyun YongHyeon map = rxc->ste_map; 1410a1b2c209SPyun YongHyeon rxc->ste_map = sc->ste_cdata.ste_rx_sparemap; 1411a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = map; 1412a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1413a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD); 1414a1b2c209SPyun YongHyeon rxc->ste_mbuf = m; 1415a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_status = 0; 1416a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_addr = htole32(segs[0].ds_addr); 1417a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_len = htole32(segs[0].ds_len | 1418a1b2c209SPyun YongHyeon STE_FRAG_LAST); 1419c8befdd5SWarner Losh return (0); 1420c8befdd5SWarner Losh } 1421c8befdd5SWarner Losh 1422c8befdd5SWarner Losh static int 142360270842SPyun YongHyeon ste_init_rx_list(struct ste_softc *sc) 1424c8befdd5SWarner Losh { 1425c8befdd5SWarner Losh struct ste_chain_data *cd; 1426c8befdd5SWarner Losh struct ste_list_data *ld; 1427a1b2c209SPyun YongHyeon int error, i; 1428c8befdd5SWarner Losh 1429fabbaac5SPyun YongHyeon sc->ste_int_rx_act = 0; 1430c8befdd5SWarner Losh cd = &sc->ste_cdata; 1431a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1432a1b2c209SPyun YongHyeon bzero(ld->ste_rx_list, STE_RX_LIST_SZ); 1433c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1434c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; 1435a1b2c209SPyun YongHyeon error = ste_newbuf(sc, &cd->ste_rx_chain[i]); 1436a1b2c209SPyun YongHyeon if (error != 0) 1437a1b2c209SPyun YongHyeon return (error); 1438c8befdd5SWarner Losh if (i == (STE_RX_LIST_CNT - 1)) { 1439a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[0]; 1440e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next = 1441e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr + 1442e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * 0)); 1443c8befdd5SWarner Losh } else { 1444a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[i + 1]; 1445e036acc0SPyun YongHyeon ld->ste_rx_list[i].ste_next = 1446e036acc0SPyun YongHyeon htole32(ld->ste_rx_list_paddr + 1447e036acc0SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * (i + 1))); 1448c8befdd5SWarner Losh } 1449c8befdd5SWarner Losh } 1450c8befdd5SWarner Losh 1451c8befdd5SWarner Losh cd->ste_rx_head = &cd->ste_rx_chain[0]; 1452a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 1453a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 1454a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1455c8befdd5SWarner Losh 1456c8befdd5SWarner Losh return (0); 1457c8befdd5SWarner Losh } 1458c8befdd5SWarner Losh 1459c8befdd5SWarner Losh static void 146060270842SPyun YongHyeon ste_init_tx_list(struct ste_softc *sc) 1461c8befdd5SWarner Losh { 1462c8befdd5SWarner Losh struct ste_chain_data *cd; 1463c8befdd5SWarner Losh struct ste_list_data *ld; 1464c8befdd5SWarner Losh int i; 1465c8befdd5SWarner Losh 1466c8befdd5SWarner Losh cd = &sc->ste_cdata; 1467a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1468a1b2c209SPyun YongHyeon bzero(ld->ste_tx_list, STE_TX_LIST_SZ); 1469c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1470c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; 1471a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_mbuf = NULL; 1472a1b2c209SPyun YongHyeon if (i == (STE_TX_LIST_CNT - 1)) { 1473a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[0]; 1474a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1475a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1476a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * 0))); 1477a1b2c209SPyun YongHyeon } else { 1478a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1]; 1479a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1480a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1481a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * (i + 1)))); 1482a1b2c209SPyun YongHyeon } 1483c8befdd5SWarner Losh } 1484c8befdd5SWarner Losh 1485a1b2c209SPyun YongHyeon cd->ste_last_tx = NULL; 1486c8befdd5SWarner Losh cd->ste_tx_prod = 0; 1487c8befdd5SWarner Losh cd->ste_tx_cons = 0; 1488a1b2c209SPyun YongHyeon cd->ste_tx_cnt = 0; 1489a1b2c209SPyun YongHyeon 1490a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1491a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1492a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1493c8befdd5SWarner Losh } 1494c8befdd5SWarner Losh 1495c8befdd5SWarner Losh static void 149660270842SPyun YongHyeon ste_init(void *xsc) 1497c8befdd5SWarner Losh { 1498c8befdd5SWarner Losh struct ste_softc *sc; 1499c8befdd5SWarner Losh 1500c8befdd5SWarner Losh sc = xsc; 1501c8befdd5SWarner Losh STE_LOCK(sc); 1502c8befdd5SWarner Losh ste_init_locked(sc); 1503c8befdd5SWarner Losh STE_UNLOCK(sc); 1504c8befdd5SWarner Losh } 1505c8befdd5SWarner Losh 1506c8befdd5SWarner Losh static void 150760270842SPyun YongHyeon ste_init_locked(struct ste_softc *sc) 1508c8befdd5SWarner Losh { 1509c8befdd5SWarner Losh struct ifnet *ifp; 1510bfe051bdSPyun YongHyeon struct mii_data *mii; 1511b4c170e1SPyun YongHyeon uint8_t val; 1512f2632c3bSPyun YongHyeon int i; 1513c8befdd5SWarner Losh 1514c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1515c8befdd5SWarner Losh ifp = sc->ste_ifp; 1516bfe051bdSPyun YongHyeon mii = device_get_softc(sc->ste_miibus); 1517c8befdd5SWarner Losh 151855d7003eSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 151955d7003eSPyun YongHyeon return; 152055d7003eSPyun YongHyeon 1521c8befdd5SWarner Losh ste_stop(sc); 15228d9f6dd9SPyun YongHyeon /* Reset the chip to a known state. */ 15238d9f6dd9SPyun YongHyeon ste_reset(sc); 1524c8befdd5SWarner Losh 1525c8befdd5SWarner Losh /* Init our MAC address */ 1526c8befdd5SWarner Losh for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 1527c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PAR0 + i, 1528c8befdd5SWarner Losh ((IF_LLADDR(sc->ste_ifp)[i] & 0xff) | 1529c8befdd5SWarner Losh IF_LLADDR(sc->ste_ifp)[i + 1] << 8)); 1530c8befdd5SWarner Losh } 1531c8befdd5SWarner Losh 1532c8befdd5SWarner Losh /* Init RX list */ 1533a1b2c209SPyun YongHyeon if (ste_init_rx_list(sc) != 0) { 1534c8befdd5SWarner Losh device_printf(sc->ste_dev, 1535c8befdd5SWarner Losh "initialization failed: no memory for RX buffers\n"); 1536c8befdd5SWarner Losh ste_stop(sc); 1537c8befdd5SWarner Losh return; 1538c8befdd5SWarner Losh } 1539c8befdd5SWarner Losh 1540c8befdd5SWarner Losh /* Set RX polling interval */ 1541c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); 1542c8befdd5SWarner Losh 1543c8befdd5SWarner Losh /* Init TX descriptors */ 1544c8befdd5SWarner Losh ste_init_tx_list(sc); 1545c8befdd5SWarner Losh 1546b4c170e1SPyun YongHyeon /* Clear and disable WOL. */ 1547b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT); 1548b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB | 1549b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB); 1550b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val); 1551b4c170e1SPyun YongHyeon 1552c8befdd5SWarner Losh /* Set the TX freethresh value */ 1553c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); 1554c8befdd5SWarner Losh 1555c8befdd5SWarner Losh /* Set the TX start threshold for best performance. */ 1556c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 1557c8befdd5SWarner Losh 1558c8befdd5SWarner Losh /* Set the TX reclaim threshold. */ 1559c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); 1560c8befdd5SWarner Losh 1561931ec15aSPyun YongHyeon /* Accept VLAN length packets */ 1562931ec15aSPyun YongHyeon CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN); 1563931ec15aSPyun YongHyeon 1564c8befdd5SWarner Losh /* Set up the RX filter. */ 1565931ec15aSPyun YongHyeon ste_rxfilter(sc); 1566c8befdd5SWarner Losh 1567c8befdd5SWarner Losh /* Load the address of the RX list. */ 1568c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1569c8befdd5SWarner Losh ste_wait(sc); 1570c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 1571a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_rx_list_paddr)); 1572c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1573c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1574c8befdd5SWarner Losh 1575a1b2c209SPyun YongHyeon /* Set TX polling interval(defer until we TX first packet). */ 1576c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 1577c8befdd5SWarner Losh 1578c8befdd5SWarner Losh /* Load address of the TX list */ 1579c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1580c8befdd5SWarner Losh ste_wait(sc); 1581c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 1582c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1583c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1584c8befdd5SWarner Losh ste_wait(sc); 1585fabbaac5SPyun YongHyeon /* Select 3.2us timer. */ 1586fabbaac5SPyun YongHyeon STE_CLRBIT4(sc, STE_DMACTL, STE_DMACTL_COUNTDOWN_SPEED | 1587fabbaac5SPyun YongHyeon STE_DMACTL_COUNTDOWN_MODE); 1588c8befdd5SWarner Losh 1589c8befdd5SWarner Losh /* Enable receiver and transmitter */ 1590c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL0, 0); 1591c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL1, 0); 1592c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); 1593c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); 1594c8befdd5SWarner Losh 1595c8befdd5SWarner Losh /* Enable stats counters. */ 1596c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); 15978657caa6SPyun YongHyeon /* Clear stats counters. */ 15988657caa6SPyun YongHyeon ste_stats_clear(sc); 1599c8befdd5SWarner Losh 1600fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0); 1601c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_ISR, 0xFFFF); 1602c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1603c8befdd5SWarner Losh /* Disable interrupts if we are polling. */ 1604c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1605c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1606c8befdd5SWarner Losh else 1607c8befdd5SWarner Losh #endif 1608c8befdd5SWarner Losh /* Enable interrupts. */ 1609c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1610c8befdd5SWarner Losh 1611bfe051bdSPyun YongHyeon sc->ste_flags &= ~STE_FLAG_LINK; 1612bfe051bdSPyun YongHyeon /* Switch to the current media. */ 1613bfe051bdSPyun YongHyeon mii_mediachg(mii); 1614c8befdd5SWarner Losh 1615c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 1616c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1617c8befdd5SWarner Losh 161810f695eeSPyun YongHyeon callout_reset(&sc->ste_callout, hz, ste_tick, sc); 1619c8befdd5SWarner Losh } 1620c8befdd5SWarner Losh 1621c8befdd5SWarner Losh static void 162260270842SPyun YongHyeon ste_stop(struct ste_softc *sc) 1623c8befdd5SWarner Losh { 1624c8befdd5SWarner Losh struct ifnet *ifp; 1625a1b2c209SPyun YongHyeon struct ste_chain_onefrag *cur_rx; 1626a1b2c209SPyun YongHyeon struct ste_chain *cur_tx; 16278d9f6dd9SPyun YongHyeon uint32_t val; 1628f2632c3bSPyun YongHyeon int i; 1629c8befdd5SWarner Losh 1630c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1631c8befdd5SWarner Losh ifp = sc->ste_ifp; 1632c8befdd5SWarner Losh 163310f695eeSPyun YongHyeon callout_stop(&sc->ste_callout); 163410f695eeSPyun YongHyeon sc->ste_timer = 0; 1635c8befdd5SWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE); 1636c8befdd5SWarner Losh 1637c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1638fabbaac5SPyun YongHyeon CSR_WRITE_2(sc, STE_COUNTDOWN, 0); 16398d9f6dd9SPyun YongHyeon /* Stop pending DMA. */ 16408d9f6dd9SPyun YongHyeon val = CSR_READ_4(sc, STE_DMACTL); 16418d9f6dd9SPyun YongHyeon val |= STE_DMACTL_TXDMA_STALL | STE_DMACTL_RXDMA_STALL; 16428d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_DMACTL, val); 1643c8befdd5SWarner Losh ste_wait(sc); 16448d9f6dd9SPyun YongHyeon /* Disable auto-polling. */ 16458d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 0); 16468d9f6dd9SPyun YongHyeon CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 16478d9f6dd9SPyun YongHyeon /* Nullify DMA address to stop any further DMA. */ 16488d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 0); 16498d9f6dd9SPyun YongHyeon CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 16508d9f6dd9SPyun YongHyeon /* Stop TX/RX MAC. */ 16518d9f6dd9SPyun YongHyeon val = CSR_READ_2(sc, STE_MACCTL1); 16528d9f6dd9SPyun YongHyeon val |= STE_MACCTL1_TX_DISABLE | STE_MACCTL1_RX_DISABLE | 16538d9f6dd9SPyun YongHyeon STE_MACCTL1_STATS_DISABLE; 16548d9f6dd9SPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, val); 16558d9f6dd9SPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) { 16568d9f6dd9SPyun YongHyeon DELAY(10); 16578d9f6dd9SPyun YongHyeon if ((CSR_READ_2(sc, STE_MACCTL1) & (STE_MACCTL1_TX_DISABLE | 16588d9f6dd9SPyun YongHyeon STE_MACCTL1_RX_DISABLE | STE_MACCTL1_STATS_DISABLE)) == 0) 16598d9f6dd9SPyun YongHyeon break; 16608d9f6dd9SPyun YongHyeon } 16618d9f6dd9SPyun YongHyeon if (i == STE_TIMEOUT) 16628d9f6dd9SPyun YongHyeon device_printf(sc->ste_dev, "Stopping MAC timed out\n"); 16638d9f6dd9SPyun YongHyeon /* Acknowledge any pending interrupts. */ 16648d9f6dd9SPyun YongHyeon CSR_READ_2(sc, STE_ISR_ACK); 16658d9f6dd9SPyun YongHyeon ste_stats_update(sc); 1666c8befdd5SWarner Losh 1667c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1668a1b2c209SPyun YongHyeon cur_rx = &sc->ste_cdata.ste_rx_chain[i]; 1669a1b2c209SPyun YongHyeon if (cur_rx->ste_mbuf != NULL) { 1670a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, 1671a1b2c209SPyun YongHyeon cur_rx->ste_map, BUS_DMASYNC_POSTREAD); 1672a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, 1673a1b2c209SPyun YongHyeon cur_rx->ste_map); 1674a1b2c209SPyun YongHyeon m_freem(cur_rx->ste_mbuf); 1675a1b2c209SPyun YongHyeon cur_rx->ste_mbuf = NULL; 1676c8befdd5SWarner Losh } 1677c8befdd5SWarner Losh } 1678c8befdd5SWarner Losh 1679c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1680a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[i]; 1681a1b2c209SPyun YongHyeon if (cur_tx->ste_mbuf != NULL) { 1682a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, 1683a1b2c209SPyun YongHyeon cur_tx->ste_map, BUS_DMASYNC_POSTWRITE); 1684a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, 1685a1b2c209SPyun YongHyeon cur_tx->ste_map); 1686a1b2c209SPyun YongHyeon m_freem(cur_tx->ste_mbuf); 1687a1b2c209SPyun YongHyeon cur_tx->ste_mbuf = NULL; 1688c8befdd5SWarner Losh } 1689c8befdd5SWarner Losh } 1690c8befdd5SWarner Losh } 1691c8befdd5SWarner Losh 1692c8befdd5SWarner Losh static void 169360270842SPyun YongHyeon ste_reset(struct ste_softc *sc) 1694c8befdd5SWarner Losh { 169538c52cfdSPyun YongHyeon uint32_t ctl; 1696c8befdd5SWarner Losh int i; 1697c8befdd5SWarner Losh 169838c52cfdSPyun YongHyeon ctl = CSR_READ_4(sc, STE_ASICCTL); 169938c52cfdSPyun YongHyeon ctl |= STE_ASICCTL_GLOBAL_RESET | STE_ASICCTL_RX_RESET | 1700c8befdd5SWarner Losh STE_ASICCTL_TX_RESET | STE_ASICCTL_DMA_RESET | 1701c8befdd5SWarner Losh STE_ASICCTL_FIFO_RESET | STE_ASICCTL_NETWORK_RESET | 1702c8befdd5SWarner Losh STE_ASICCTL_AUTOINIT_RESET |STE_ASICCTL_HOST_RESET | 170338c52cfdSPyun YongHyeon STE_ASICCTL_EXTRESET_RESET; 170438c52cfdSPyun YongHyeon CSR_WRITE_4(sc, STE_ASICCTL, ctl); 170538c52cfdSPyun YongHyeon CSR_READ_4(sc, STE_ASICCTL); 170638c52cfdSPyun YongHyeon /* 170738c52cfdSPyun YongHyeon * Due to the need of accessing EEPROM controller can take 170838c52cfdSPyun YongHyeon * up to 1ms to complete the global reset. 170938c52cfdSPyun YongHyeon */ 171038c52cfdSPyun YongHyeon DELAY(1000); 1711c8befdd5SWarner Losh 1712c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 1713c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) 1714c8befdd5SWarner Losh break; 171538c52cfdSPyun YongHyeon DELAY(10); 1716c8befdd5SWarner Losh } 1717c8befdd5SWarner Losh 1718c8befdd5SWarner Losh if (i == STE_TIMEOUT) 1719c8befdd5SWarner Losh device_printf(sc->ste_dev, "global reset never completed\n"); 1720c8befdd5SWarner Losh } 1721c8befdd5SWarner Losh 172281598b3eSPyun YongHyeon static void 172381598b3eSPyun YongHyeon ste_restart_tx(struct ste_softc *sc) 172481598b3eSPyun YongHyeon { 172581598b3eSPyun YongHyeon uint16_t mac; 172681598b3eSPyun YongHyeon int i; 172781598b3eSPyun YongHyeon 172881598b3eSPyun YongHyeon for (i = 0; i < STE_TIMEOUT; i++) { 172981598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1); 173081598b3eSPyun YongHyeon mac |= STE_MACCTL1_TX_ENABLE; 173181598b3eSPyun YongHyeon CSR_WRITE_2(sc, STE_MACCTL1, mac); 173281598b3eSPyun YongHyeon mac = CSR_READ_2(sc, STE_MACCTL1); 173381598b3eSPyun YongHyeon if ((mac & STE_MACCTL1_TX_ENABLED) != 0) 173481598b3eSPyun YongHyeon break; 173581598b3eSPyun YongHyeon DELAY(10); 173681598b3eSPyun YongHyeon } 173781598b3eSPyun YongHyeon 173881598b3eSPyun YongHyeon if (i == STE_TIMEOUT) 173981598b3eSPyun YongHyeon device_printf(sc->ste_dev, "starting Tx failed"); 174081598b3eSPyun YongHyeon } 174181598b3eSPyun YongHyeon 1742c8befdd5SWarner Losh static int 174360270842SPyun YongHyeon ste_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 1744c8befdd5SWarner Losh { 1745c8befdd5SWarner Losh struct ste_softc *sc; 1746c8befdd5SWarner Losh struct ifreq *ifr; 1747c8befdd5SWarner Losh struct mii_data *mii; 1748b4c170e1SPyun YongHyeon int error = 0, mask; 1749c8befdd5SWarner Losh 1750c8befdd5SWarner Losh sc = ifp->if_softc; 1751c8befdd5SWarner Losh ifr = (struct ifreq *)data; 1752c8befdd5SWarner Losh 1753c8befdd5SWarner Losh switch (command) { 1754c8befdd5SWarner Losh case SIOCSIFFLAGS: 1755c8befdd5SWarner Losh STE_LOCK(sc); 1756931ec15aSPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 1757931ec15aSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 1758931ec15aSPyun YongHyeon ((ifp->if_flags ^ sc->ste_if_flags) & 1759931ec15aSPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0) 1760931ec15aSPyun YongHyeon ste_rxfilter(sc); 1761931ec15aSPyun YongHyeon else 1762c8befdd5SWarner Losh ste_init_locked(sc); 1763931ec15aSPyun YongHyeon } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1764c8befdd5SWarner Losh ste_stop(sc); 1765c8befdd5SWarner Losh sc->ste_if_flags = ifp->if_flags; 1766c8befdd5SWarner Losh STE_UNLOCK(sc); 1767c8befdd5SWarner Losh break; 1768c8befdd5SWarner Losh case SIOCADDMULTI: 1769c8befdd5SWarner Losh case SIOCDELMULTI: 1770c8befdd5SWarner Losh STE_LOCK(sc); 1771931ec15aSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 1772931ec15aSPyun YongHyeon ste_rxfilter(sc); 1773c8befdd5SWarner Losh STE_UNLOCK(sc); 1774c8befdd5SWarner Losh break; 1775c8befdd5SWarner Losh case SIOCGIFMEDIA: 1776c8befdd5SWarner Losh case SIOCSIFMEDIA: 1777c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 1778c8befdd5SWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1779c8befdd5SWarner Losh break; 1780c8befdd5SWarner Losh case SIOCSIFCAP: 1781c8befdd5SWarner Losh STE_LOCK(sc); 1782b4c170e1SPyun YongHyeon mask = ifr->ifr_reqcap ^ ifp->if_capenable; 1783b4c170e1SPyun YongHyeon #ifdef DEVICE_POLLING 1784b4c170e1SPyun YongHyeon if ((mask & IFCAP_POLLING) != 0 && 1785b4c170e1SPyun YongHyeon (IFCAP_POLLING & ifp->if_capabilities) != 0) { 1786b4c170e1SPyun YongHyeon ifp->if_capenable ^= IFCAP_POLLING; 1787b4c170e1SPyun YongHyeon if ((IFCAP_POLLING & ifp->if_capenable) != 0) { 1788b4c170e1SPyun YongHyeon error = ether_poll_register(ste_poll, ifp); 1789b4c170e1SPyun YongHyeon if (error != 0) { 1790c8befdd5SWarner Losh STE_UNLOCK(sc); 1791b4c170e1SPyun YongHyeon break; 1792c8befdd5SWarner Losh } 1793b4c170e1SPyun YongHyeon /* Disable interrupts. */ 1794b4c170e1SPyun YongHyeon CSR_WRITE_2(sc, STE_IMR, 0); 1795b4c170e1SPyun YongHyeon } else { 1796c8befdd5SWarner Losh error = ether_poll_deregister(ifp); 1797c8befdd5SWarner Losh /* Enable interrupts. */ 1798c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1799b4c170e1SPyun YongHyeon } 1800c8befdd5SWarner Losh } 1801c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 1802b4c170e1SPyun YongHyeon if ((mask & IFCAP_WOL_MAGIC) != 0 && 1803b4c170e1SPyun YongHyeon (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) 1804b4c170e1SPyun YongHyeon ifp->if_capenable ^= IFCAP_WOL_MAGIC; 1805b4c170e1SPyun YongHyeon STE_UNLOCK(sc); 1806c8befdd5SWarner Losh break; 1807c8befdd5SWarner Losh default: 1808c8befdd5SWarner Losh error = ether_ioctl(ifp, command, data); 1809c8befdd5SWarner Losh break; 1810c8befdd5SWarner Losh } 1811c8befdd5SWarner Losh 1812c8befdd5SWarner Losh return (error); 1813c8befdd5SWarner Losh } 1814c8befdd5SWarner Losh 1815c8befdd5SWarner Losh static int 1816a1b2c209SPyun YongHyeon ste_encap(struct ste_softc *sc, struct mbuf **m_head, struct ste_chain *txc) 1817c8befdd5SWarner Losh { 1818a1b2c209SPyun YongHyeon struct ste_frag *frag; 1819c8befdd5SWarner Losh struct mbuf *m; 1820a1b2c209SPyun YongHyeon struct ste_desc *desc; 1821a1b2c209SPyun YongHyeon bus_dma_segment_t txsegs[STE_MAXFRAGS]; 1822a1b2c209SPyun YongHyeon int error, i, nsegs; 1823c8befdd5SWarner Losh 1824a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 1825a1b2c209SPyun YongHyeon M_ASSERTPKTHDR((*m_head)); 1826c8befdd5SWarner Losh 1827a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1828a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1829a1b2c209SPyun YongHyeon if (error == EFBIG) { 1830c6499eccSGleb Smirnoff m = m_collapse(*m_head, M_NOWAIT, STE_MAXFRAGS); 1831a1b2c209SPyun YongHyeon if (m == NULL) { 1832a1b2c209SPyun YongHyeon m_freem(*m_head); 1833a1b2c209SPyun YongHyeon *m_head = NULL; 1834a1b2c209SPyun YongHyeon return (ENOMEM); 1835c8befdd5SWarner Losh } 1836a1b2c209SPyun YongHyeon *m_head = m; 1837a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1838a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1839a1b2c209SPyun YongHyeon if (error != 0) { 1840a1b2c209SPyun YongHyeon m_freem(*m_head); 1841a1b2c209SPyun YongHyeon *m_head = NULL; 1842a1b2c209SPyun YongHyeon return (error); 1843c8befdd5SWarner Losh } 1844a1b2c209SPyun YongHyeon } else if (error != 0) 1845a1b2c209SPyun YongHyeon return (error); 1846a1b2c209SPyun YongHyeon if (nsegs == 0) { 1847a1b2c209SPyun YongHyeon m_freem(*m_head); 1848a1b2c209SPyun YongHyeon *m_head = NULL; 1849a1b2c209SPyun YongHyeon return (EIO); 1850a1b2c209SPyun YongHyeon } 1851a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, txc->ste_map, 1852a1b2c209SPyun YongHyeon BUS_DMASYNC_PREWRITE); 1853c8befdd5SWarner Losh 1854a1b2c209SPyun YongHyeon desc = txc->ste_ptr; 1855a1b2c209SPyun YongHyeon for (i = 0; i < nsegs; i++) { 1856a1b2c209SPyun YongHyeon frag = &desc->ste_frags[i]; 1857a1b2c209SPyun YongHyeon frag->ste_addr = htole32(STE_ADDR_LO(txsegs[i].ds_addr)); 1858a1b2c209SPyun YongHyeon frag->ste_len = htole32(txsegs[i].ds_len); 1859a1b2c209SPyun YongHyeon } 1860a1b2c209SPyun YongHyeon desc->ste_frags[i - 1].ste_len |= htole32(STE_FRAG_LAST); 1861c8befdd5SWarner Losh /* 1862a1b2c209SPyun YongHyeon * Because we use Tx polling we can't chain multiple 1863a1b2c209SPyun YongHyeon * Tx descriptors here. Otherwise we race with controller. 1864c8befdd5SWarner Losh */ 1865a1b2c209SPyun YongHyeon desc->ste_next = 0; 1866ae49e7a6SPyun YongHyeon if ((sc->ste_cdata.ste_tx_prod % STE_TX_INTR_FRAMES) == 0) 1867ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS | 1868ae49e7a6SPyun YongHyeon STE_TXCTL_DMAINTR); 1869ae49e7a6SPyun YongHyeon else 1870ae49e7a6SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS); 1871a1b2c209SPyun YongHyeon txc->ste_mbuf = *m_head; 1872a1b2c209SPyun YongHyeon STE_INC(sc->ste_cdata.ste_tx_prod, STE_TX_LIST_CNT); 1873a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt++; 1874c8befdd5SWarner Losh 1875c8befdd5SWarner Losh return (0); 1876c8befdd5SWarner Losh } 1877c8befdd5SWarner Losh 1878c8befdd5SWarner Losh static void 187960270842SPyun YongHyeon ste_start(struct ifnet *ifp) 1880c8befdd5SWarner Losh { 1881c8befdd5SWarner Losh struct ste_softc *sc; 1882c8befdd5SWarner Losh 1883c8befdd5SWarner Losh sc = ifp->if_softc; 1884c8befdd5SWarner Losh STE_LOCK(sc); 1885c8befdd5SWarner Losh ste_start_locked(ifp); 1886c8befdd5SWarner Losh STE_UNLOCK(sc); 1887c8befdd5SWarner Losh } 1888c8befdd5SWarner Losh 1889c8befdd5SWarner Losh static void 189060270842SPyun YongHyeon ste_start_locked(struct ifnet *ifp) 1891c8befdd5SWarner Losh { 1892c8befdd5SWarner Losh struct ste_softc *sc; 1893c8befdd5SWarner Losh struct ste_chain *cur_tx; 1894f2632c3bSPyun YongHyeon struct mbuf *m_head = NULL; 1895a1b2c209SPyun YongHyeon int enq; 1896c8befdd5SWarner Losh 1897c8befdd5SWarner Losh sc = ifp->if_softc; 1898c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1899c8befdd5SWarner Losh 19004465097bSPyun YongHyeon if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 19014465097bSPyun YongHyeon IFF_DRV_RUNNING || (sc->ste_flags & STE_FLAG_LINK) == 0) 1902c8befdd5SWarner Losh return; 1903c8befdd5SWarner Losh 1904a1b2c209SPyun YongHyeon for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { 1905a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == STE_TX_LIST_CNT - 1) { 1906c8befdd5SWarner Losh /* 1907a1b2c209SPyun YongHyeon * Controller may have cached copy of the last used 1908a1b2c209SPyun YongHyeon * next ptr so we have to reserve one TFD to avoid 1909a1b2c209SPyun YongHyeon * TFD overruns. 1910c8befdd5SWarner Losh */ 1911c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1912c8befdd5SWarner Losh break; 1913c8befdd5SWarner Losh } 1914c8befdd5SWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 1915c8befdd5SWarner Losh if (m_head == NULL) 1916c8befdd5SWarner Losh break; 1917a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[sc->ste_cdata.ste_tx_prod]; 1918a1b2c209SPyun YongHyeon if (ste_encap(sc, &m_head, cur_tx) != 0) { 1919a1b2c209SPyun YongHyeon if (m_head == NULL) 1920c8befdd5SWarner Losh break; 1921a1b2c209SPyun YongHyeon IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 1922a1b2c209SPyun YongHyeon break; 1923a1b2c209SPyun YongHyeon } 1924a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_last_tx == NULL) { 1925a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1926a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1927a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1928c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1929c8befdd5SWarner Losh ste_wait(sc); 1930c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 1931a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_tx_list_paddr)); 1932c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); 1933c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1934c8befdd5SWarner Losh ste_wait(sc); 1935c8befdd5SWarner Losh } else { 1936a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_ptr->ste_next = 1937a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_phys; 1938a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1939a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1940a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1941c8befdd5SWarner Losh } 1942a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx = cur_tx; 1943c8befdd5SWarner Losh 1944a1b2c209SPyun YongHyeon enq++; 1945c8befdd5SWarner Losh /* 1946c8befdd5SWarner Losh * If there's a BPF listener, bounce a copy of this frame 1947c8befdd5SWarner Losh * to him. 1948c8befdd5SWarner Losh */ 1949a1b2c209SPyun YongHyeon BPF_MTAP(ifp, m_head); 1950c8befdd5SWarner Losh } 1951a1b2c209SPyun YongHyeon 1952a1b2c209SPyun YongHyeon if (enq > 0) 1953a1b2c209SPyun YongHyeon sc->ste_timer = STE_TX_TIMEOUT; 1954c8befdd5SWarner Losh } 1955c8befdd5SWarner Losh 1956c8befdd5SWarner Losh static void 19577cf545d0SJohn Baldwin ste_watchdog(struct ste_softc *sc) 1958c8befdd5SWarner Losh { 19597cf545d0SJohn Baldwin struct ifnet *ifp; 1960c8befdd5SWarner Losh 19617cf545d0SJohn Baldwin ifp = sc->ste_ifp; 19627cf545d0SJohn Baldwin STE_LOCK_ASSERT(sc); 1963c8befdd5SWarner Losh 196410f695eeSPyun YongHyeon if (sc->ste_timer == 0 || --sc->ste_timer) 196510f695eeSPyun YongHyeon return; 196610f695eeSPyun YongHyeon 1967c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1968c8befdd5SWarner Losh if_printf(ifp, "watchdog timeout\n"); 1969c8befdd5SWarner Losh 1970c8befdd5SWarner Losh ste_txeof(sc); 197181598b3eSPyun YongHyeon ste_txeoc(sc); 1972a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 197355d7003eSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1974c8befdd5SWarner Losh ste_init_locked(sc); 1975c8befdd5SWarner Losh 1976c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1977c8befdd5SWarner Losh ste_start_locked(ifp); 1978c8befdd5SWarner Losh } 1979c8befdd5SWarner Losh 1980c8befdd5SWarner Losh static int 198160270842SPyun YongHyeon ste_shutdown(device_t dev) 1982c8befdd5SWarner Losh { 1983b4c170e1SPyun YongHyeon 1984b4c170e1SPyun YongHyeon return (ste_suspend(dev)); 1985b4c170e1SPyun YongHyeon } 1986b4c170e1SPyun YongHyeon 1987b4c170e1SPyun YongHyeon static int 1988b4c170e1SPyun YongHyeon ste_suspend(device_t dev) 1989b4c170e1SPyun YongHyeon { 1990c8befdd5SWarner Losh struct ste_softc *sc; 1991c8befdd5SWarner Losh 1992c8befdd5SWarner Losh sc = device_get_softc(dev); 1993c8befdd5SWarner Losh 1994c8befdd5SWarner Losh STE_LOCK(sc); 1995c8befdd5SWarner Losh ste_stop(sc); 1996b4c170e1SPyun YongHyeon ste_setwol(sc); 1997b4c170e1SPyun YongHyeon STE_UNLOCK(sc); 1998b4c170e1SPyun YongHyeon 1999b4c170e1SPyun YongHyeon return (0); 2000b4c170e1SPyun YongHyeon } 2001b4c170e1SPyun YongHyeon 2002b4c170e1SPyun YongHyeon static int 2003b4c170e1SPyun YongHyeon ste_resume(device_t dev) 2004b4c170e1SPyun YongHyeon { 2005b4c170e1SPyun YongHyeon struct ste_softc *sc; 2006b4c170e1SPyun YongHyeon struct ifnet *ifp; 2007b4c170e1SPyun YongHyeon int pmc; 2008b4c170e1SPyun YongHyeon uint16_t pmstat; 2009b4c170e1SPyun YongHyeon 2010b4c170e1SPyun YongHyeon sc = device_get_softc(dev); 2011b4c170e1SPyun YongHyeon STE_LOCK(sc); 20123b0a4aefSJohn Baldwin if (pci_find_cap(sc->ste_dev, PCIY_PMG, &pmc) == 0) { 2013b4c170e1SPyun YongHyeon /* Disable PME and clear PME status. */ 2014b4c170e1SPyun YongHyeon pmstat = pci_read_config(sc->ste_dev, 2015b4c170e1SPyun YongHyeon pmc + PCIR_POWER_STATUS, 2); 2016b4c170e1SPyun YongHyeon if ((pmstat & PCIM_PSTAT_PMEENABLE) != 0) { 2017b4c170e1SPyun YongHyeon pmstat &= ~PCIM_PSTAT_PMEENABLE; 2018b4c170e1SPyun YongHyeon pci_write_config(sc->ste_dev, 2019b4c170e1SPyun YongHyeon pmc + PCIR_POWER_STATUS, pmstat, 2); 2020b4c170e1SPyun YongHyeon } 2021b4c170e1SPyun YongHyeon } 2022b4c170e1SPyun YongHyeon ifp = sc->ste_ifp; 2023b4c170e1SPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 2024b4c170e1SPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 2025b4c170e1SPyun YongHyeon ste_init_locked(sc); 2026b4c170e1SPyun YongHyeon } 2027c8befdd5SWarner Losh STE_UNLOCK(sc); 2028c8befdd5SWarner Losh 2029c8befdd5SWarner Losh return (0); 2030c8befdd5SWarner Losh } 20318657caa6SPyun YongHyeon 20328657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 20338657caa6SPyun YongHyeon SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 20348657caa6SPyun YongHyeon #define STE_SYSCTL_STAT_ADD64(c, h, n, p, d) \ 20356dc7dc9aSMatthew D Fleming SYSCTL_ADD_UQUAD(c, h, OID_AUTO, n, CTLFLAG_RD, p, d) 20368657caa6SPyun YongHyeon 20378657caa6SPyun YongHyeon static void 20388657caa6SPyun YongHyeon ste_sysctl_node(struct ste_softc *sc) 20398657caa6SPyun YongHyeon { 20408657caa6SPyun YongHyeon struct sysctl_ctx_list *ctx; 20418657caa6SPyun YongHyeon struct sysctl_oid_list *child, *parent; 20428657caa6SPyun YongHyeon struct sysctl_oid *tree; 20438657caa6SPyun YongHyeon struct ste_hw_stats *stats; 20448657caa6SPyun YongHyeon 20458657caa6SPyun YongHyeon stats = &sc->ste_stats; 20468657caa6SPyun YongHyeon ctx = device_get_sysctl_ctx(sc->ste_dev); 20478657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->ste_dev)); 20488657caa6SPyun YongHyeon 2049fabbaac5SPyun YongHyeon SYSCTL_ADD_INT(ctx, child, OID_AUTO, "int_rx_mod", 2050fabbaac5SPyun YongHyeon CTLFLAG_RW, &sc->ste_int_rx_mod, 0, "ste RX interrupt moderation"); 2051fabbaac5SPyun YongHyeon /* Pull in device tunables. */ 2052fabbaac5SPyun YongHyeon sc->ste_int_rx_mod = STE_IM_RX_TIMER_DEFAULT; 2053fabbaac5SPyun YongHyeon resource_int_value(device_get_name(sc->ste_dev), 2054fabbaac5SPyun YongHyeon device_get_unit(sc->ste_dev), "int_rx_mod", &sc->ste_int_rx_mod); 2055fabbaac5SPyun YongHyeon 2056*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", 2057*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "STE statistics"); 20588657caa6SPyun YongHyeon parent = SYSCTL_CHILDREN(tree); 20598657caa6SPyun YongHyeon 20608657caa6SPyun YongHyeon /* Rx statistics. */ 2061*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", 2062*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Rx MAC statistics"); 20638657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree); 20648657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets", 20658657caa6SPyun YongHyeon &stats->rx_bytes, "Good octets"); 20668657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 20678657caa6SPyun YongHyeon &stats->rx_frames, "Good frames"); 20688657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 20698657caa6SPyun YongHyeon &stats->rx_bcast_frames, "Good broadcast frames"); 20708657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 20718657caa6SPyun YongHyeon &stats->rx_mcast_frames, "Good multicast frames"); 20728657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "lost_frames", 20738657caa6SPyun YongHyeon &stats->rx_lost_frames, "Lost frames"); 20748657caa6SPyun YongHyeon 20758657caa6SPyun YongHyeon /* Tx statistics. */ 2076*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", 2077*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Tx MAC statistics"); 20788657caa6SPyun YongHyeon child = SYSCTL_CHILDREN(tree); 20798657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD64(ctx, child, "good_octets", 20808657caa6SPyun YongHyeon &stats->tx_bytes, "Good octets"); 20818657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 20828657caa6SPyun YongHyeon &stats->tx_frames, "Good frames"); 20838657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 20848657caa6SPyun YongHyeon &stats->tx_bcast_frames, "Good broadcast frames"); 20858657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 20868657caa6SPyun YongHyeon &stats->tx_mcast_frames, "Good multicast frames"); 20878657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "carrier_errs", 20888657caa6SPyun YongHyeon &stats->tx_carrsense_errs, "Carrier sense errors"); 20898657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "single_colls", 20908657caa6SPyun YongHyeon &stats->tx_single_colls, "Single collisions"); 20918657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "multi_colls", 20928657caa6SPyun YongHyeon &stats->tx_multi_colls, "Multiple collisions"); 20938657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 20948657caa6SPyun YongHyeon &stats->tx_late_colls, "Late collisions"); 20958657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "defers", 20968657caa6SPyun YongHyeon &stats->tx_frames_defered, "Frames with deferrals"); 20978657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "excess_defers", 20988657caa6SPyun YongHyeon &stats->tx_excess_defers, "Frames with excessive derferrals"); 20998657caa6SPyun YongHyeon STE_SYSCTL_STAT_ADD32(ctx, child, "abort", 21008657caa6SPyun YongHyeon &stats->tx_abort, "Aborted frames due to Excessive collisions"); 21018657caa6SPyun YongHyeon } 21028657caa6SPyun YongHyeon 21038657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD32 21048657caa6SPyun YongHyeon #undef STE_SYSCTL_STAT_ADD64 2105b4c170e1SPyun YongHyeon 2106b4c170e1SPyun YongHyeon static void 2107b4c170e1SPyun YongHyeon ste_setwol(struct ste_softc *sc) 2108b4c170e1SPyun YongHyeon { 2109b4c170e1SPyun YongHyeon struct ifnet *ifp; 2110b4c170e1SPyun YongHyeon uint16_t pmstat; 2111b4c170e1SPyun YongHyeon uint8_t val; 2112b4c170e1SPyun YongHyeon int pmc; 2113b4c170e1SPyun YongHyeon 2114b4c170e1SPyun YongHyeon STE_LOCK_ASSERT(sc); 2115b4c170e1SPyun YongHyeon 21163b0a4aefSJohn Baldwin if (pci_find_cap(sc->ste_dev, PCIY_PMG, &pmc) != 0) { 2117b4c170e1SPyun YongHyeon /* Disable WOL. */ 2118b4c170e1SPyun YongHyeon CSR_READ_1(sc, STE_WAKE_EVENT); 2119b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, 0); 2120b4c170e1SPyun YongHyeon return; 2121b4c170e1SPyun YongHyeon } 2122b4c170e1SPyun YongHyeon 2123b4c170e1SPyun YongHyeon ifp = sc->ste_ifp; 2124b4c170e1SPyun YongHyeon val = CSR_READ_1(sc, STE_WAKE_EVENT); 2125b4c170e1SPyun YongHyeon val &= ~(STE_WAKEEVENT_WAKEPKT_ENB | STE_WAKEEVENT_MAGICPKT_ENB | 2126b4c170e1SPyun YongHyeon STE_WAKEEVENT_LINKEVT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB); 2127b4c170e1SPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2128b4c170e1SPyun YongHyeon val |= STE_WAKEEVENT_MAGICPKT_ENB | STE_WAKEEVENT_WAKEONLAN_ENB; 2129b4c170e1SPyun YongHyeon CSR_WRITE_1(sc, STE_WAKE_EVENT, val); 2130b4c170e1SPyun YongHyeon /* Request PME. */ 2131b4c170e1SPyun YongHyeon pmstat = pci_read_config(sc->ste_dev, pmc + PCIR_POWER_STATUS, 2); 2132b4c170e1SPyun YongHyeon pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); 2133b4c170e1SPyun YongHyeon if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) 2134b4c170e1SPyun YongHyeon pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; 2135b4c170e1SPyun YongHyeon pci_write_config(sc->ste_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); 2136b4c170e1SPyun YongHyeon } 2137