1c8befdd5SWarner Losh /*- 2c8befdd5SWarner Losh * Copyright (c) 1997, 1998, 1999 3c8befdd5SWarner Losh * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4c8befdd5SWarner Losh * 5c8befdd5SWarner Losh * Redistribution and use in source and binary forms, with or without 6c8befdd5SWarner Losh * modification, are permitted provided that the following conditions 7c8befdd5SWarner Losh * are met: 8c8befdd5SWarner Losh * 1. Redistributions of source code must retain the above copyright 9c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer. 10c8befdd5SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11c8befdd5SWarner Losh * notice, this list of conditions and the following disclaimer in the 12c8befdd5SWarner Losh * documentation and/or other materials provided with the distribution. 13c8befdd5SWarner Losh * 3. All advertising materials mentioning features or use of this software 14c8befdd5SWarner Losh * must display the following acknowledgement: 15c8befdd5SWarner Losh * This product includes software developed by Bill Paul. 16c8befdd5SWarner Losh * 4. Neither the name of the author nor the names of any co-contributors 17c8befdd5SWarner Losh * may be used to endorse or promote products derived from this software 18c8befdd5SWarner Losh * without specific prior written permission. 19c8befdd5SWarner Losh * 20c8befdd5SWarner Losh * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21c8befdd5SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22c8befdd5SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23c8befdd5SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24c8befdd5SWarner Losh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25c8befdd5SWarner Losh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26c8befdd5SWarner Losh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27c8befdd5SWarner Losh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28c8befdd5SWarner Losh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29c8befdd5SWarner Losh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30c8befdd5SWarner Losh * THE POSSIBILITY OF SUCH DAMAGE. 31c8befdd5SWarner Losh */ 32c8befdd5SWarner Losh 33c8befdd5SWarner Losh #include <sys/cdefs.h> 34c8befdd5SWarner Losh __FBSDID("$FreeBSD$"); 35c8befdd5SWarner Losh 36c8befdd5SWarner Losh #ifdef HAVE_KERNEL_OPTION_HEADERS 37c8befdd5SWarner Losh #include "opt_device_polling.h" 38c8befdd5SWarner Losh #endif 39c8befdd5SWarner Losh 40c8befdd5SWarner Losh #include <sys/param.h> 41c8befdd5SWarner Losh #include <sys/systm.h> 42a1b2c209SPyun YongHyeon #include <sys/bus.h> 43a1b2c209SPyun YongHyeon #include <sys/endian.h> 44c8befdd5SWarner Losh #include <sys/kernel.h> 45a1b2c209SPyun YongHyeon #include <sys/lock.h> 46a1b2c209SPyun YongHyeon #include <sys/malloc.h> 47a1b2c209SPyun YongHyeon #include <sys/mbuf.h> 48c8befdd5SWarner Losh #include <sys/module.h> 49a1b2c209SPyun YongHyeon #include <sys/rman.h> 50c8befdd5SWarner Losh #include <sys/socket.h> 51a1b2c209SPyun YongHyeon #include <sys/sockio.h> 52c8befdd5SWarner Losh #include <sys/sysctl.h> 53c8befdd5SWarner Losh 54a1b2c209SPyun YongHyeon #include <net/bpf.h> 55c8befdd5SWarner Losh #include <net/if.h> 56c8befdd5SWarner Losh #include <net/if_arp.h> 57c8befdd5SWarner Losh #include <net/ethernet.h> 58c8befdd5SWarner Losh #include <net/if_dl.h> 59c8befdd5SWarner Losh #include <net/if_media.h> 60c8befdd5SWarner Losh #include <net/if_types.h> 61c8befdd5SWarner Losh #include <net/if_vlan_var.h> 62c8befdd5SWarner Losh 63c8befdd5SWarner Losh #include <machine/bus.h> 64c8befdd5SWarner Losh #include <machine/resource.h> 65c8befdd5SWarner Losh 66c8befdd5SWarner Losh #include <dev/mii/mii.h> 67c8befdd5SWarner Losh #include <dev/mii/miivar.h> 68c8befdd5SWarner Losh 69c8befdd5SWarner Losh #include <dev/pci/pcireg.h> 70c8befdd5SWarner Losh #include <dev/pci/pcivar.h> 71c8befdd5SWarner Losh 72a1b2c209SPyun YongHyeon #include <dev/ste/if_stereg.h> 73a1b2c209SPyun YongHyeon 74c8befdd5SWarner Losh /* "device miibus" required. See GENERIC if you get errors here. */ 75c8befdd5SWarner Losh #include "miibus_if.h" 76c8befdd5SWarner Losh 77c8befdd5SWarner Losh #define STE_USEIOSPACE 78c8befdd5SWarner Losh 79c8befdd5SWarner Losh MODULE_DEPEND(ste, pci, 1, 1, 1); 80c8befdd5SWarner Losh MODULE_DEPEND(ste, ether, 1, 1, 1); 81c8befdd5SWarner Losh MODULE_DEPEND(ste, miibus, 1, 1, 1); 82c8befdd5SWarner Losh 83c8befdd5SWarner Losh /* 84c8befdd5SWarner Losh * Various supported device vendors/types and their names. 85c8befdd5SWarner Losh */ 86c8befdd5SWarner Losh static struct ste_type ste_devs[] = { 87c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_1, "Sundance ST201 10/100BaseTX" }, 88c8befdd5SWarner Losh { ST_VENDORID, ST_DEVICEID_ST201_2, "Sundance ST201 10/100BaseTX" }, 89c8befdd5SWarner Losh { DL_VENDORID, DL_DEVICEID_DL10050, "D-Link DL10050 10/100BaseTX" }, 90c8befdd5SWarner Losh { 0, 0, NULL } 91c8befdd5SWarner Losh }; 92c8befdd5SWarner Losh 93c8befdd5SWarner Losh static int ste_attach(device_t); 94c8befdd5SWarner Losh static int ste_detach(device_t); 95084dc54bSPyun YongHyeon static int ste_probe(device_t); 96c8befdd5SWarner Losh static int ste_shutdown(device_t); 97084dc54bSPyun YongHyeon 98a1b2c209SPyun YongHyeon static int ste_dma_alloc(struct ste_softc *); 99a1b2c209SPyun YongHyeon static void ste_dma_free(struct ste_softc *); 100a1b2c209SPyun YongHyeon static void ste_dmamap_cb(void *, bus_dma_segment_t *, int, int); 101084dc54bSPyun YongHyeon static int ste_eeprom_wait(struct ste_softc *); 102a1b2c209SPyun YongHyeon static int ste_encap(struct ste_softc *, struct mbuf **, 103a1b2c209SPyun YongHyeon struct ste_chain *); 104c8befdd5SWarner Losh static int ste_ifmedia_upd(struct ifnet *); 105c8befdd5SWarner Losh static void ste_ifmedia_upd_locked(struct ifnet *); 106c8befdd5SWarner Losh static void ste_ifmedia_sts(struct ifnet *, struct ifmediareq *); 107084dc54bSPyun YongHyeon static void ste_init(void *); 108084dc54bSPyun YongHyeon static void ste_init_locked(struct ste_softc *); 109c8befdd5SWarner Losh static int ste_init_rx_list(struct ste_softc *); 110c8befdd5SWarner Losh static void ste_init_tx_list(struct ste_softc *); 111084dc54bSPyun YongHyeon static void ste_intr(void *); 112084dc54bSPyun YongHyeon static int ste_ioctl(struct ifnet *, u_long, caddr_t); 113084dc54bSPyun YongHyeon static int ste_mii_readreg(struct ste_softc *, struct ste_mii_frame *); 114084dc54bSPyun YongHyeon static void ste_mii_send(struct ste_softc *, uint32_t, int); 115084dc54bSPyun YongHyeon static void ste_mii_sync(struct ste_softc *); 116084dc54bSPyun YongHyeon static int ste_mii_writereg(struct ste_softc *, struct ste_mii_frame *); 117084dc54bSPyun YongHyeon static int ste_miibus_readreg(device_t, int, int); 118084dc54bSPyun YongHyeon static void ste_miibus_statchg(device_t); 119084dc54bSPyun YongHyeon static int ste_miibus_writereg(device_t, int, int, int); 120a1b2c209SPyun YongHyeon static int ste_newbuf(struct ste_softc *, struct ste_chain_onefrag *); 121084dc54bSPyun YongHyeon static int ste_read_eeprom(struct ste_softc *, caddr_t, int, int, int); 122084dc54bSPyun YongHyeon static void ste_reset(struct ste_softc *); 123a1b2c209SPyun YongHyeon static int ste_rxeof(struct ste_softc *, int); 124084dc54bSPyun YongHyeon static void ste_setmulti(struct ste_softc *); 125084dc54bSPyun YongHyeon static void ste_start(struct ifnet *); 126084dc54bSPyun YongHyeon static void ste_start_locked(struct ifnet *); 127084dc54bSPyun YongHyeon static void ste_stats_update(void *); 128084dc54bSPyun YongHyeon static void ste_stop(struct ste_softc *); 129084dc54bSPyun YongHyeon static void ste_txeoc(struct ste_softc *); 130084dc54bSPyun YongHyeon static void ste_txeof(struct ste_softc *); 131084dc54bSPyun YongHyeon static void ste_wait(struct ste_softc *); 132084dc54bSPyun YongHyeon static void ste_watchdog(struct ste_softc *); 133c8befdd5SWarner Losh 134c8befdd5SWarner Losh #ifdef STE_USEIOSPACE 135c8befdd5SWarner Losh #define STE_RES SYS_RES_IOPORT 136c8befdd5SWarner Losh #define STE_RID STE_PCI_LOIO 137c8befdd5SWarner Losh #else 138c8befdd5SWarner Losh #define STE_RES SYS_RES_MEMORY 139c8befdd5SWarner Losh #define STE_RID STE_PCI_LOMEM 140c8befdd5SWarner Losh #endif 141c8befdd5SWarner Losh 142c8befdd5SWarner Losh static device_method_t ste_methods[] = { 143c8befdd5SWarner Losh /* Device interface */ 144c8befdd5SWarner Losh DEVMETHOD(device_probe, ste_probe), 145c8befdd5SWarner Losh DEVMETHOD(device_attach, ste_attach), 146c8befdd5SWarner Losh DEVMETHOD(device_detach, ste_detach), 147c8befdd5SWarner Losh DEVMETHOD(device_shutdown, ste_shutdown), 148c8befdd5SWarner Losh 149c8befdd5SWarner Losh /* bus interface */ 150c8befdd5SWarner Losh DEVMETHOD(bus_print_child, bus_generic_print_child), 151c8befdd5SWarner Losh DEVMETHOD(bus_driver_added, bus_generic_driver_added), 152c8befdd5SWarner Losh 153c8befdd5SWarner Losh /* MII interface */ 154c8befdd5SWarner Losh DEVMETHOD(miibus_readreg, ste_miibus_readreg), 155c8befdd5SWarner Losh DEVMETHOD(miibus_writereg, ste_miibus_writereg), 156c8befdd5SWarner Losh DEVMETHOD(miibus_statchg, ste_miibus_statchg), 157c8befdd5SWarner Losh 158c8befdd5SWarner Losh { 0, 0 } 159c8befdd5SWarner Losh }; 160c8befdd5SWarner Losh 161c8befdd5SWarner Losh static driver_t ste_driver = { 162c8befdd5SWarner Losh "ste", 163c8befdd5SWarner Losh ste_methods, 164c8befdd5SWarner Losh sizeof(struct ste_softc) 165c8befdd5SWarner Losh }; 166c8befdd5SWarner Losh 167c8befdd5SWarner Losh static devclass_t ste_devclass; 168c8befdd5SWarner Losh 169c8befdd5SWarner Losh DRIVER_MODULE(ste, pci, ste_driver, ste_devclass, 0, 0); 170c8befdd5SWarner Losh DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); 171c8befdd5SWarner Losh 172c8befdd5SWarner Losh #define STE_SETBIT4(sc, reg, x) \ 173c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) 174c8befdd5SWarner Losh 175c8befdd5SWarner Losh #define STE_CLRBIT4(sc, reg, x) \ 176c8befdd5SWarner Losh CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) 177c8befdd5SWarner Losh 178c8befdd5SWarner Losh #define STE_SETBIT2(sc, reg, x) \ 179c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x)) 180c8befdd5SWarner Losh 181c8befdd5SWarner Losh #define STE_CLRBIT2(sc, reg, x) \ 182c8befdd5SWarner Losh CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x)) 183c8befdd5SWarner Losh 184c8befdd5SWarner Losh #define STE_SETBIT1(sc, reg, x) \ 185c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) 186c8befdd5SWarner Losh 187c8befdd5SWarner Losh #define STE_CLRBIT1(sc, reg, x) \ 188c8befdd5SWarner Losh CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x)) 189c8befdd5SWarner Losh 190c8befdd5SWarner Losh 191c8befdd5SWarner Losh #define MII_SET(x) STE_SETBIT1(sc, STE_PHYCTL, x) 192c8befdd5SWarner Losh #define MII_CLR(x) STE_CLRBIT1(sc, STE_PHYCTL, x) 193c8befdd5SWarner Losh 194c8befdd5SWarner Losh /* 195c8befdd5SWarner Losh * Sync the PHYs by setting data bit and strobing the clock 32 times. 196c8befdd5SWarner Losh */ 197c8befdd5SWarner Losh static void 19860270842SPyun YongHyeon ste_mii_sync(struct ste_softc *sc) 199c8befdd5SWarner Losh { 20042306cb0SPyun YongHyeon int i; 201c8befdd5SWarner Losh 202c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR|STE_PHYCTL_MDATA); 203c8befdd5SWarner Losh 204c8befdd5SWarner Losh for (i = 0; i < 32; i++) { 205c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 206c8befdd5SWarner Losh DELAY(1); 207c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 208c8befdd5SWarner Losh DELAY(1); 209c8befdd5SWarner Losh } 210c8befdd5SWarner Losh } 211c8befdd5SWarner Losh 212c8befdd5SWarner Losh /* 213c8befdd5SWarner Losh * Clock a series of bits through the MII. 214c8befdd5SWarner Losh */ 215c8befdd5SWarner Losh static void 21656af54f2SPyun YongHyeon ste_mii_send(struct ste_softc *sc, uint32_t bits, int cnt) 217c8befdd5SWarner Losh { 218c8befdd5SWarner Losh int i; 219c8befdd5SWarner Losh 220c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 221c8befdd5SWarner Losh 222c8befdd5SWarner Losh for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 223c8befdd5SWarner Losh if (bits & i) { 224c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDATA); 225c8befdd5SWarner Losh } else { 226c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDATA); 227c8befdd5SWarner Losh } 228c8befdd5SWarner Losh DELAY(1); 229c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 230c8befdd5SWarner Losh DELAY(1); 231c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 232c8befdd5SWarner Losh } 233c8befdd5SWarner Losh } 234c8befdd5SWarner Losh 235c8befdd5SWarner Losh /* 236c8befdd5SWarner Losh * Read an PHY register through the MII. 237c8befdd5SWarner Losh */ 238c8befdd5SWarner Losh static int 23960270842SPyun YongHyeon ste_mii_readreg(struct ste_softc *sc, struct ste_mii_frame *frame) 240c8befdd5SWarner Losh { 241c8befdd5SWarner Losh int i, ack; 242c8befdd5SWarner Losh 243c8befdd5SWarner Losh /* 244c8befdd5SWarner Losh * Set up frame for RX. 245c8befdd5SWarner Losh */ 246c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 247c8befdd5SWarner Losh frame->mii_opcode = STE_MII_READOP; 248c8befdd5SWarner Losh frame->mii_turnaround = 0; 249c8befdd5SWarner Losh frame->mii_data = 0; 250c8befdd5SWarner Losh 251c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PHYCTL, 0); 252c8befdd5SWarner Losh /* 253c8befdd5SWarner Losh * Turn on data xmit. 254c8befdd5SWarner Losh */ 255c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 256c8befdd5SWarner Losh 257c8befdd5SWarner Losh ste_mii_sync(sc); 258c8befdd5SWarner Losh 259c8befdd5SWarner Losh /* 260c8befdd5SWarner Losh * Send command/address info. 261c8befdd5SWarner Losh */ 262c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 263c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 264c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 265c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 266c8befdd5SWarner Losh 267c8befdd5SWarner Losh /* Turn off xmit. */ 268c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 269c8befdd5SWarner Losh 270c8befdd5SWarner Losh /* Idle bit */ 271c8befdd5SWarner Losh MII_CLR((STE_PHYCTL_MCLK|STE_PHYCTL_MDATA)); 272c8befdd5SWarner Losh DELAY(1); 273c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 274c8befdd5SWarner Losh DELAY(1); 275c8befdd5SWarner Losh 276c8befdd5SWarner Losh /* Check for ack */ 277c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 278c8befdd5SWarner Losh DELAY(1); 279c8befdd5SWarner Losh ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; 280c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 281c8befdd5SWarner Losh DELAY(1); 282c8befdd5SWarner Losh 283c8befdd5SWarner Losh /* 284c8befdd5SWarner Losh * Now try reading data bits. If the ack failed, we still 285c8befdd5SWarner Losh * need to clock through 16 cycles to keep the PHY(s) in sync. 286c8befdd5SWarner Losh */ 287c8befdd5SWarner Losh if (ack) { 288c8befdd5SWarner Losh for (i = 0; i < 16; i++) { 289c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 290c8befdd5SWarner Losh DELAY(1); 291c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 292c8befdd5SWarner Losh DELAY(1); 293c8befdd5SWarner Losh } 294c8befdd5SWarner Losh goto fail; 295c8befdd5SWarner Losh } 296c8befdd5SWarner Losh 297c8befdd5SWarner Losh for (i = 0x8000; i; i >>= 1) { 298c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 299c8befdd5SWarner Losh DELAY(1); 300c8befdd5SWarner Losh if (!ack) { 301c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA) 302c8befdd5SWarner Losh frame->mii_data |= i; 303c8befdd5SWarner Losh DELAY(1); 304c8befdd5SWarner Losh } 305c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 306c8befdd5SWarner Losh DELAY(1); 307c8befdd5SWarner Losh } 308c8befdd5SWarner Losh 309c8befdd5SWarner Losh fail: 310c8befdd5SWarner Losh 311c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 312c8befdd5SWarner Losh DELAY(1); 313c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 314c8befdd5SWarner Losh DELAY(1); 315c8befdd5SWarner Losh 316c8befdd5SWarner Losh if (ack) 317c8befdd5SWarner Losh return (1); 318c8befdd5SWarner Losh return (0); 319c8befdd5SWarner Losh } 320c8befdd5SWarner Losh 321c8befdd5SWarner Losh /* 322c8befdd5SWarner Losh * Write to a PHY register through the MII. 323c8befdd5SWarner Losh */ 324c8befdd5SWarner Losh static int 32560270842SPyun YongHyeon ste_mii_writereg(struct ste_softc *sc, struct ste_mii_frame *frame) 326c8befdd5SWarner Losh { 327c8befdd5SWarner Losh 328c8befdd5SWarner Losh /* 329c8befdd5SWarner Losh * Set up frame for TX. 330c8befdd5SWarner Losh */ 331c8befdd5SWarner Losh 332c8befdd5SWarner Losh frame->mii_stdelim = STE_MII_STARTDELIM; 333c8befdd5SWarner Losh frame->mii_opcode = STE_MII_WRITEOP; 334c8befdd5SWarner Losh frame->mii_turnaround = STE_MII_TURNAROUND; 335c8befdd5SWarner Losh 336c8befdd5SWarner Losh /* 337c8befdd5SWarner Losh * Turn on data output. 338c8befdd5SWarner Losh */ 339c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MDIR); 340c8befdd5SWarner Losh 341c8befdd5SWarner Losh ste_mii_sync(sc); 342c8befdd5SWarner Losh 343c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_stdelim, 2); 344c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_opcode, 2); 345c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_phyaddr, 5); 346c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_regaddr, 5); 347c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_turnaround, 2); 348c8befdd5SWarner Losh ste_mii_send(sc, frame->mii_data, 16); 349c8befdd5SWarner Losh 350c8befdd5SWarner Losh /* Idle bit. */ 351c8befdd5SWarner Losh MII_SET(STE_PHYCTL_MCLK); 352c8befdd5SWarner Losh DELAY(1); 353c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MCLK); 354c8befdd5SWarner Losh DELAY(1); 355c8befdd5SWarner Losh 356c8befdd5SWarner Losh /* 357c8befdd5SWarner Losh * Turn off xmit. 358c8befdd5SWarner Losh */ 359c8befdd5SWarner Losh MII_CLR(STE_PHYCTL_MDIR); 360c8befdd5SWarner Losh 361c8befdd5SWarner Losh return (0); 362c8befdd5SWarner Losh } 363c8befdd5SWarner Losh 364c8befdd5SWarner Losh static int 36560270842SPyun YongHyeon ste_miibus_readreg(device_t dev, int phy, int reg) 366c8befdd5SWarner Losh { 367c8befdd5SWarner Losh struct ste_softc *sc; 368c8befdd5SWarner Losh struct ste_mii_frame frame; 369c8befdd5SWarner Losh 370c8befdd5SWarner Losh sc = device_get_softc(dev); 371c8befdd5SWarner Losh 372c8befdd5SWarner Losh if ( sc->ste_one_phy && phy != 0 ) 373c8befdd5SWarner Losh return (0); 374c8befdd5SWarner Losh 375c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 376c8befdd5SWarner Losh 377c8befdd5SWarner Losh frame.mii_phyaddr = phy; 378c8befdd5SWarner Losh frame.mii_regaddr = reg; 379c8befdd5SWarner Losh ste_mii_readreg(sc, &frame); 380c8befdd5SWarner Losh 381c8befdd5SWarner Losh return (frame.mii_data); 382c8befdd5SWarner Losh } 383c8befdd5SWarner Losh 384c8befdd5SWarner Losh static int 38560270842SPyun YongHyeon ste_miibus_writereg(device_t dev, int phy, int reg, int data) 386c8befdd5SWarner Losh { 387c8befdd5SWarner Losh struct ste_softc *sc; 388c8befdd5SWarner Losh struct ste_mii_frame frame; 389c8befdd5SWarner Losh 390c8befdd5SWarner Losh sc = device_get_softc(dev); 391c8befdd5SWarner Losh bzero((char *)&frame, sizeof(frame)); 392c8befdd5SWarner Losh 393c8befdd5SWarner Losh frame.mii_phyaddr = phy; 394c8befdd5SWarner Losh frame.mii_regaddr = reg; 395c8befdd5SWarner Losh frame.mii_data = data; 396c8befdd5SWarner Losh 397c8befdd5SWarner Losh ste_mii_writereg(sc, &frame); 398c8befdd5SWarner Losh 399c8befdd5SWarner Losh return (0); 400c8befdd5SWarner Losh } 401c8befdd5SWarner Losh 402c8befdd5SWarner Losh static void 40360270842SPyun YongHyeon ste_miibus_statchg(device_t dev) 404c8befdd5SWarner Losh { 405c8befdd5SWarner Losh struct ste_softc *sc; 406c8befdd5SWarner Losh struct mii_data *mii; 407c8befdd5SWarner Losh 408c8befdd5SWarner Losh sc = device_get_softc(dev); 409c8befdd5SWarner Losh 410c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 411c8befdd5SWarner Losh 412c8befdd5SWarner Losh if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 413c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); 414c8befdd5SWarner Losh } else { 415c8befdd5SWarner Losh STE_CLRBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); 416c8befdd5SWarner Losh } 417c8befdd5SWarner Losh } 418c8befdd5SWarner Losh 419c8befdd5SWarner Losh static int 42060270842SPyun YongHyeon ste_ifmedia_upd(struct ifnet *ifp) 421c8befdd5SWarner Losh { 422c8befdd5SWarner Losh struct ste_softc *sc; 423c8befdd5SWarner Losh 424c8befdd5SWarner Losh sc = ifp->if_softc; 425c8befdd5SWarner Losh STE_LOCK(sc); 426c8befdd5SWarner Losh ste_ifmedia_upd_locked(ifp); 427c8befdd5SWarner Losh STE_UNLOCK(sc); 428c8befdd5SWarner Losh 429c8befdd5SWarner Losh return (0); 430c8befdd5SWarner Losh } 431c8befdd5SWarner Losh 432c8befdd5SWarner Losh static void 43360270842SPyun YongHyeon ste_ifmedia_upd_locked(struct ifnet *ifp) 434c8befdd5SWarner Losh { 435c8befdd5SWarner Losh struct ste_softc *sc; 436c8befdd5SWarner Losh struct mii_data *mii; 437c8befdd5SWarner Losh 438c8befdd5SWarner Losh sc = ifp->if_softc; 439c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 440c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 441c8befdd5SWarner Losh sc->ste_link = 0; 442c8befdd5SWarner Losh if (mii->mii_instance) { 443c8befdd5SWarner Losh struct mii_softc *miisc; 444c8befdd5SWarner Losh LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 445c8befdd5SWarner Losh mii_phy_reset(miisc); 446c8befdd5SWarner Losh } 447c8befdd5SWarner Losh mii_mediachg(mii); 448c8befdd5SWarner Losh } 449c8befdd5SWarner Losh 450c8befdd5SWarner Losh static void 45160270842SPyun YongHyeon ste_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 452c8befdd5SWarner Losh { 453c8befdd5SWarner Losh struct ste_softc *sc; 454c8befdd5SWarner Losh struct mii_data *mii; 455c8befdd5SWarner Losh 456c8befdd5SWarner Losh sc = ifp->if_softc; 457c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 458c8befdd5SWarner Losh 459c8befdd5SWarner Losh STE_LOCK(sc); 460c8befdd5SWarner Losh mii_pollstat(mii); 461c8befdd5SWarner Losh ifmr->ifm_active = mii->mii_media_active; 462c8befdd5SWarner Losh ifmr->ifm_status = mii->mii_media_status; 463c8befdd5SWarner Losh STE_UNLOCK(sc); 464c8befdd5SWarner Losh } 465c8befdd5SWarner Losh 466c8befdd5SWarner Losh static void 46760270842SPyun YongHyeon ste_wait(struct ste_softc *sc) 468c8befdd5SWarner Losh { 46942306cb0SPyun YongHyeon int i; 470c8befdd5SWarner Losh 471c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 472c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) 473c8befdd5SWarner Losh break; 4741bf71544SPyun YongHyeon DELAY(1); 475c8befdd5SWarner Losh } 476c8befdd5SWarner Losh 477c8befdd5SWarner Losh if (i == STE_TIMEOUT) 478c8befdd5SWarner Losh device_printf(sc->ste_dev, "command never completed!\n"); 479c8befdd5SWarner Losh } 480c8befdd5SWarner Losh 481c8befdd5SWarner Losh /* 482c8befdd5SWarner Losh * The EEPROM is slow: give it time to come ready after issuing 483c8befdd5SWarner Losh * it a command. 484c8befdd5SWarner Losh */ 485c8befdd5SWarner Losh static int 48660270842SPyun YongHyeon ste_eeprom_wait(struct ste_softc *sc) 487c8befdd5SWarner Losh { 488c8befdd5SWarner Losh int i; 489c8befdd5SWarner Losh 490c8befdd5SWarner Losh DELAY(1000); 491c8befdd5SWarner Losh 492c8befdd5SWarner Losh for (i = 0; i < 100; i++) { 493c8befdd5SWarner Losh if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) 494c8befdd5SWarner Losh DELAY(1000); 495c8befdd5SWarner Losh else 496c8befdd5SWarner Losh break; 497c8befdd5SWarner Losh } 498c8befdd5SWarner Losh 499c8befdd5SWarner Losh if (i == 100) { 500c8befdd5SWarner Losh device_printf(sc->ste_dev, "eeprom failed to come ready\n"); 501c8befdd5SWarner Losh return (1); 502c8befdd5SWarner Losh } 503c8befdd5SWarner Losh 504c8befdd5SWarner Losh return (0); 505c8befdd5SWarner Losh } 506c8befdd5SWarner Losh 507c8befdd5SWarner Losh /* 508c8befdd5SWarner Losh * Read a sequence of words from the EEPROM. Note that ethernet address 509c8befdd5SWarner Losh * data is stored in the EEPROM in network byte order. 510c8befdd5SWarner Losh */ 511c8befdd5SWarner Losh static int 51260270842SPyun YongHyeon ste_read_eeprom(struct ste_softc *sc, caddr_t dest, int off, int cnt, int swap) 513c8befdd5SWarner Losh { 514f2632c3bSPyun YongHyeon uint16_t word, *ptr; 515c8befdd5SWarner Losh int err = 0, i; 516c8befdd5SWarner Losh 517c8befdd5SWarner Losh if (ste_eeprom_wait(sc)) 518c8befdd5SWarner Losh return (1); 519c8befdd5SWarner Losh 520c8befdd5SWarner Losh for (i = 0; i < cnt; i++) { 521c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); 522c8befdd5SWarner Losh err = ste_eeprom_wait(sc); 523c8befdd5SWarner Losh if (err) 524c8befdd5SWarner Losh break; 525c8befdd5SWarner Losh word = CSR_READ_2(sc, STE_EEPROM_DATA); 52656af54f2SPyun YongHyeon ptr = (uint16_t *)(dest + (i * 2)); 527c8befdd5SWarner Losh if (swap) 528c8befdd5SWarner Losh *ptr = ntohs(word); 529c8befdd5SWarner Losh else 530c8befdd5SWarner Losh *ptr = word; 531c8befdd5SWarner Losh } 532c8befdd5SWarner Losh 533c8befdd5SWarner Losh return (err ? 1 : 0); 534c8befdd5SWarner Losh } 535c8befdd5SWarner Losh 536c8befdd5SWarner Losh static void 53760270842SPyun YongHyeon ste_setmulti(struct ste_softc *sc) 538c8befdd5SWarner Losh { 539c8befdd5SWarner Losh struct ifnet *ifp; 540c8befdd5SWarner Losh struct ifmultiaddr *ifma; 541f2632c3bSPyun YongHyeon uint32_t hashes[2] = { 0, 0 }; 542f2632c3bSPyun YongHyeon int h; 543c8befdd5SWarner Losh 544c8befdd5SWarner Losh ifp = sc->ste_ifp; 545c8befdd5SWarner Losh if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 546c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); 547c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); 548c8befdd5SWarner Losh return; 549c8befdd5SWarner Losh } 550c8befdd5SWarner Losh 551c8befdd5SWarner Losh /* first, zot all the existing hash bits */ 552c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, 0); 553c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, 0); 554c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, 0); 555c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, 0); 556c8befdd5SWarner Losh 557c8befdd5SWarner Losh /* now program new ones */ 558eb956cd0SRobert Watson if_maddr_rlock(ifp); 559c8befdd5SWarner Losh TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 560c8befdd5SWarner Losh if (ifma->ifma_addr->sa_family != AF_LINK) 561c8befdd5SWarner Losh continue; 562c8befdd5SWarner Losh h = ether_crc32_be(LLADDR((struct sockaddr_dl *) 563c8befdd5SWarner Losh ifma->ifma_addr), ETHER_ADDR_LEN) & 0x3F; 564c8befdd5SWarner Losh if (h < 32) 565c8befdd5SWarner Losh hashes[0] |= (1 << h); 566c8befdd5SWarner Losh else 567c8befdd5SWarner Losh hashes[1] |= (1 << (h - 32)); 568c8befdd5SWarner Losh } 569eb956cd0SRobert Watson if_maddr_runlock(ifp); 570c8befdd5SWarner Losh 571c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); 572c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); 573c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); 574c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); 575c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); 576c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); 577c8befdd5SWarner Losh } 578c8befdd5SWarner Losh 579c8befdd5SWarner Losh #ifdef DEVICE_POLLING 580c8befdd5SWarner Losh static poll_handler_t ste_poll, ste_poll_locked; 581c8befdd5SWarner Losh 5821abcdbd1SAttilio Rao static int 583c8befdd5SWarner Losh ste_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 584c8befdd5SWarner Losh { 585c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 5861abcdbd1SAttilio Rao int rx_npkts = 0; 587c8befdd5SWarner Losh 588c8befdd5SWarner Losh STE_LOCK(sc); 589c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 5901abcdbd1SAttilio Rao rx_npkts = ste_poll_locked(ifp, cmd, count); 591c8befdd5SWarner Losh STE_UNLOCK(sc); 5921abcdbd1SAttilio Rao return (rx_npkts); 593c8befdd5SWarner Losh } 594c8befdd5SWarner Losh 5951abcdbd1SAttilio Rao static int 596c8befdd5SWarner Losh ste_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) 597c8befdd5SWarner Losh { 598c8befdd5SWarner Losh struct ste_softc *sc = ifp->if_softc; 5991abcdbd1SAttilio Rao int rx_npkts; 600c8befdd5SWarner Losh 601c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 602c8befdd5SWarner Losh 603a1b2c209SPyun YongHyeon rx_npkts = ste_rxeof(sc, count); 604c8befdd5SWarner Losh ste_txeof(sc); 605c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 606c8befdd5SWarner Losh ste_start_locked(ifp); 607c8befdd5SWarner Losh 608c8befdd5SWarner Losh if (cmd == POLL_AND_CHECK_STATUS) { 60956af54f2SPyun YongHyeon uint16_t status; 610c8befdd5SWarner Losh 611c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 612c8befdd5SWarner Losh 613c8befdd5SWarner Losh if (status & STE_ISR_TX_DONE) 614c8befdd5SWarner Losh ste_txeoc(sc); 615c8befdd5SWarner Losh 616c8befdd5SWarner Losh if (status & STE_ISR_STATS_OFLOW) { 617c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 618c8befdd5SWarner Losh ste_stats_update(sc); 619c8befdd5SWarner Losh } 620c8befdd5SWarner Losh 621c8befdd5SWarner Losh if (status & STE_ISR_LINKEVENT) 622c8befdd5SWarner Losh mii_pollstat(device_get_softc(sc->ste_miibus)); 623c8befdd5SWarner Losh 624c8befdd5SWarner Losh if (status & STE_ISR_HOSTERR) { 625c8befdd5SWarner Losh ste_reset(sc); 626c8befdd5SWarner Losh ste_init_locked(sc); 627c8befdd5SWarner Losh } 628c8befdd5SWarner Losh } 6291abcdbd1SAttilio Rao return (rx_npkts); 630c8befdd5SWarner Losh } 631c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 632c8befdd5SWarner Losh 633c8befdd5SWarner Losh static void 63460270842SPyun YongHyeon ste_intr(void *xsc) 635c8befdd5SWarner Losh { 636c8befdd5SWarner Losh struct ste_softc *sc; 637c8befdd5SWarner Losh struct ifnet *ifp; 63856af54f2SPyun YongHyeon uint16_t status; 639c8befdd5SWarner Losh 640c8befdd5SWarner Losh sc = xsc; 641c8befdd5SWarner Losh STE_LOCK(sc); 642c8befdd5SWarner Losh ifp = sc->ste_ifp; 643c8befdd5SWarner Losh 644c8befdd5SWarner Losh #ifdef DEVICE_POLLING 645c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) { 646c8befdd5SWarner Losh STE_UNLOCK(sc); 647c8befdd5SWarner Losh return; 648c8befdd5SWarner Losh } 649c8befdd5SWarner Losh #endif 650c8befdd5SWarner Losh 651c8befdd5SWarner Losh /* See if this is really our interrupt. */ 652c8befdd5SWarner Losh if (!(CSR_READ_2(sc, STE_ISR) & STE_ISR_INTLATCH)) { 653c8befdd5SWarner Losh STE_UNLOCK(sc); 654c8befdd5SWarner Losh return; 655c8befdd5SWarner Losh } 656c8befdd5SWarner Losh 657c8befdd5SWarner Losh for (;;) { 658c8befdd5SWarner Losh status = CSR_READ_2(sc, STE_ISR_ACK); 659c8befdd5SWarner Losh 660c8befdd5SWarner Losh if (!(status & STE_INTRS)) 661c8befdd5SWarner Losh break; 662c8befdd5SWarner Losh 663a1b2c209SPyun YongHyeon if (status & STE_ISR_RX_DMADONE) 664a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 665c8befdd5SWarner Losh 666c8befdd5SWarner Losh if (status & STE_ISR_TX_DMADONE) 667c8befdd5SWarner Losh ste_txeof(sc); 668c8befdd5SWarner Losh 669c8befdd5SWarner Losh if (status & STE_ISR_TX_DONE) 670c8befdd5SWarner Losh ste_txeoc(sc); 671c8befdd5SWarner Losh 672c8befdd5SWarner Losh if (status & STE_ISR_STATS_OFLOW) { 673c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 674c8befdd5SWarner Losh ste_stats_update(sc); 675c8befdd5SWarner Losh } 676c8befdd5SWarner Losh 677c8befdd5SWarner Losh if (status & STE_ISR_LINKEVENT) 678c8befdd5SWarner Losh mii_pollstat(device_get_softc(sc->ste_miibus)); 679c8befdd5SWarner Losh 680c8befdd5SWarner Losh 681c8befdd5SWarner Losh if (status & STE_ISR_HOSTERR) { 682c8befdd5SWarner Losh ste_reset(sc); 683c8befdd5SWarner Losh ste_init_locked(sc); 684c8befdd5SWarner Losh } 685c8befdd5SWarner Losh } 686c8befdd5SWarner Losh 687c8befdd5SWarner Losh /* Re-enable interrupts */ 688c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 689c8befdd5SWarner Losh 690c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 691c8befdd5SWarner Losh ste_start_locked(ifp); 692c8befdd5SWarner Losh 693c8befdd5SWarner Losh STE_UNLOCK(sc); 694c8befdd5SWarner Losh } 695c8befdd5SWarner Losh 696c8befdd5SWarner Losh /* 697c8befdd5SWarner Losh * A frame has been uploaded: pass the resulting mbuf chain up to 698c8befdd5SWarner Losh * the higher level protocols. 699c8befdd5SWarner Losh */ 7001abcdbd1SAttilio Rao static int 701a1b2c209SPyun YongHyeon ste_rxeof(struct ste_softc *sc, int count) 702c8befdd5SWarner Losh { 703c8befdd5SWarner Losh struct mbuf *m; 704c8befdd5SWarner Losh struct ifnet *ifp; 705c8befdd5SWarner Losh struct ste_chain_onefrag *cur_rx; 70656af54f2SPyun YongHyeon uint32_t rxstat; 707a1b2c209SPyun YongHyeon int total_len, rx_npkts; 708c8befdd5SWarner Losh 709c8befdd5SWarner Losh ifp = sc->ste_ifp; 710c8befdd5SWarner Losh 711a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 712a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 713a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 714c8befdd5SWarner Losh 715c8befdd5SWarner Losh cur_rx = sc->ste_cdata.ste_rx_head; 716a1b2c209SPyun YongHyeon for (rx_npkts = 0; rx_npkts < STE_RX_LIST_CNT; rx_npkts++, 717a1b2c209SPyun YongHyeon cur_rx = cur_rx->ste_next) { 718a1b2c209SPyun YongHyeon rxstat = le32toh(cur_rx->ste_ptr->ste_status); 719a1b2c209SPyun YongHyeon if ((rxstat & STE_RXSTAT_DMADONE) == 0) 720a1b2c209SPyun YongHyeon break; 721a1b2c209SPyun YongHyeon #ifdef DEVICE_POLLING 722a1b2c209SPyun YongHyeon if (ifp->if_capenable & IFCAP_POLLING) { 723a1b2c209SPyun YongHyeon if (count == 0) 724a1b2c209SPyun YongHyeon break; 725a1b2c209SPyun YongHyeon count--; 726a1b2c209SPyun YongHyeon } 727a1b2c209SPyun YongHyeon #endif 728a1b2c209SPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 729a1b2c209SPyun YongHyeon break; 730c8befdd5SWarner Losh /* 731c8befdd5SWarner Losh * If an error occurs, update stats, clear the 732c8befdd5SWarner Losh * status word and leave the mbuf cluster in place: 733c8befdd5SWarner Losh * it should simply get re-used next time this descriptor 734c8befdd5SWarner Losh * comes up in the ring. 735c8befdd5SWarner Losh */ 736c8befdd5SWarner Losh if (rxstat & STE_RXSTAT_FRAME_ERR) { 737c8befdd5SWarner Losh ifp->if_ierrors++; 738c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 739c8befdd5SWarner Losh continue; 740c8befdd5SWarner Losh } 741c8befdd5SWarner Losh 742c8befdd5SWarner Losh /* No errors; receive the packet. */ 743c8befdd5SWarner Losh m = cur_rx->ste_mbuf; 744a1b2c209SPyun YongHyeon total_len = STE_RX_BYTES(rxstat); 745c8befdd5SWarner Losh 746c8befdd5SWarner Losh /* 747c8befdd5SWarner Losh * Try to conjure up a new mbuf cluster. If that 748c8befdd5SWarner Losh * fails, it means we have an out of memory condition and 749c8befdd5SWarner Losh * should leave the buffer in place and continue. This will 750c8befdd5SWarner Losh * result in a lost packet, but there's little else we 751c8befdd5SWarner Losh * can do in this situation. 752c8befdd5SWarner Losh */ 753a1b2c209SPyun YongHyeon if (ste_newbuf(sc, cur_rx) != 0) { 754c8befdd5SWarner Losh ifp->if_ierrors++; 755c8befdd5SWarner Losh cur_rx->ste_ptr->ste_status = 0; 756c8befdd5SWarner Losh continue; 757c8befdd5SWarner Losh } 758c8befdd5SWarner Losh 759c8befdd5SWarner Losh m->m_pkthdr.rcvif = ifp; 760c8befdd5SWarner Losh m->m_pkthdr.len = m->m_len = total_len; 761c8befdd5SWarner Losh 762c8befdd5SWarner Losh ifp->if_ipackets++; 763c8befdd5SWarner Losh STE_UNLOCK(sc); 764c8befdd5SWarner Losh (*ifp->if_input)(ifp, m); 765c8befdd5SWarner Losh STE_LOCK(sc); 766a1b2c209SPyun YongHyeon } 767c8befdd5SWarner Losh 768a1b2c209SPyun YongHyeon if (rx_npkts > 0) { 769a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_head = cur_rx; 770a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 771a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 772a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 773c8befdd5SWarner Losh } 774c8befdd5SWarner Losh 7751abcdbd1SAttilio Rao return (rx_npkts); 776c8befdd5SWarner Losh } 777c8befdd5SWarner Losh 778c8befdd5SWarner Losh static void 77960270842SPyun YongHyeon ste_txeoc(struct ste_softc *sc) 780c8befdd5SWarner Losh { 781c8befdd5SWarner Losh struct ifnet *ifp; 782f2632c3bSPyun YongHyeon uint8_t txstat; 783c8befdd5SWarner Losh 784c8befdd5SWarner Losh ifp = sc->ste_ifp; 785c8befdd5SWarner Losh 786c8befdd5SWarner Losh while ((txstat = CSR_READ_1(sc, STE_TX_STATUS)) & 787c8befdd5SWarner Losh STE_TXSTATUS_TXDONE) { 788c8befdd5SWarner Losh if (txstat & STE_TXSTATUS_UNDERRUN || 789c8befdd5SWarner Losh txstat & STE_TXSTATUS_EXCESSCOLLS || 790c8befdd5SWarner Losh txstat & STE_TXSTATUS_RECLAIMERR) { 791c8befdd5SWarner Losh ifp->if_oerrors++; 792c8befdd5SWarner Losh device_printf(sc->ste_dev, 793c8befdd5SWarner Losh "transmission error: %x\n", txstat); 794c8befdd5SWarner Losh 795c8befdd5SWarner Losh ste_reset(sc); 796c8befdd5SWarner Losh ste_init_locked(sc); 797c8befdd5SWarner Losh 798c8befdd5SWarner Losh if (txstat & STE_TXSTATUS_UNDERRUN && 799c8befdd5SWarner Losh sc->ste_tx_thresh < STE_PACKET_SIZE) { 800c8befdd5SWarner Losh sc->ste_tx_thresh += STE_MIN_FRAMELEN; 801c8befdd5SWarner Losh device_printf(sc->ste_dev, 802c8befdd5SWarner Losh "tx underrun, increasing tx" 803c8befdd5SWarner Losh " start threshold to %d bytes\n", 804c8befdd5SWarner Losh sc->ste_tx_thresh); 805c8befdd5SWarner Losh } 806c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 807c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_RECLAIM_THRESH, 808c8befdd5SWarner Losh (STE_PACKET_SIZE >> 4)); 809c8befdd5SWarner Losh } 810c8befdd5SWarner Losh ste_init_locked(sc); 811c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STATUS, txstat); 812c8befdd5SWarner Losh } 813c8befdd5SWarner Losh } 814c8befdd5SWarner Losh 815c8befdd5SWarner Losh static void 81660270842SPyun YongHyeon ste_txeof(struct ste_softc *sc) 817c8befdd5SWarner Losh { 818c8befdd5SWarner Losh struct ifnet *ifp; 819f2632c3bSPyun YongHyeon struct ste_chain *cur_tx; 820a1b2c209SPyun YongHyeon uint32_t txstat; 821c8befdd5SWarner Losh int idx; 822c8befdd5SWarner Losh 823a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 824c8befdd5SWarner Losh 825a1b2c209SPyun YongHyeon ifp = sc->ste_ifp; 826c8befdd5SWarner Losh idx = sc->ste_cdata.ste_tx_cons; 827a1b2c209SPyun YongHyeon if (idx == sc->ste_cdata.ste_tx_prod) 828a1b2c209SPyun YongHyeon return; 829a1b2c209SPyun YongHyeon 830a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 831a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 832a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 833a1b2c209SPyun YongHyeon 834c8befdd5SWarner Losh while (idx != sc->ste_cdata.ste_tx_prod) { 835c8befdd5SWarner Losh cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; 836a1b2c209SPyun YongHyeon txstat = le32toh(cur_tx->ste_ptr->ste_ctl); 837a1b2c209SPyun YongHyeon if ((txstat & STE_TXCTL_DMADONE) == 0) 838c8befdd5SWarner Losh break; 839a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map, 840a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTWRITE); 841a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, cur_tx->ste_map); 842a1b2c209SPyun YongHyeon KASSERT(cur_tx->ste_mbuf != NULL, 843a1b2c209SPyun YongHyeon ("%s: freeing NULL mbuf!\n", __func__)); 844c8befdd5SWarner Losh m_freem(cur_tx->ste_mbuf); 845c8befdd5SWarner Losh cur_tx->ste_mbuf = NULL; 846c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 847c8befdd5SWarner Losh ifp->if_opackets++; 848a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt--; 849c8befdd5SWarner Losh STE_INC(idx, STE_TX_LIST_CNT); 850c8befdd5SWarner Losh } 851c8befdd5SWarner Losh 852c8befdd5SWarner Losh sc->ste_cdata.ste_tx_cons = idx; 853a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == 0) 8547cf545d0SJohn Baldwin sc->ste_timer = 0; 855c8befdd5SWarner Losh } 856c8befdd5SWarner Losh 857c8befdd5SWarner Losh static void 85860270842SPyun YongHyeon ste_stats_update(void *xsc) 859c8befdd5SWarner Losh { 860c8befdd5SWarner Losh struct ste_softc *sc; 861c8befdd5SWarner Losh struct ifnet *ifp; 862c8befdd5SWarner Losh struct mii_data *mii; 863c8befdd5SWarner Losh 864c8befdd5SWarner Losh sc = xsc; 865c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 866c8befdd5SWarner Losh 867c8befdd5SWarner Losh ifp = sc->ste_ifp; 868c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 869c8befdd5SWarner Losh 870c8befdd5SWarner Losh ifp->if_collisions += CSR_READ_1(sc, STE_LATE_COLLS) 871c8befdd5SWarner Losh + CSR_READ_1(sc, STE_MULTI_COLLS) 872c8befdd5SWarner Losh + CSR_READ_1(sc, STE_SINGLE_COLLS); 873c8befdd5SWarner Losh 874c8befdd5SWarner Losh if (!sc->ste_link) { 875c8befdd5SWarner Losh mii_pollstat(mii); 876c8befdd5SWarner Losh if (mii->mii_media_status & IFM_ACTIVE && 877c8befdd5SWarner Losh IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 878c8befdd5SWarner Losh sc->ste_link++; 879c8befdd5SWarner Losh /* 880c8befdd5SWarner Losh * we don't get a call-back on re-init so do it 881c8befdd5SWarner Losh * otherwise we get stuck in the wrong link state 882c8befdd5SWarner Losh */ 883c8befdd5SWarner Losh ste_miibus_statchg(sc->ste_dev); 884c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 885c8befdd5SWarner Losh ste_start_locked(ifp); 886c8befdd5SWarner Losh } 887c8befdd5SWarner Losh } 888c8befdd5SWarner Losh 8897cf545d0SJohn Baldwin if (sc->ste_timer > 0 && --sc->ste_timer == 0) 8907cf545d0SJohn Baldwin ste_watchdog(sc); 891c8befdd5SWarner Losh callout_reset(&sc->ste_stat_callout, hz, ste_stats_update, sc); 892c8befdd5SWarner Losh } 893c8befdd5SWarner Losh 894c8befdd5SWarner Losh 895c8befdd5SWarner Losh /* 896c8befdd5SWarner Losh * Probe for a Sundance ST201 chip. Check the PCI vendor and device 897c8befdd5SWarner Losh * IDs against our list and return a device name if we find a match. 898c8befdd5SWarner Losh */ 899c8befdd5SWarner Losh static int 90060270842SPyun YongHyeon ste_probe(device_t dev) 901c8befdd5SWarner Losh { 902c8befdd5SWarner Losh struct ste_type *t; 903c8befdd5SWarner Losh 904c8befdd5SWarner Losh t = ste_devs; 905c8befdd5SWarner Losh 906c8befdd5SWarner Losh while (t->ste_name != NULL) { 907c8befdd5SWarner Losh if ((pci_get_vendor(dev) == t->ste_vid) && 908c8befdd5SWarner Losh (pci_get_device(dev) == t->ste_did)) { 909c8befdd5SWarner Losh device_set_desc(dev, t->ste_name); 910c8befdd5SWarner Losh return (BUS_PROBE_DEFAULT); 911c8befdd5SWarner Losh } 912c8befdd5SWarner Losh t++; 913c8befdd5SWarner Losh } 914c8befdd5SWarner Losh 915c8befdd5SWarner Losh return (ENXIO); 916c8befdd5SWarner Losh } 917c8befdd5SWarner Losh 918c8befdd5SWarner Losh /* 919c8befdd5SWarner Losh * Attach the interface. Allocate softc structures, do ifmedia 920c8befdd5SWarner Losh * setup and ethernet/BPF attach. 921c8befdd5SWarner Losh */ 922c8befdd5SWarner Losh static int 92360270842SPyun YongHyeon ste_attach(device_t dev) 924c8befdd5SWarner Losh { 925c8befdd5SWarner Losh struct ste_softc *sc; 926c8befdd5SWarner Losh struct ifnet *ifp; 927c8befdd5SWarner Losh u_char eaddr[6]; 928f2632c3bSPyun YongHyeon int error = 0, rid; 929c8befdd5SWarner Losh 930c8befdd5SWarner Losh sc = device_get_softc(dev); 931c8befdd5SWarner Losh sc->ste_dev = dev; 932c8befdd5SWarner Losh 933c8befdd5SWarner Losh /* 934c8befdd5SWarner Losh * Only use one PHY since this chip reports multiple 935c8befdd5SWarner Losh * Note on the DFE-550 the PHY is at 1 on the DFE-580 936c8befdd5SWarner Losh * it is at 0 & 1. It is rev 0x12. 937c8befdd5SWarner Losh */ 938c8befdd5SWarner Losh if (pci_get_vendor(dev) == DL_VENDORID && 939c8befdd5SWarner Losh pci_get_device(dev) == DL_DEVICEID_DL10050 && 940c8befdd5SWarner Losh pci_get_revid(dev) == 0x12 ) 941c8befdd5SWarner Losh sc->ste_one_phy = 1; 942c8befdd5SWarner Losh 943c8befdd5SWarner Losh mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 944c8befdd5SWarner Losh MTX_DEF); 945c8befdd5SWarner Losh /* 946c8befdd5SWarner Losh * Map control/status registers. 947c8befdd5SWarner Losh */ 948c8befdd5SWarner Losh pci_enable_busmaster(dev); 949c8befdd5SWarner Losh 950c8befdd5SWarner Losh rid = STE_RID; 951c8befdd5SWarner Losh sc->ste_res = bus_alloc_resource_any(dev, STE_RES, &rid, RF_ACTIVE); 952c8befdd5SWarner Losh 953c8befdd5SWarner Losh if (sc->ste_res == NULL) { 954c8befdd5SWarner Losh device_printf(dev, "couldn't map ports/memory\n"); 955c8befdd5SWarner Losh error = ENXIO; 956c8befdd5SWarner Losh goto fail; 957c8befdd5SWarner Losh } 958c8befdd5SWarner Losh 959c8befdd5SWarner Losh sc->ste_btag = rman_get_bustag(sc->ste_res); 960c8befdd5SWarner Losh sc->ste_bhandle = rman_get_bushandle(sc->ste_res); 961c8befdd5SWarner Losh 962c8befdd5SWarner Losh /* Allocate interrupt */ 963c8befdd5SWarner Losh rid = 0; 964c8befdd5SWarner Losh sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 965c8befdd5SWarner Losh RF_SHAREABLE | RF_ACTIVE); 966c8befdd5SWarner Losh 967c8befdd5SWarner Losh if (sc->ste_irq == NULL) { 968c8befdd5SWarner Losh device_printf(dev, "couldn't map interrupt\n"); 969c8befdd5SWarner Losh error = ENXIO; 970c8befdd5SWarner Losh goto fail; 971c8befdd5SWarner Losh } 972c8befdd5SWarner Losh 973c8befdd5SWarner Losh callout_init_mtx(&sc->ste_stat_callout, &sc->ste_mtx, 0); 974c8befdd5SWarner Losh 975c8befdd5SWarner Losh /* Reset the adapter. */ 976c8befdd5SWarner Losh ste_reset(sc); 977c8befdd5SWarner Losh 978c8befdd5SWarner Losh /* 979c8befdd5SWarner Losh * Get station address from the EEPROM. 980c8befdd5SWarner Losh */ 981c8befdd5SWarner Losh if (ste_read_eeprom(sc, eaddr, 982c8befdd5SWarner Losh STE_EEADDR_NODE0, 3, 0)) { 983c8befdd5SWarner Losh device_printf(dev, "failed to read station address\n"); 984c8befdd5SWarner Losh error = ENXIO;; 985c8befdd5SWarner Losh goto fail; 986c8befdd5SWarner Losh } 987c8befdd5SWarner Losh 988a1b2c209SPyun YongHyeon if ((error = ste_dma_alloc(sc)) != 0) 989c8befdd5SWarner Losh goto fail; 990c8befdd5SWarner Losh 991c8befdd5SWarner Losh ifp = sc->ste_ifp = if_alloc(IFT_ETHER); 992c8befdd5SWarner Losh if (ifp == NULL) { 993c8befdd5SWarner Losh device_printf(dev, "can not if_alloc()\n"); 994c8befdd5SWarner Losh error = ENOSPC; 995c8befdd5SWarner Losh goto fail; 996c8befdd5SWarner Losh } 997c8befdd5SWarner Losh 998c8befdd5SWarner Losh /* Do MII setup. */ 999c8befdd5SWarner Losh if (mii_phy_probe(dev, &sc->ste_miibus, 1000c8befdd5SWarner Losh ste_ifmedia_upd, ste_ifmedia_sts)) { 1001c8befdd5SWarner Losh device_printf(dev, "MII without any phy!\n"); 1002c8befdd5SWarner Losh error = ENXIO; 1003c8befdd5SWarner Losh goto fail; 1004c8befdd5SWarner Losh } 1005c8befdd5SWarner Losh 1006c8befdd5SWarner Losh ifp->if_softc = sc; 1007c8befdd5SWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 1008c8befdd5SWarner Losh ifp->if_mtu = ETHERMTU; 1009c8befdd5SWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 1010c8befdd5SWarner Losh ifp->if_ioctl = ste_ioctl; 1011c8befdd5SWarner Losh ifp->if_start = ste_start; 1012c8befdd5SWarner Losh ifp->if_init = ste_init; 1013c8befdd5SWarner Losh IFQ_SET_MAXLEN(&ifp->if_snd, STE_TX_LIST_CNT - 1); 1014c8befdd5SWarner Losh ifp->if_snd.ifq_drv_maxlen = STE_TX_LIST_CNT - 1; 1015c8befdd5SWarner Losh IFQ_SET_READY(&ifp->if_snd); 1016c8befdd5SWarner Losh 1017c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1018c8befdd5SWarner Losh 1019c8befdd5SWarner Losh /* 1020c8befdd5SWarner Losh * Call MI attach routine. 1021c8befdd5SWarner Losh */ 1022c8befdd5SWarner Losh ether_ifattach(ifp, eaddr); 1023c8befdd5SWarner Losh 1024c8befdd5SWarner Losh /* 1025c8befdd5SWarner Losh * Tell the upper layer(s) we support long frames. 1026c8befdd5SWarner Losh */ 1027c8befdd5SWarner Losh ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); 1028c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_VLAN_MTU; 1029c8befdd5SWarner Losh ifp->if_capenable = ifp->if_capabilities; 1030c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1031c8befdd5SWarner Losh ifp->if_capabilities |= IFCAP_POLLING; 1032c8befdd5SWarner Losh #endif 1033c8befdd5SWarner Losh 1034c8befdd5SWarner Losh /* Hook interrupt last to avoid having to lock softc */ 1035c8befdd5SWarner Losh error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET | INTR_MPSAFE, 1036c8befdd5SWarner Losh NULL, ste_intr, sc, &sc->ste_intrhand); 1037c8befdd5SWarner Losh 1038c8befdd5SWarner Losh if (error) { 1039c8befdd5SWarner Losh device_printf(dev, "couldn't set up irq\n"); 1040c8befdd5SWarner Losh ether_ifdetach(ifp); 1041c8befdd5SWarner Losh goto fail; 1042c8befdd5SWarner Losh } 1043c8befdd5SWarner Losh 1044c8befdd5SWarner Losh fail: 1045c8befdd5SWarner Losh if (error) 1046c8befdd5SWarner Losh ste_detach(dev); 1047c8befdd5SWarner Losh 1048c8befdd5SWarner Losh return (error); 1049c8befdd5SWarner Losh } 1050c8befdd5SWarner Losh 1051c8befdd5SWarner Losh /* 1052c8befdd5SWarner Losh * Shutdown hardware and free up resources. This can be called any 1053c8befdd5SWarner Losh * time after the mutex has been initialized. It is called in both 1054c8befdd5SWarner Losh * the error case in attach and the normal detach case so it needs 1055c8befdd5SWarner Losh * to be careful about only freeing resources that have actually been 1056c8befdd5SWarner Losh * allocated. 1057c8befdd5SWarner Losh */ 1058c8befdd5SWarner Losh static int 105960270842SPyun YongHyeon ste_detach(device_t dev) 1060c8befdd5SWarner Losh { 1061c8befdd5SWarner Losh struct ste_softc *sc; 1062c8befdd5SWarner Losh struct ifnet *ifp; 1063c8befdd5SWarner Losh 1064c8befdd5SWarner Losh sc = device_get_softc(dev); 1065c8befdd5SWarner Losh KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized")); 1066c8befdd5SWarner Losh ifp = sc->ste_ifp; 1067c8befdd5SWarner Losh 1068c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1069c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1070c8befdd5SWarner Losh ether_poll_deregister(ifp); 1071c8befdd5SWarner Losh #endif 1072c8befdd5SWarner Losh 1073c8befdd5SWarner Losh /* These should only be active if attach succeeded */ 1074c8befdd5SWarner Losh if (device_is_attached(dev)) { 10757cf545d0SJohn Baldwin ether_ifdetach(ifp); 1076c8befdd5SWarner Losh STE_LOCK(sc); 1077c8befdd5SWarner Losh ste_stop(sc); 1078c8befdd5SWarner Losh STE_UNLOCK(sc); 1079c8befdd5SWarner Losh callout_drain(&sc->ste_stat_callout); 1080c8befdd5SWarner Losh } 1081c8befdd5SWarner Losh if (sc->ste_miibus) 1082c8befdd5SWarner Losh device_delete_child(dev, sc->ste_miibus); 1083c8befdd5SWarner Losh bus_generic_detach(dev); 1084c8befdd5SWarner Losh 1085c8befdd5SWarner Losh if (sc->ste_intrhand) 1086c8befdd5SWarner Losh bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); 1087c8befdd5SWarner Losh if (sc->ste_irq) 1088c8befdd5SWarner Losh bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); 1089c8befdd5SWarner Losh if (sc->ste_res) 1090c8befdd5SWarner Losh bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); 1091c8befdd5SWarner Losh 1092c8befdd5SWarner Losh if (ifp) 1093c8befdd5SWarner Losh if_free(ifp); 1094c8befdd5SWarner Losh 1095a1b2c209SPyun YongHyeon ste_dma_free(sc); 1096c8befdd5SWarner Losh mtx_destroy(&sc->ste_mtx); 1097c8befdd5SWarner Losh 1098c8befdd5SWarner Losh return (0); 1099c8befdd5SWarner Losh } 1100c8befdd5SWarner Losh 1101a1b2c209SPyun YongHyeon struct ste_dmamap_arg { 1102a1b2c209SPyun YongHyeon bus_addr_t ste_busaddr; 1103a1b2c209SPyun YongHyeon }; 1104a1b2c209SPyun YongHyeon 1105a1b2c209SPyun YongHyeon static void 1106a1b2c209SPyun YongHyeon ste_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1107c8befdd5SWarner Losh { 1108a1b2c209SPyun YongHyeon struct ste_dmamap_arg *ctx; 1109c8befdd5SWarner Losh 1110a1b2c209SPyun YongHyeon if (error != 0) 1111a1b2c209SPyun YongHyeon return; 1112a1b2c209SPyun YongHyeon 1113a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1114a1b2c209SPyun YongHyeon 1115a1b2c209SPyun YongHyeon ctx = (struct ste_dmamap_arg *)arg; 1116a1b2c209SPyun YongHyeon ctx->ste_busaddr = segs[0].ds_addr; 1117c8befdd5SWarner Losh } 1118c8befdd5SWarner Losh 1119a1b2c209SPyun YongHyeon static int 1120a1b2c209SPyun YongHyeon ste_dma_alloc(struct ste_softc *sc) 1121a1b2c209SPyun YongHyeon { 1122a1b2c209SPyun YongHyeon struct ste_chain *txc; 1123a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1124a1b2c209SPyun YongHyeon struct ste_dmamap_arg ctx; 1125a1b2c209SPyun YongHyeon int error, i; 1126c8befdd5SWarner Losh 1127a1b2c209SPyun YongHyeon /* Create parent DMA tag. */ 1128a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1129a1b2c209SPyun YongHyeon bus_get_dma_tag(sc->ste_dev), /* parent */ 1130a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1131a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1132a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1133a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1134a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 1135a1b2c209SPyun YongHyeon 0, /* nsegments */ 1136a1b2c209SPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1137a1b2c209SPyun YongHyeon 0, /* flags */ 1138a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1139a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_parent_tag); 1140a1b2c209SPyun YongHyeon if (error != 0) { 1141a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1142a1b2c209SPyun YongHyeon "could not create parent DMA tag.\n"); 1143a1b2c209SPyun YongHyeon goto fail; 1144a1b2c209SPyun YongHyeon } 1145c8befdd5SWarner Losh 1146a1b2c209SPyun YongHyeon /* Create DMA tag for Tx descriptor list. */ 1147a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1148a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1149a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1150a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1151a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1152a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1153a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsize */ 1154a1b2c209SPyun YongHyeon 1, /* nsegments */ 1155a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, /* maxsegsize */ 1156a1b2c209SPyun YongHyeon 0, /* flags */ 1157a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1158a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_tag); 1159a1b2c209SPyun YongHyeon if (error != 0) { 1160a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1161a1b2c209SPyun YongHyeon "could not create Tx list DMA tag.\n"); 1162a1b2c209SPyun YongHyeon goto fail; 1163a1b2c209SPyun YongHyeon } 1164a1b2c209SPyun YongHyeon 1165a1b2c209SPyun YongHyeon /* Create DMA tag for Rx descriptor list. */ 1166a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1167a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1168a1b2c209SPyun YongHyeon STE_DESC_ALIGN, 0, /* alignment, boundary */ 1169a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1170a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1171a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1172a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsize */ 1173a1b2c209SPyun YongHyeon 1, /* nsegments */ 1174a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, /* maxsegsize */ 1175a1b2c209SPyun YongHyeon 0, /* flags */ 1176a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1177a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_tag); 1178a1b2c209SPyun YongHyeon if (error != 0) { 1179a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1180a1b2c209SPyun YongHyeon "could not create Rx list DMA tag.\n"); 1181a1b2c209SPyun YongHyeon goto fail; 1182a1b2c209SPyun YongHyeon } 1183a1b2c209SPyun YongHyeon 1184a1b2c209SPyun YongHyeon /* Create DMA tag for Tx buffers. */ 1185a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1186a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1187a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1188a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1189a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1190a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1191a1b2c209SPyun YongHyeon MCLBYTES * STE_MAXFRAGS, /* maxsize */ 1192a1b2c209SPyun YongHyeon STE_MAXFRAGS, /* nsegments */ 1193a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1194a1b2c209SPyun YongHyeon 0, /* flags */ 1195a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1196a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_tag); 1197a1b2c209SPyun YongHyeon if (error != 0) { 1198a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Tx DMA tag.\n"); 1199a1b2c209SPyun YongHyeon goto fail; 1200a1b2c209SPyun YongHyeon } 1201a1b2c209SPyun YongHyeon 1202a1b2c209SPyun YongHyeon /* Create DMA tag for Rx buffers. */ 1203a1b2c209SPyun YongHyeon error = bus_dma_tag_create( 1204a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag, /* parent */ 1205a1b2c209SPyun YongHyeon 1, 0, /* alignment, boundary */ 1206a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 1207a1b2c209SPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 1208a1b2c209SPyun YongHyeon NULL, NULL, /* filter, filterarg */ 1209a1b2c209SPyun YongHyeon MCLBYTES, /* maxsize */ 1210a1b2c209SPyun YongHyeon 1, /* nsegments */ 1211a1b2c209SPyun YongHyeon MCLBYTES, /* maxsegsize */ 1212a1b2c209SPyun YongHyeon 0, /* flags */ 1213a1b2c209SPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 1214a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_tag); 1215a1b2c209SPyun YongHyeon if (error != 0) { 1216a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, "could not create Rx DMA tag.\n"); 1217a1b2c209SPyun YongHyeon goto fail; 1218a1b2c209SPyun YongHyeon } 1219a1b2c209SPyun YongHyeon 1220a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Tx list. */ 1221a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_tx_list_tag, 1222a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_tx_list, 1223a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1224a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_tx_list_map); 1225a1b2c209SPyun YongHyeon if (error != 0) { 1226a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1227a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Tx list.\n"); 1228a1b2c209SPyun YongHyeon goto fail; 1229a1b2c209SPyun YongHyeon } 1230a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1231a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_tx_list_tag, 1232a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, sc->ste_ldata.ste_tx_list, 1233a1b2c209SPyun YongHyeon STE_TX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1234a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1235a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1236a1b2c209SPyun YongHyeon "could not load DMA'able memory for Tx list.\n"); 1237a1b2c209SPyun YongHyeon goto fail; 1238a1b2c209SPyun YongHyeon } 1239a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list_paddr = ctx.ste_busaddr; 1240a1b2c209SPyun YongHyeon 1241a1b2c209SPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for Rx list. */ 1242a1b2c209SPyun YongHyeon error = bus_dmamem_alloc(sc->ste_cdata.ste_rx_list_tag, 1243a1b2c209SPyun YongHyeon (void **)&sc->ste_ldata.ste_rx_list, 1244a1b2c209SPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 1245a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_list_map); 1246a1b2c209SPyun YongHyeon if (error != 0) { 1247a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1248a1b2c209SPyun YongHyeon "could not allocate DMA'able memory for Rx list.\n"); 1249a1b2c209SPyun YongHyeon goto fail; 1250a1b2c209SPyun YongHyeon } 1251a1b2c209SPyun YongHyeon ctx.ste_busaddr = 0; 1252a1b2c209SPyun YongHyeon error = bus_dmamap_load(sc->ste_cdata.ste_rx_list_tag, 1253a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, sc->ste_ldata.ste_rx_list, 1254a1b2c209SPyun YongHyeon STE_RX_LIST_SZ, ste_dmamap_cb, &ctx, 0); 1255a1b2c209SPyun YongHyeon if (error != 0 || ctx.ste_busaddr == 0) { 1256a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1257a1b2c209SPyun YongHyeon "could not load DMA'able memory for Rx list.\n"); 1258a1b2c209SPyun YongHyeon goto fail; 1259a1b2c209SPyun YongHyeon } 1260a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list_paddr = ctx.ste_busaddr; 1261a1b2c209SPyun YongHyeon 1262a1b2c209SPyun YongHyeon /* Create DMA maps for Tx buffers. */ 1263a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1264a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1265a1b2c209SPyun YongHyeon txc->ste_ptr = NULL; 1266a1b2c209SPyun YongHyeon txc->ste_mbuf = NULL; 1267a1b2c209SPyun YongHyeon txc->ste_next = NULL; 1268a1b2c209SPyun YongHyeon txc->ste_phys = 0; 1269a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1270a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_tx_tag, 0, 1271a1b2c209SPyun YongHyeon &txc->ste_map); 1272a1b2c209SPyun YongHyeon if (error != 0) { 1273a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1274a1b2c209SPyun YongHyeon "could not create Tx dmamap.\n"); 1275a1b2c209SPyun YongHyeon goto fail; 1276a1b2c209SPyun YongHyeon } 1277a1b2c209SPyun YongHyeon } 1278a1b2c209SPyun YongHyeon /* Create DMA maps for Rx buffers. */ 1279a1b2c209SPyun YongHyeon if ((error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1280a1b2c209SPyun YongHyeon &sc->ste_cdata.ste_rx_sparemap)) != 0) { 1281a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1282a1b2c209SPyun YongHyeon "could not create spare Rx dmamap.\n"); 1283a1b2c209SPyun YongHyeon goto fail; 1284a1b2c209SPyun YongHyeon } 1285a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1286a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1287a1b2c209SPyun YongHyeon rxc->ste_ptr = NULL; 1288a1b2c209SPyun YongHyeon rxc->ste_mbuf = NULL; 1289a1b2c209SPyun YongHyeon rxc->ste_next = NULL; 1290a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1291a1b2c209SPyun YongHyeon error = bus_dmamap_create(sc->ste_cdata.ste_rx_tag, 0, 1292a1b2c209SPyun YongHyeon &rxc->ste_map); 1293a1b2c209SPyun YongHyeon if (error != 0) { 1294a1b2c209SPyun YongHyeon device_printf(sc->ste_dev, 1295a1b2c209SPyun YongHyeon "could not create Rx dmamap.\n"); 1296a1b2c209SPyun YongHyeon goto fail; 1297a1b2c209SPyun YongHyeon } 1298a1b2c209SPyun YongHyeon } 1299a1b2c209SPyun YongHyeon 1300a1b2c209SPyun YongHyeon fail: 1301a1b2c209SPyun YongHyeon return (error); 1302a1b2c209SPyun YongHyeon } 1303a1b2c209SPyun YongHyeon 1304a1b2c209SPyun YongHyeon static void 1305a1b2c209SPyun YongHyeon ste_dma_free(struct ste_softc *sc) 1306a1b2c209SPyun YongHyeon { 1307a1b2c209SPyun YongHyeon struct ste_chain *txc; 1308a1b2c209SPyun YongHyeon struct ste_chain_onefrag *rxc; 1309a1b2c209SPyun YongHyeon int i; 1310a1b2c209SPyun YongHyeon 1311a1b2c209SPyun YongHyeon /* Tx buffers. */ 1312a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_tag != NULL) { 1313a1b2c209SPyun YongHyeon for (i = 0; i < STE_TX_LIST_CNT; i++) { 1314a1b2c209SPyun YongHyeon txc = &sc->ste_cdata.ste_tx_chain[i]; 1315a1b2c209SPyun YongHyeon if (txc->ste_map != NULL) { 1316a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_tx_tag, 1317a1b2c209SPyun YongHyeon txc->ste_map); 1318a1b2c209SPyun YongHyeon txc->ste_map = NULL; 1319a1b2c209SPyun YongHyeon } 1320a1b2c209SPyun YongHyeon } 1321a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_tag); 1322a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_tag = NULL; 1323a1b2c209SPyun YongHyeon } 1324a1b2c209SPyun YongHyeon /* Rx buffers. */ 1325a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_tag != NULL) { 1326a1b2c209SPyun YongHyeon for (i = 0; i < STE_RX_LIST_CNT; i++) { 1327a1b2c209SPyun YongHyeon rxc = &sc->ste_cdata.ste_rx_chain[i]; 1328a1b2c209SPyun YongHyeon if (rxc->ste_map != NULL) { 1329a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1330a1b2c209SPyun YongHyeon rxc->ste_map); 1331a1b2c209SPyun YongHyeon rxc->ste_map = NULL; 1332a1b2c209SPyun YongHyeon } 1333a1b2c209SPyun YongHyeon } 1334a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_sparemap != NULL) { 1335a1b2c209SPyun YongHyeon bus_dmamap_destroy(sc->ste_cdata.ste_rx_tag, 1336a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap); 1337a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = NULL; 1338a1b2c209SPyun YongHyeon } 1339a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_tag); 1340a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_tag = NULL; 1341a1b2c209SPyun YongHyeon } 1342a1b2c209SPyun YongHyeon /* Tx descriptor list. */ 1343a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_tag != NULL) { 1344a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_map != NULL) 1345a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_list_tag, 1346a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1347a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_list_map != NULL && 1348a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list != NULL) 1349a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_tx_list_tag, 1350a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list, 1351a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map); 1352a1b2c209SPyun YongHyeon sc->ste_ldata.ste_tx_list = NULL; 1353a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map = NULL; 1354a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_tx_list_tag); 1355a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_tag = NULL; 1356a1b2c209SPyun YongHyeon } 1357a1b2c209SPyun YongHyeon /* Rx descriptor list. */ 1358a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_tag != NULL) { 1359a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_map != NULL) 1360a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_list_tag, 1361a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1362a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_rx_list_map != NULL && 1363a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list != NULL) 1364a1b2c209SPyun YongHyeon bus_dmamem_free(sc->ste_cdata.ste_rx_list_tag, 1365a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list, 1366a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map); 1367a1b2c209SPyun YongHyeon sc->ste_ldata.ste_rx_list = NULL; 1368a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map = NULL; 1369a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_rx_list_tag); 1370a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_tag = NULL; 1371a1b2c209SPyun YongHyeon } 1372a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_parent_tag != NULL) { 1373a1b2c209SPyun YongHyeon bus_dma_tag_destroy(sc->ste_cdata.ste_parent_tag); 1374a1b2c209SPyun YongHyeon sc->ste_cdata.ste_parent_tag = NULL; 1375a1b2c209SPyun YongHyeon } 1376a1b2c209SPyun YongHyeon } 1377a1b2c209SPyun YongHyeon 1378a1b2c209SPyun YongHyeon static int 1379a1b2c209SPyun YongHyeon ste_newbuf(struct ste_softc *sc, struct ste_chain_onefrag *rxc) 1380a1b2c209SPyun YongHyeon { 1381a1b2c209SPyun YongHyeon struct mbuf *m; 1382a1b2c209SPyun YongHyeon bus_dma_segment_t segs[1]; 1383a1b2c209SPyun YongHyeon bus_dmamap_t map; 1384a1b2c209SPyun YongHyeon int error, nsegs; 1385a1b2c209SPyun YongHyeon 1386a1b2c209SPyun YongHyeon m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1387a1b2c209SPyun YongHyeon if (m == NULL) 1388a1b2c209SPyun YongHyeon return (ENOBUFS); 1389a1b2c209SPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES; 1390a1b2c209SPyun YongHyeon m_adj(m, ETHER_ALIGN); 1391a1b2c209SPyun YongHyeon 1392a1b2c209SPyun YongHyeon if ((error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_rx_tag, 1393a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap, m, segs, &nsegs, 0)) != 0) { 1394a1b2c209SPyun YongHyeon m_freem(m); 1395a1b2c209SPyun YongHyeon return (error); 1396a1b2c209SPyun YongHyeon } 1397a1b2c209SPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1398a1b2c209SPyun YongHyeon 1399a1b2c209SPyun YongHyeon if (rxc->ste_mbuf != NULL) { 1400a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1401a1b2c209SPyun YongHyeon BUS_DMASYNC_POSTREAD); 1402a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, rxc->ste_map); 1403a1b2c209SPyun YongHyeon } 1404a1b2c209SPyun YongHyeon map = rxc->ste_map; 1405a1b2c209SPyun YongHyeon rxc->ste_map = sc->ste_cdata.ste_rx_sparemap; 1406a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_sparemap = map; 1407a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, rxc->ste_map, 1408a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD); 1409a1b2c209SPyun YongHyeon rxc->ste_mbuf = m; 1410a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_status = 0; 1411a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_addr = htole32(segs[0].ds_addr); 1412a1b2c209SPyun YongHyeon rxc->ste_ptr->ste_frag.ste_len = htole32(segs[0].ds_len | 1413a1b2c209SPyun YongHyeon STE_FRAG_LAST); 1414c8befdd5SWarner Losh return (0); 1415c8befdd5SWarner Losh } 1416c8befdd5SWarner Losh 1417c8befdd5SWarner Losh static int 141860270842SPyun YongHyeon ste_init_rx_list(struct ste_softc *sc) 1419c8befdd5SWarner Losh { 1420c8befdd5SWarner Losh struct ste_chain_data *cd; 1421c8befdd5SWarner Losh struct ste_list_data *ld; 1422a1b2c209SPyun YongHyeon int error, i; 1423c8befdd5SWarner Losh 1424c8befdd5SWarner Losh cd = &sc->ste_cdata; 1425a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1426a1b2c209SPyun YongHyeon bzero(ld->ste_rx_list, STE_RX_LIST_SZ); 1427c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1428c8befdd5SWarner Losh cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; 1429a1b2c209SPyun YongHyeon error = ste_newbuf(sc, &cd->ste_rx_chain[i]); 1430a1b2c209SPyun YongHyeon if (error != 0) 1431a1b2c209SPyun YongHyeon return (error); 1432c8befdd5SWarner Losh if (i == (STE_RX_LIST_CNT - 1)) { 1433a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[0]; 1434a1b2c209SPyun YongHyeon ld->ste_rx_list[i].ste_next = ld->ste_rx_list_paddr + 1435a1b2c209SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * 0); 1436c8befdd5SWarner Losh } else { 1437a1b2c209SPyun YongHyeon cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[i + 1]; 1438a1b2c209SPyun YongHyeon ld->ste_rx_list[i].ste_next = ld->ste_rx_list_paddr + 1439a1b2c209SPyun YongHyeon (sizeof(struct ste_desc_onefrag) * (i + 1)); 1440c8befdd5SWarner Losh } 1441c8befdd5SWarner Losh } 1442c8befdd5SWarner Losh 1443c8befdd5SWarner Losh cd->ste_rx_head = &cd->ste_rx_chain[0]; 1444a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_list_tag, 1445a1b2c209SPyun YongHyeon sc->ste_cdata.ste_rx_list_map, 1446a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1447c8befdd5SWarner Losh 1448c8befdd5SWarner Losh return (0); 1449c8befdd5SWarner Losh } 1450c8befdd5SWarner Losh 1451c8befdd5SWarner Losh static void 145260270842SPyun YongHyeon ste_init_tx_list(struct ste_softc *sc) 1453c8befdd5SWarner Losh { 1454c8befdd5SWarner Losh struct ste_chain_data *cd; 1455c8befdd5SWarner Losh struct ste_list_data *ld; 1456c8befdd5SWarner Losh int i; 1457c8befdd5SWarner Losh 1458c8befdd5SWarner Losh cd = &sc->ste_cdata; 1459a1b2c209SPyun YongHyeon ld = &sc->ste_ldata; 1460a1b2c209SPyun YongHyeon bzero(ld->ste_tx_list, STE_TX_LIST_SZ); 1461c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1462c8befdd5SWarner Losh cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; 1463a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_mbuf = NULL; 1464a1b2c209SPyun YongHyeon if (i == (STE_TX_LIST_CNT - 1)) { 1465a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[0]; 1466a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1467a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1468a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * 0))); 1469a1b2c209SPyun YongHyeon } else { 1470a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1]; 1471a1b2c209SPyun YongHyeon cd->ste_tx_chain[i].ste_phys = htole32(STE_ADDR_LO( 1472a1b2c209SPyun YongHyeon ld->ste_tx_list_paddr + 1473a1b2c209SPyun YongHyeon (sizeof(struct ste_desc) * (i + 1)))); 1474a1b2c209SPyun YongHyeon } 1475c8befdd5SWarner Losh } 1476c8befdd5SWarner Losh 1477a1b2c209SPyun YongHyeon cd->ste_last_tx = NULL; 1478c8befdd5SWarner Losh cd->ste_tx_prod = 0; 1479c8befdd5SWarner Losh cd->ste_tx_cons = 0; 1480a1b2c209SPyun YongHyeon cd->ste_tx_cnt = 0; 1481a1b2c209SPyun YongHyeon 1482a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1483a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1484a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1485c8befdd5SWarner Losh } 1486c8befdd5SWarner Losh 1487c8befdd5SWarner Losh static void 148860270842SPyun YongHyeon ste_init(void *xsc) 1489c8befdd5SWarner Losh { 1490c8befdd5SWarner Losh struct ste_softc *sc; 1491c8befdd5SWarner Losh 1492c8befdd5SWarner Losh sc = xsc; 1493c8befdd5SWarner Losh STE_LOCK(sc); 1494c8befdd5SWarner Losh ste_init_locked(sc); 1495c8befdd5SWarner Losh STE_UNLOCK(sc); 1496c8befdd5SWarner Losh } 1497c8befdd5SWarner Losh 1498c8befdd5SWarner Losh static void 149960270842SPyun YongHyeon ste_init_locked(struct ste_softc *sc) 1500c8befdd5SWarner Losh { 1501c8befdd5SWarner Losh struct ifnet *ifp; 1502f2632c3bSPyun YongHyeon int i; 1503c8befdd5SWarner Losh 1504c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1505c8befdd5SWarner Losh ifp = sc->ste_ifp; 1506c8befdd5SWarner Losh 1507c8befdd5SWarner Losh ste_stop(sc); 1508c8befdd5SWarner Losh 1509c8befdd5SWarner Losh /* Init our MAC address */ 1510c8befdd5SWarner Losh for (i = 0; i < ETHER_ADDR_LEN; i += 2) { 1511c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_PAR0 + i, 1512c8befdd5SWarner Losh ((IF_LLADDR(sc->ste_ifp)[i] & 0xff) | 1513c8befdd5SWarner Losh IF_LLADDR(sc->ste_ifp)[i + 1] << 8)); 1514c8befdd5SWarner Losh } 1515c8befdd5SWarner Losh 1516c8befdd5SWarner Losh /* Init RX list */ 1517a1b2c209SPyun YongHyeon if (ste_init_rx_list(sc) != 0) { 1518c8befdd5SWarner Losh device_printf(sc->ste_dev, 1519c8befdd5SWarner Losh "initialization failed: no memory for RX buffers\n"); 1520c8befdd5SWarner Losh ste_stop(sc); 1521c8befdd5SWarner Losh return; 1522c8befdd5SWarner Losh } 1523c8befdd5SWarner Losh 1524c8befdd5SWarner Losh /* Set RX polling interval */ 1525c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); 1526c8befdd5SWarner Losh 1527c8befdd5SWarner Losh /* Init TX descriptors */ 1528c8befdd5SWarner Losh ste_init_tx_list(sc); 1529c8befdd5SWarner Losh 1530c8befdd5SWarner Losh /* Set the TX freethresh value */ 1531c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); 1532c8befdd5SWarner Losh 1533c8befdd5SWarner Losh /* Set the TX start threshold for best performance. */ 1534c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); 1535c8befdd5SWarner Losh 1536c8befdd5SWarner Losh /* Set the TX reclaim threshold. */ 1537c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); 1538c8befdd5SWarner Losh 1539c8befdd5SWarner Losh /* Set up the RX filter. */ 1540c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_RX_MODE, STE_RXMODE_UNICAST); 1541c8befdd5SWarner Losh 1542c8befdd5SWarner Losh /* If we want promiscuous mode, set the allframes bit. */ 1543c8befdd5SWarner Losh if (ifp->if_flags & IFF_PROMISC) { 1544c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); 1545c8befdd5SWarner Losh } else { 1546c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); 1547c8befdd5SWarner Losh } 1548c8befdd5SWarner Losh 1549c8befdd5SWarner Losh /* Set capture broadcast bit to accept broadcast frames. */ 1550c8befdd5SWarner Losh if (ifp->if_flags & IFF_BROADCAST) { 1551c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); 1552c8befdd5SWarner Losh } else { 1553c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); 1554c8befdd5SWarner Losh } 1555c8befdd5SWarner Losh 1556c8befdd5SWarner Losh ste_setmulti(sc); 1557c8befdd5SWarner Losh 1558c8befdd5SWarner Losh /* Load the address of the RX list. */ 1559c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1560c8befdd5SWarner Losh ste_wait(sc); 1561c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, 1562a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_rx_list_paddr)); 1563c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1564c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); 1565c8befdd5SWarner Losh 1566a1b2c209SPyun YongHyeon /* Set TX polling interval(defer until we TX first packet). */ 1567c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); 1568c8befdd5SWarner Losh 1569c8befdd5SWarner Losh /* Load address of the TX list */ 1570c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1571c8befdd5SWarner Losh ste_wait(sc); 1572c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); 1573c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1574c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1575c8befdd5SWarner Losh ste_wait(sc); 1576c8befdd5SWarner Losh 1577c8befdd5SWarner Losh /* Enable receiver and transmitter */ 1578c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL0, 0); 1579c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MACCTL1, 0); 1580c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); 1581c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); 1582c8befdd5SWarner Losh 1583c8befdd5SWarner Losh /* Enable stats counters. */ 1584c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); 1585c8befdd5SWarner Losh 1586c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_ISR, 0xFFFF); 1587c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1588c8befdd5SWarner Losh /* Disable interrupts if we are polling. */ 1589c8befdd5SWarner Losh if (ifp->if_capenable & IFCAP_POLLING) 1590c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1591c8befdd5SWarner Losh else 1592c8befdd5SWarner Losh #endif 1593c8befdd5SWarner Losh /* Enable interrupts. */ 1594c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1595c8befdd5SWarner Losh 1596c8befdd5SWarner Losh /* Accept VLAN length packets */ 1597c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN); 1598c8befdd5SWarner Losh 1599c8befdd5SWarner Losh ste_ifmedia_upd_locked(ifp); 1600c8befdd5SWarner Losh 1601c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 1602c8befdd5SWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1603c8befdd5SWarner Losh 1604c8befdd5SWarner Losh callout_reset(&sc->ste_stat_callout, hz, ste_stats_update, sc); 1605c8befdd5SWarner Losh } 1606c8befdd5SWarner Losh 1607c8befdd5SWarner Losh static void 160860270842SPyun YongHyeon ste_stop(struct ste_softc *sc) 1609c8befdd5SWarner Losh { 1610c8befdd5SWarner Losh struct ifnet *ifp; 1611a1b2c209SPyun YongHyeon struct ste_chain_onefrag *cur_rx; 1612a1b2c209SPyun YongHyeon struct ste_chain *cur_tx; 1613f2632c3bSPyun YongHyeon int i; 1614c8befdd5SWarner Losh 1615c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1616c8befdd5SWarner Losh ifp = sc->ste_ifp; 1617c8befdd5SWarner Losh 1618c8befdd5SWarner Losh callout_stop(&sc->ste_stat_callout); 1619c8befdd5SWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE); 1620c8befdd5SWarner Losh 1621c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1622c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_DISABLE); 1623c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_DISABLE); 1624c8befdd5SWarner Losh STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_DISABLE); 1625c8befdd5SWarner Losh STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1626c8befdd5SWarner Losh STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); 1627c8befdd5SWarner Losh ste_wait(sc); 1628c8befdd5SWarner Losh /* 1629c8befdd5SWarner Losh * Try really hard to stop the RX engine or under heavy RX 1630c8befdd5SWarner Losh * data chip will write into de-allocated memory. 1631c8befdd5SWarner Losh */ 1632c8befdd5SWarner Losh ste_reset(sc); 1633c8befdd5SWarner Losh 1634c8befdd5SWarner Losh sc->ste_link = 0; 1635c8befdd5SWarner Losh 1636c8befdd5SWarner Losh for (i = 0; i < STE_RX_LIST_CNT; i++) { 1637a1b2c209SPyun YongHyeon cur_rx = &sc->ste_cdata.ste_rx_chain[i]; 1638a1b2c209SPyun YongHyeon if (cur_rx->ste_mbuf != NULL) { 1639a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_rx_tag, 1640a1b2c209SPyun YongHyeon cur_rx->ste_map, BUS_DMASYNC_POSTREAD); 1641a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_rx_tag, 1642a1b2c209SPyun YongHyeon cur_rx->ste_map); 1643a1b2c209SPyun YongHyeon m_freem(cur_rx->ste_mbuf); 1644a1b2c209SPyun YongHyeon cur_rx->ste_mbuf = NULL; 1645c8befdd5SWarner Losh } 1646c8befdd5SWarner Losh } 1647c8befdd5SWarner Losh 1648c8befdd5SWarner Losh for (i = 0; i < STE_TX_LIST_CNT; i++) { 1649a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[i]; 1650a1b2c209SPyun YongHyeon if (cur_tx->ste_mbuf != NULL) { 1651a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, 1652a1b2c209SPyun YongHyeon cur_tx->ste_map, BUS_DMASYNC_POSTWRITE); 1653a1b2c209SPyun YongHyeon bus_dmamap_unload(sc->ste_cdata.ste_tx_tag, 1654a1b2c209SPyun YongHyeon cur_tx->ste_map); 1655a1b2c209SPyun YongHyeon m_freem(cur_tx->ste_mbuf); 1656a1b2c209SPyun YongHyeon cur_tx->ste_mbuf = NULL; 1657c8befdd5SWarner Losh } 1658c8befdd5SWarner Losh } 1659c8befdd5SWarner Losh } 1660c8befdd5SWarner Losh 1661c8befdd5SWarner Losh static void 166260270842SPyun YongHyeon ste_reset(struct ste_softc *sc) 1663c8befdd5SWarner Losh { 1664c8befdd5SWarner Losh int i; 1665c8befdd5SWarner Losh 1666c8befdd5SWarner Losh STE_SETBIT4(sc, STE_ASICCTL, 1667c8befdd5SWarner Losh STE_ASICCTL_GLOBAL_RESET|STE_ASICCTL_RX_RESET| 1668c8befdd5SWarner Losh STE_ASICCTL_TX_RESET|STE_ASICCTL_DMA_RESET| 1669c8befdd5SWarner Losh STE_ASICCTL_FIFO_RESET|STE_ASICCTL_NETWORK_RESET| 1670c8befdd5SWarner Losh STE_ASICCTL_AUTOINIT_RESET|STE_ASICCTL_HOST_RESET| 1671c8befdd5SWarner Losh STE_ASICCTL_EXTRESET_RESET); 1672c8befdd5SWarner Losh 1673c8befdd5SWarner Losh DELAY(100000); 1674c8befdd5SWarner Losh 1675c8befdd5SWarner Losh for (i = 0; i < STE_TIMEOUT; i++) { 1676c8befdd5SWarner Losh if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) 1677c8befdd5SWarner Losh break; 1678c8befdd5SWarner Losh } 1679c8befdd5SWarner Losh 1680c8befdd5SWarner Losh if (i == STE_TIMEOUT) 1681c8befdd5SWarner Losh device_printf(sc->ste_dev, "global reset never completed\n"); 1682c8befdd5SWarner Losh } 1683c8befdd5SWarner Losh 1684c8befdd5SWarner Losh static int 168560270842SPyun YongHyeon ste_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 1686c8befdd5SWarner Losh { 1687c8befdd5SWarner Losh struct ste_softc *sc; 1688c8befdd5SWarner Losh struct ifreq *ifr; 1689c8befdd5SWarner Losh struct mii_data *mii; 1690c8befdd5SWarner Losh int error = 0; 1691c8befdd5SWarner Losh 1692c8befdd5SWarner Losh sc = ifp->if_softc; 1693c8befdd5SWarner Losh ifr = (struct ifreq *)data; 1694c8befdd5SWarner Losh 1695c8befdd5SWarner Losh switch (command) { 1696c8befdd5SWarner Losh case SIOCSIFFLAGS: 1697c8befdd5SWarner Losh STE_LOCK(sc); 1698c8befdd5SWarner Losh if (ifp->if_flags & IFF_UP) { 1699c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1700c8befdd5SWarner Losh ifp->if_flags & IFF_PROMISC && 1701c8befdd5SWarner Losh !(sc->ste_if_flags & IFF_PROMISC)) { 1702c8befdd5SWarner Losh STE_SETBIT1(sc, STE_RX_MODE, 1703c8befdd5SWarner Losh STE_RXMODE_PROMISC); 1704c8befdd5SWarner Losh } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1705c8befdd5SWarner Losh !(ifp->if_flags & IFF_PROMISC) && 1706c8befdd5SWarner Losh sc->ste_if_flags & IFF_PROMISC) { 1707c8befdd5SWarner Losh STE_CLRBIT1(sc, STE_RX_MODE, 1708c8befdd5SWarner Losh STE_RXMODE_PROMISC); 1709c8befdd5SWarner Losh } 1710c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING && 1711c8befdd5SWarner Losh (ifp->if_flags ^ sc->ste_if_flags) & IFF_ALLMULTI) 1712c8befdd5SWarner Losh ste_setmulti(sc); 1713c8befdd5SWarner Losh if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 1714c8befdd5SWarner Losh sc->ste_tx_thresh = STE_TXSTART_THRESH; 1715c8befdd5SWarner Losh ste_init_locked(sc); 1716c8befdd5SWarner Losh } 1717c8befdd5SWarner Losh } else { 1718c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 1719c8befdd5SWarner Losh ste_stop(sc); 1720c8befdd5SWarner Losh } 1721c8befdd5SWarner Losh sc->ste_if_flags = ifp->if_flags; 1722c8befdd5SWarner Losh STE_UNLOCK(sc); 1723c8befdd5SWarner Losh error = 0; 1724c8befdd5SWarner Losh break; 1725c8befdd5SWarner Losh case SIOCADDMULTI: 1726c8befdd5SWarner Losh case SIOCDELMULTI: 1727c8befdd5SWarner Losh STE_LOCK(sc); 1728c8befdd5SWarner Losh ste_setmulti(sc); 1729c8befdd5SWarner Losh STE_UNLOCK(sc); 1730c8befdd5SWarner Losh error = 0; 1731c8befdd5SWarner Losh break; 1732c8befdd5SWarner Losh case SIOCGIFMEDIA: 1733c8befdd5SWarner Losh case SIOCSIFMEDIA: 1734c8befdd5SWarner Losh mii = device_get_softc(sc->ste_miibus); 1735c8befdd5SWarner Losh error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 1736c8befdd5SWarner Losh break; 1737c8befdd5SWarner Losh case SIOCSIFCAP: 1738c8befdd5SWarner Losh #ifdef DEVICE_POLLING 1739c8befdd5SWarner Losh if (ifr->ifr_reqcap & IFCAP_POLLING && 1740c8befdd5SWarner Losh !(ifp->if_capenable & IFCAP_POLLING)) { 1741c8befdd5SWarner Losh error = ether_poll_register(ste_poll, ifp); 1742c8befdd5SWarner Losh if (error) 1743c8befdd5SWarner Losh return (error); 1744c8befdd5SWarner Losh STE_LOCK(sc); 1745c8befdd5SWarner Losh /* Disable interrupts */ 1746c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, 0); 1747c8befdd5SWarner Losh ifp->if_capenable |= IFCAP_POLLING; 1748c8befdd5SWarner Losh STE_UNLOCK(sc); 1749c8befdd5SWarner Losh return (error); 1750c8befdd5SWarner Losh 1751c8befdd5SWarner Losh } 1752c8befdd5SWarner Losh if (!(ifr->ifr_reqcap & IFCAP_POLLING) && 1753c8befdd5SWarner Losh ifp->if_capenable & IFCAP_POLLING) { 1754c8befdd5SWarner Losh error = ether_poll_deregister(ifp); 1755c8befdd5SWarner Losh /* Enable interrupts. */ 1756c8befdd5SWarner Losh STE_LOCK(sc); 1757c8befdd5SWarner Losh CSR_WRITE_2(sc, STE_IMR, STE_INTRS); 1758c8befdd5SWarner Losh ifp->if_capenable &= ~IFCAP_POLLING; 1759c8befdd5SWarner Losh STE_UNLOCK(sc); 1760c8befdd5SWarner Losh return (error); 1761c8befdd5SWarner Losh } 1762c8befdd5SWarner Losh #endif /* DEVICE_POLLING */ 1763c8befdd5SWarner Losh break; 1764c8befdd5SWarner Losh default: 1765c8befdd5SWarner Losh error = ether_ioctl(ifp, command, data); 1766c8befdd5SWarner Losh break; 1767c8befdd5SWarner Losh } 1768c8befdd5SWarner Losh 1769c8befdd5SWarner Losh return (error); 1770c8befdd5SWarner Losh } 1771c8befdd5SWarner Losh 1772c8befdd5SWarner Losh static int 1773a1b2c209SPyun YongHyeon ste_encap(struct ste_softc *sc, struct mbuf **m_head, struct ste_chain *txc) 1774c8befdd5SWarner Losh { 1775a1b2c209SPyun YongHyeon struct ste_frag *frag; 1776c8befdd5SWarner Losh struct mbuf *m; 1777a1b2c209SPyun YongHyeon struct ste_desc *desc; 1778a1b2c209SPyun YongHyeon bus_dma_segment_t txsegs[STE_MAXFRAGS]; 1779a1b2c209SPyun YongHyeon int error, i, nsegs; 1780c8befdd5SWarner Losh 1781a1b2c209SPyun YongHyeon STE_LOCK_ASSERT(sc); 1782a1b2c209SPyun YongHyeon M_ASSERTPKTHDR((*m_head)); 1783c8befdd5SWarner Losh 1784a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1785a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1786a1b2c209SPyun YongHyeon if (error == EFBIG) { 1787a1b2c209SPyun YongHyeon m = m_collapse(*m_head, M_DONTWAIT, STE_MAXFRAGS); 1788a1b2c209SPyun YongHyeon if (m == NULL) { 1789a1b2c209SPyun YongHyeon m_freem(*m_head); 1790a1b2c209SPyun YongHyeon *m_head = NULL; 1791a1b2c209SPyun YongHyeon return (ENOMEM); 1792c8befdd5SWarner Losh } 1793a1b2c209SPyun YongHyeon *m_head = m; 1794a1b2c209SPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->ste_cdata.ste_tx_tag, 1795a1b2c209SPyun YongHyeon txc->ste_map, *m_head, txsegs, &nsegs, 0); 1796a1b2c209SPyun YongHyeon if (error != 0) { 1797a1b2c209SPyun YongHyeon m_freem(*m_head); 1798a1b2c209SPyun YongHyeon *m_head = NULL; 1799a1b2c209SPyun YongHyeon return (error); 1800c8befdd5SWarner Losh } 1801a1b2c209SPyun YongHyeon } else if (error != 0) 1802a1b2c209SPyun YongHyeon return (error); 1803a1b2c209SPyun YongHyeon if (nsegs == 0) { 1804a1b2c209SPyun YongHyeon m_freem(*m_head); 1805a1b2c209SPyun YongHyeon *m_head = NULL; 1806a1b2c209SPyun YongHyeon return (EIO); 1807a1b2c209SPyun YongHyeon } 1808a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_tag, txc->ste_map, 1809a1b2c209SPyun YongHyeon BUS_DMASYNC_PREWRITE); 1810c8befdd5SWarner Losh 1811a1b2c209SPyun YongHyeon desc = txc->ste_ptr; 1812a1b2c209SPyun YongHyeon for (i = 0; i < nsegs; i++) { 1813a1b2c209SPyun YongHyeon frag = &desc->ste_frags[i]; 1814a1b2c209SPyun YongHyeon frag->ste_addr = htole32(STE_ADDR_LO(txsegs[i].ds_addr)); 1815a1b2c209SPyun YongHyeon frag->ste_len = htole32(txsegs[i].ds_len); 1816a1b2c209SPyun YongHyeon } 1817a1b2c209SPyun YongHyeon desc->ste_frags[i - 1].ste_len |= htole32(STE_FRAG_LAST); 1818c8befdd5SWarner Losh /* 1819a1b2c209SPyun YongHyeon * Because we use Tx polling we can't chain multiple 1820a1b2c209SPyun YongHyeon * Tx descriptors here. Otherwise we race with controller. 1821c8befdd5SWarner Losh */ 1822a1b2c209SPyun YongHyeon desc->ste_next = 0; 1823a1b2c209SPyun YongHyeon desc->ste_ctl = htole32(STE_TXCTL_ALIGN_DIS | STE_TXCTL_DMAINTR); 1824a1b2c209SPyun YongHyeon txc->ste_mbuf = *m_head; 1825a1b2c209SPyun YongHyeon STE_INC(sc->ste_cdata.ste_tx_prod, STE_TX_LIST_CNT); 1826a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_cnt++; 1827c8befdd5SWarner Losh 1828c8befdd5SWarner Losh return (0); 1829c8befdd5SWarner Losh } 1830c8befdd5SWarner Losh 1831c8befdd5SWarner Losh static void 183260270842SPyun YongHyeon ste_start(struct ifnet *ifp) 1833c8befdd5SWarner Losh { 1834c8befdd5SWarner Losh struct ste_softc *sc; 1835c8befdd5SWarner Losh 1836c8befdd5SWarner Losh sc = ifp->if_softc; 1837c8befdd5SWarner Losh STE_LOCK(sc); 1838c8befdd5SWarner Losh ste_start_locked(ifp); 1839c8befdd5SWarner Losh STE_UNLOCK(sc); 1840c8befdd5SWarner Losh } 1841c8befdd5SWarner Losh 1842c8befdd5SWarner Losh static void 184360270842SPyun YongHyeon ste_start_locked(struct ifnet *ifp) 1844c8befdd5SWarner Losh { 1845c8befdd5SWarner Losh struct ste_softc *sc; 1846c8befdd5SWarner Losh struct ste_chain *cur_tx; 1847f2632c3bSPyun YongHyeon struct mbuf *m_head = NULL; 1848a1b2c209SPyun YongHyeon int enq; 1849c8befdd5SWarner Losh 1850c8befdd5SWarner Losh sc = ifp->if_softc; 1851c8befdd5SWarner Losh STE_LOCK_ASSERT(sc); 1852c8befdd5SWarner Losh 1853c8befdd5SWarner Losh if (!sc->ste_link) 1854c8befdd5SWarner Losh return; 1855c8befdd5SWarner Losh 1856c8befdd5SWarner Losh if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 1857c8befdd5SWarner Losh return; 1858c8befdd5SWarner Losh 1859a1b2c209SPyun YongHyeon for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd);) { 1860a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_tx_cnt == STE_TX_LIST_CNT - 1) { 1861c8befdd5SWarner Losh /* 1862a1b2c209SPyun YongHyeon * Controller may have cached copy of the last used 1863a1b2c209SPyun YongHyeon * next ptr so we have to reserve one TFD to avoid 1864a1b2c209SPyun YongHyeon * TFD overruns. 1865c8befdd5SWarner Losh */ 1866c8befdd5SWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1867c8befdd5SWarner Losh break; 1868c8befdd5SWarner Losh } 1869c8befdd5SWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 1870c8befdd5SWarner Losh if (m_head == NULL) 1871c8befdd5SWarner Losh break; 1872a1b2c209SPyun YongHyeon cur_tx = &sc->ste_cdata.ste_tx_chain[sc->ste_cdata.ste_tx_prod]; 1873a1b2c209SPyun YongHyeon if (ste_encap(sc, &m_head, cur_tx) != 0) { 1874a1b2c209SPyun YongHyeon if (m_head == NULL) 1875c8befdd5SWarner Losh break; 1876a1b2c209SPyun YongHyeon IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 1877a1b2c209SPyun YongHyeon break; 1878a1b2c209SPyun YongHyeon } 1879a1b2c209SPyun YongHyeon if (sc->ste_cdata.ste_last_tx == NULL) { 1880a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1881a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1882a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1883c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); 1884c8befdd5SWarner Losh ste_wait(sc); 1885c8befdd5SWarner Losh CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 1886a1b2c209SPyun YongHyeon STE_ADDR_LO(sc->ste_ldata.ste_tx_list_paddr)); 1887c8befdd5SWarner Losh CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); 1888c8befdd5SWarner Losh STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); 1889c8befdd5SWarner Losh ste_wait(sc); 1890c8befdd5SWarner Losh } else { 1891a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_ptr->ste_next = 1892a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx->ste_phys; 1893a1b2c209SPyun YongHyeon bus_dmamap_sync(sc->ste_cdata.ste_tx_list_tag, 1894a1b2c209SPyun YongHyeon sc->ste_cdata.ste_tx_list_map, 1895a1b2c209SPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1896c8befdd5SWarner Losh } 1897a1b2c209SPyun YongHyeon sc->ste_cdata.ste_last_tx = cur_tx; 1898c8befdd5SWarner Losh 1899a1b2c209SPyun YongHyeon enq++; 1900c8befdd5SWarner Losh /* 1901c8befdd5SWarner Losh * If there's a BPF listener, bounce a copy of this frame 1902c8befdd5SWarner Losh * to him. 1903c8befdd5SWarner Losh */ 1904a1b2c209SPyun YongHyeon BPF_MTAP(ifp, m_head); 1905c8befdd5SWarner Losh } 1906a1b2c209SPyun YongHyeon 1907a1b2c209SPyun YongHyeon if (enq > 0) 1908a1b2c209SPyun YongHyeon sc->ste_timer = STE_TX_TIMEOUT; 1909c8befdd5SWarner Losh } 1910c8befdd5SWarner Losh 1911c8befdd5SWarner Losh static void 19127cf545d0SJohn Baldwin ste_watchdog(struct ste_softc *sc) 1913c8befdd5SWarner Losh { 19147cf545d0SJohn Baldwin struct ifnet *ifp; 1915c8befdd5SWarner Losh 19167cf545d0SJohn Baldwin ifp = sc->ste_ifp; 19177cf545d0SJohn Baldwin STE_LOCK_ASSERT(sc); 1918c8befdd5SWarner Losh 1919c8befdd5SWarner Losh ifp->if_oerrors++; 1920c8befdd5SWarner Losh if_printf(ifp, "watchdog timeout\n"); 1921c8befdd5SWarner Losh 1922c8befdd5SWarner Losh ste_txeoc(sc); 1923c8befdd5SWarner Losh ste_txeof(sc); 1924a1b2c209SPyun YongHyeon ste_rxeof(sc, -1); 1925c8befdd5SWarner Losh ste_reset(sc); 1926c8befdd5SWarner Losh ste_init_locked(sc); 1927c8befdd5SWarner Losh 1928c8befdd5SWarner Losh if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 1929c8befdd5SWarner Losh ste_start_locked(ifp); 1930c8befdd5SWarner Losh } 1931c8befdd5SWarner Losh 1932c8befdd5SWarner Losh static int 193360270842SPyun YongHyeon ste_shutdown(device_t dev) 1934c8befdd5SWarner Losh { 1935c8befdd5SWarner Losh struct ste_softc *sc; 1936c8befdd5SWarner Losh 1937c8befdd5SWarner Losh sc = device_get_softc(dev); 1938c8befdd5SWarner Losh 1939c8befdd5SWarner Losh STE_LOCK(sc); 1940c8befdd5SWarner Losh ste_stop(sc); 1941c8befdd5SWarner Losh STE_UNLOCK(sc); 1942c8befdd5SWarner Losh 1943c8befdd5SWarner Losh return (0); 1944c8befdd5SWarner Losh } 1945