if_arge.c revision 188808
1218893Sdim/*- 2193326Sed * Copyright (c) 2009, Oleksandr Tymoshenko 3193326Sed * All rights reserved. 4193326Sed * 5193326Sed * Redistribution and use in source and binary forms, with or without 6193326Sed * modification, are permitted provided that the following conditions 7193326Sed * are met: 8193326Sed * 1. Redistributions of source code must retain the above copyright 9193326Sed * notice unmodified, this list of conditions, and the following 10193326Sed * disclaimer. 11193326Sed * 2. Redistributions in binary form must reproduce the above copyright 12193326Sed * notice, this list of conditions and the following disclaimer in the 13193326Sed * documentation and/or other materials provided with the distribution. 14208600Srdivacky * 15193326Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17224145Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18199512Srdivacky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20199512Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21212904Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23212904Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25226633Sdim * SUCH DAMAGE. 26224145Sdim */ 27198092Srdivacky 28218893Sdim#include <sys/cdefs.h> 29218893Sdim__FBSDID("$FreeBSD$"); 30193326Sed 31218893Sdim/* 32218893Sdim * AR71XX gigabit ethernet driver 33193326Sed */ 34193326Sed#include <sys/param.h> 35193326Sed#include <sys/endian.h> 36234353Sdim#include <sys/systm.h> 37224145Sdim#include <sys/sockio.h> 38193326Sed#include <sys/mbuf.h> 39193326Sed#include <sys/malloc.h> 40226633Sdim#include <sys/kernel.h> 41193326Sed#include <sys/module.h> 42198092Srdivacky#include <sys/socket.h> 43193326Sed#include <sys/taskqueue.h> 44234353Sdim 45234353Sdim#include <net/if.h> 46226633Sdim#include <net/if_arp.h> 47226633Sdim#include <net/ethernet.h> 48198092Srdivacky#include <net/if_dl.h> 49234353Sdim#include <net/if_media.h> 50234353Sdim#include <net/if_types.h> 51234353Sdim 52234353Sdim#include <net/bpf.h> 53234353Sdim 54234353Sdim#include <machine/bus.h> 55234353Sdim#include <machine/cache.h> 56212904Sdim#include <machine/resource.h> 57234353Sdim#include <vm/vm_param.h> 58234353Sdim#include <vm/vm.h> 59234353Sdim#include <vm/pmap.h> 60234353Sdim#include <machine/pmap.h> 61234353Sdim#include <sys/bus.h> 62198092Srdivacky#include <sys/rman.h> 63198092Srdivacky 64212904Sdim#include <dev/mii/mii.h> 65212904Sdim#include <dev/mii/miivar.h> 66212904Sdim 67212904Sdim#include <dev/pci/pcireg.h> 68212904Sdim#include <dev/pci/pcivar.h> 69212904Sdim 70212904SdimMODULE_DEPEND(arge, ether, 1, 1, 1); 71212904SdimMODULE_DEPEND(arge, miibus, 1, 1, 1); 72212904Sdim 73212904Sdim#include "miibus_if.h" 74218893Sdim 75218893Sdim#include <mips/atheros/ar71xxreg.h> 76218893Sdim#include <mips/atheros/if_argevar.h> 77218893Sdim 78224145Sdim#undef ARGE_DEBUG 79224145Sdim#ifdef ARGE_DEBUG 80224145Sdim#define dprintf printf 81224145Sdim#else 82224145Sdim#define dprintf(x, arg...) 83224145Sdim#endif 84224145Sdim 85224145Sdimstatic int arge_attach(device_t); 86224145Sdimstatic int arge_detach(device_t); 87224145Sdimstatic int arge_fix_chain(struct mbuf **mp); 88224145Sdimstatic void arge_flush_ddr(struct arge_softc *); 89224145Sdimstatic int arge_ifmedia_upd(struct ifnet *); 90224145Sdimstatic void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *); 91224145Sdimstatic int arge_ioctl(struct ifnet *, u_long, caddr_t); 92224145Sdimstatic void arge_init(void *); 93224145Sdimstatic void arge_init_locked(struct arge_softc *); 94224145Sdimstatic void arge_link_task(void *, int); 95224145Sdimstatic int arge_miibus_readreg(device_t, int, int); 96234353Sdimstatic void arge_miibus_statchg(device_t); 97234353Sdimstatic int arge_miibus_writereg(device_t, int, int, int); 98234353Sdimstatic int arge_probe(device_t); 99234353Sdimstatic void arge_reset_dma(struct arge_softc *); 100224145Sdimstatic int arge_resume(device_t); 101224145Sdimstatic int arge_rx_ring_init(struct arge_softc *); 102224145Sdimstatic int arge_tx_ring_init(struct arge_softc *); 103224145Sdimstatic void arge_shutdown(device_t); 104224145Sdimstatic void arge_start(struct ifnet *); 105224145Sdimstatic void arge_start_locked(struct ifnet *); 106234353Sdimstatic void arge_stop(struct arge_softc *); 107224145Sdimstatic int arge_suspend(device_t); 108224145Sdim 109224145Sdimstatic void arge_rx_locked(struct arge_softc *); 110224145Sdimstatic void arge_tx_locked(struct arge_softc *); 111224145Sdimstatic void arge_intr(void *); 112224145Sdimstatic int arge_intr_filter(void *); 113224145Sdimstatic void arge_tx_intr(struct arge_softc *, uint32_t); 114224145Sdimstatic void arge_rx_intr(struct arge_softc *, uint32_t); 115224145Sdimstatic void arge_tick(void *); 116226633Sdim 117226633Sdimstatic void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int); 118226633Sdimstatic int arge_dma_alloc(struct arge_softc *); 119226633Sdimstatic void arge_dma_free(struct arge_softc *); 120226633Sdimstatic int arge_newbuf(struct arge_softc *, int); 121226633Sdimstatic __inline void arge_fixup_rx(struct mbuf *); 122226633Sdim 123202879Srdivackystatic device_method_t arge_methods[] = { 124226633Sdim /* Device interface */ 125226633Sdim DEVMETHOD(device_probe, arge_probe), 126226633Sdim DEVMETHOD(device_attach, arge_attach), 127226633Sdim DEVMETHOD(device_detach, arge_detach), 128226633Sdim DEVMETHOD(device_suspend, arge_suspend), 129226633Sdim DEVMETHOD(device_resume, arge_resume), 130226633Sdim DEVMETHOD(device_shutdown, arge_shutdown), 131226633Sdim 132226633Sdim /* bus interface */ 133226633Sdim DEVMETHOD(bus_print_child, bus_generic_print_child), 134226633Sdim DEVMETHOD(bus_driver_added, bus_generic_driver_added), 135202879Srdivacky 136202879Srdivacky /* MII interface */ 137226633Sdim DEVMETHOD(miibus_readreg, arge_miibus_readreg), 138226633Sdim DEVMETHOD(miibus_writereg, arge_miibus_writereg), 139226633Sdim DEVMETHOD(miibus_statchg, arge_miibus_statchg), 140226633Sdim 141226633Sdim { 0, 0 } 142226633Sdim}; 143226633Sdim 144226633Sdimstatic driver_t arge_driver = { 145226633Sdim "arge", 146226633Sdim arge_methods, 147202879Srdivacky sizeof(struct arge_softc) 148202879Srdivacky}; 149226633Sdim 150202879Srdivackystatic devclass_t arge_devclass; 151202879Srdivacky 152202879SrdivackyDRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); 153223017SdimDRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); 154221345Sdim 155202879Srdivacky/* 156202879Srdivacky * Flushes all 157202879Srdivacky */ 158202879Srdivackystatic void 159202879Srdivackyarge_flush_ddr(struct arge_softc *sc) 160202879Srdivacky{ 161202879Srdivacky 162202879Srdivacky ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); 163202879Srdivacky while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) 164202879Srdivacky ; 165202879Srdivacky 166202879Srdivacky ATH_WRITE_REG(sc->arge_ddr_flush_reg, 1); 167202879Srdivacky while (ATH_READ_REG(sc->arge_ddr_flush_reg) & 1) 168202879Srdivacky ; 169198092Srdivacky} 170193326Sed 171193326Sedstatic int 172193326Sedarge_probe(device_t dev) 173193326Sed{ 174193326Sed 175193326Sed device_set_desc(dev, "Atheros AR71xx built-in ethernet interface"); 176226633Sdim return (0); 177226633Sdim} 178226633Sdim 179212904Sdimstatic int 180212904Sdimarge_attach(device_t dev) 181212904Sdim{ 182212904Sdim uint8_t eaddr[ETHER_ADDR_LEN]; 183212904Sdim struct ifnet *ifp; 184223017Sdim struct arge_softc *sc; 185234353Sdim int error = 0, rid, phynum; 186234353Sdim uint32_t reg; 187234353Sdim 188234353Sdim sc = device_get_softc(dev); 189223017Sdim sc->arge_dev = dev; 190212904Sdim sc->arge_mac_unit = device_get_unit(dev); 191212904Sdim 192212904Sdim KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)), 193234353Sdim ("if_arge: Only MAC0 and MAC1 supported")); 194234353Sdim if (sc->arge_mac_unit == 0) { 195221345Sdim sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE0; 196221345Sdim sc->arge_pll_reg = AR71XX_PLL_ETH_INT0_CLK; 197193326Sed } else { 198221345Sdim sc->arge_ddr_flush_reg = AR71XX_WB_FLUSH_GE1; 199221345Sdim sc->arge_pll_reg = AR71XX_PLL_ETH_INT1_CLK; 200221345Sdim } 201221345Sdim 202221345Sdim /* 203226633Sdim * Get which PHY of 5 available we should use for this unit 204221345Sdim */ 205226633Sdim if (resource_int_value(device_get_name(dev), device_get_unit(dev), 206226633Sdim "phy", &phynum) != 0) { 207221345Sdim /* 208221345Sdim * Use port 4 (WAN) for GE0. For any other port use 209221345Sdim * its PHY the same as its unit number 210221345Sdim */ 211193326Sed if (sc->arge_mac_unit == 0) 212193326Sed phynum = 4; 213208600Srdivacky else 214208600Srdivacky phynum = sc->arge_mac_unit; 215234353Sdim 216208600Srdivacky device_printf(dev, "No PHY specified, using %d\n", phynum); 217193326Sed } 218193326Sed 219193326Sed sc->arge_phy_num = phynum; 220193326Sed 221193326Sed 222226633Sdim mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 223193326Sed MTX_DEF); 224193326Sed callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); 225193326Sed TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc); 226234353Sdim 227193326Sed /* Map control/status registers. */ 228193326Sed sc->arge_rid = 0; 229193326Sed sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 230193326Sed &sc->arge_rid, RF_ACTIVE); 231208600Srdivacky 232208600Srdivacky if (sc->arge_res == NULL) { 233208600Srdivacky device_printf(dev, "couldn't map memory\n"); 234208600Srdivacky error = ENXIO; 235208600Srdivacky goto fail; 236208600Srdivacky } 237208600Srdivacky 238193326Sed /* Allocate interrupts */ 239198092Srdivacky rid = 0; 240193326Sed sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 241193326Sed RF_SHAREABLE | RF_ACTIVE); 242210299Sed 243210299Sed if (sc->arge_irq == NULL) { 244226633Sdim device_printf(dev, "couldn't map interrupt\n"); 245226633Sdim error = ENXIO; 246193326Sed goto fail; 247193326Sed } 248193326Sed 249193326Sed /* Allocate ifnet structure. */ 250193326Sed ifp = sc->arge_ifp = if_alloc(IFT_ETHER); 251193326Sed 252207619Srdivacky if (ifp == NULL) { 253234353Sdim device_printf(dev, "couldn't allocate ifnet structure\n"); 254234353Sdim error = ENOSPC; 255198092Srdivacky goto fail; 256218893Sdim } 257218893Sdim 258218893Sdim ifp->if_softc = sc; 259218893Sdim if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 260198092Srdivacky ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 261212904Sdim ifp->if_ioctl = arge_ioctl; 262212904Sdim ifp->if_start = arge_start; 263212904Sdim ifp->if_init = arge_init; 264218893Sdim 265218893Sdim /* XXX: add real size */ 266221345Sdim IFQ_SET_MAXLEN(&ifp->if_snd, 9); 267221345Sdim ifp->if_snd.ifq_maxlen = 9; 268226633Sdim IFQ_SET_READY(&ifp->if_snd); 269226633Sdim 270226633Sdim ifp->if_capenable = ifp->if_capabilities; 271226633Sdim 272226633Sdim eaddr[0] = 0x00; 273226633Sdim eaddr[1] = 0x15; 274226633Sdim eaddr[2] = 0x6d; 275226633Sdim eaddr[3] = 0xc1; 276218893Sdim eaddr[4] = 0x28; 277226633Sdim eaddr[5] = 0x2e; 278218893Sdim 279218893Sdim if (arge_dma_alloc(sc) != 0) { 280218893Sdim error = ENXIO; 281226633Sdim goto fail; 282218893Sdim } 283218893Sdim 284218893Sdim ARGE_WRITE(sc, AR71XX_MAC_CFG1, 285226633Sdim MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE | 286218893Sdim MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE); 287218893Sdim 288198092Srdivacky reg = ARGE_READ(sc, AR71XX_MAC_CFG2); 289198092Srdivacky reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ; 290198092Srdivacky ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg); 291198092Srdivacky 292198092Srdivacky ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); 293210299Sed 294210299Sed /* Reset MII bus */ 295210299Sed ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); 296210299Sed DELAY(100); 297234353Sdim ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); 298234353Sdim DELAY(100); 299210299Sed 300210299Sed /* 301234353Sdim * Set all Ethernet address registers to the same initial values 302210299Sed * set all four addresses to 66-88-aa-cc-dd-ee 303210299Sed */ 304210299Sed ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, 0x6dc1282e); 305210299Sed ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, 0x00000015); 306226633Sdim 307210299Sed ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, 308210299Sed FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); 309210299Sed ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000); 310210299Sed ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff); 311210299Sed 312210299Sed reg = FIFO_RX_FILTMATCH_ALL; 313210299Sed ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH, reg); 314210299Sed 315210299Sed reg = FIFO_RX_FILTMASK_ALL; 316210299Sed reg &= ~FIFO_RX_FILTMASK_BYTE_MODE; 317210299Sed ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, reg); 318210299Sed 319210299Sed /* Do MII setup. */ 320210299Sed if (mii_phy_probe(dev, &sc->arge_miibus, 321212904Sdim arge_ifmedia_upd, arge_ifmedia_sts)) { 322212904Sdim device_printf(dev, "MII without any phy!\n"); 323212904Sdim error = ENXIO; 324212904Sdim goto fail; 325212904Sdim } 326212904Sdim 327212904Sdim /* Call MI attach routine. */ 328212904Sdim ether_ifattach(ifp, eaddr); 329212904Sdim 330226633Sdim /* Hook interrupt last to avoid having to lock softc */ 331212904Sdim error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, 332212904Sdim arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); 333212904Sdim 334212904Sdim if (error) { 335212904Sdim device_printf(dev, "couldn't set up irq\n"); 336212904Sdim ether_ifdetach(ifp); 337212904Sdim goto fail; 338212904Sdim } 339212904Sdim 340212904Sdimfail: 341212904Sdim if (error) 342212904Sdim arge_detach(dev); 343212904Sdim 344212904Sdim return (error); 345212904Sdim} 346212904Sdim 347212904Sdimstatic int 348212904Sdimarge_detach(device_t dev) 349218893Sdim{ 350218893Sdim struct arge_softc *sc = device_get_softc(dev); 351212904Sdim struct ifnet *ifp = sc->arge_ifp; 352212904Sdim 353212904Sdim KASSERT(mtx_initialized(&sc->arge_mtx), ("arge mutex not initialized")); 354212904Sdim 355218893Sdim /* These should only be active if attach succeeded */ 356218893Sdim if (device_is_attached(dev)) { 357210299Sed ARGE_LOCK(sc); 358198092Srdivacky sc->arge_detach = 1; 359198092Srdivacky arge_stop(sc); 360224145Sdim ARGE_UNLOCK(sc); 361224145Sdim taskqueue_drain(taskqueue_swi, &sc->arge_link_task); 362226633Sdim ether_ifdetach(ifp); 363226633Sdim } 364224145Sdim 365224145Sdim if (sc->arge_miibus) 366224145Sdim device_delete_child(dev, sc->arge_miibus); 367224145Sdim bus_generic_detach(dev); 368224145Sdim 369224145Sdim if (sc->arge_intrhand) 370224145Sdim bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand); 371224145Sdim 372234353Sdim if (sc->arge_res) 373234353Sdim bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid, 374234353Sdim sc->arge_res); 375224145Sdim 376234353Sdim if (ifp) 377224145Sdim if_free(ifp); 378224145Sdim 379224145Sdim arge_dma_free(sc); 380224145Sdim 381224145Sdim mtx_destroy(&sc->arge_mtx); 382224145Sdim 383224145Sdim return (0); 384224145Sdim 385224145Sdim} 386224145Sdim 387226633Sdimstatic int 388224145Sdimarge_suspend(device_t dev) 389224145Sdim{ 390224145Sdim 391224145Sdim panic("%s", __func__); 392224145Sdim return 0; 393226633Sdim} 394224145Sdim 395224145Sdimstatic int 396224145Sdimarge_resume(device_t dev) 397224145Sdim{ 398224145Sdim 399224145Sdim panic("%s", __func__); 400224145Sdim return 0; 401198092Srdivacky} 402198092Srdivacky 403234353Sdimstatic void 404234353Sdimarge_shutdown(device_t dev) 405234353Sdim{ 406234353Sdim struct arge_softc *sc; 407234353Sdim 408234353Sdim sc = device_get_softc(dev); 409234353Sdim 410234353Sdim ARGE_LOCK(sc); 411234353Sdim arge_stop(sc); 412234353Sdim ARGE_UNLOCK(sc); 413202879Srdivacky} 414202879Srdivacky 415202879Srdivackystatic int 416198092Srdivackyarge_miibus_readreg(device_t dev, int phy, int reg) 417198092Srdivacky{ 418198092Srdivacky struct arge_softc * sc = device_get_softc(dev); 419198092Srdivacky int i, result; 420198092Srdivacky uint32_t addr = 0x1000 | (phy << MAC_MII_PHY_ADDR_SHIFT) 421198092Srdivacky | (reg & MAC_MII_REG_MASK); 422226633Sdim 423198092Srdivacky if (phy != sc->arge_phy_num) 424198092Srdivacky return (0); 425198092Srdivacky 426198092Srdivacky ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); 427234353Sdim ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); 428234353Sdim ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); 429234353Sdim 430234353Sdim i = ARGE_MII_TIMEOUT; 431234353Sdim while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & 432234353Sdim MAC_MII_INDICATOR_BUSY) && (i--)) 433234353Sdim DELAY(5); 434234353Sdim 435234353Sdim if (i < 0) { 436234353Sdim dprintf("%s timedout\n", __func__); 437234353Sdim /* XXX: return ERRNO istead? */ 438234353Sdim return (-1); 439234353Sdim } 440234353Sdim 441234353Sdim result = ARGE_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; 442234353Sdim ARGE_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); 443234353Sdim dprintf("%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, 444234353Sdim phy, reg, addr, result); 445234353Sdim 446234353Sdim return (result); 447234353Sdim} 448234353Sdim 449234353Sdimstatic int 450234353Sdimarge_miibus_writereg(device_t dev, int phy, int reg, int data) 451234353Sdim{ 452234353Sdim struct arge_softc * sc = device_get_softc(dev); 453234353Sdim int i; 454234353Sdim uint32_t addr = 0x1000 455234353Sdim | (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); 456234353Sdim 457234353Sdim dprintf("%s: phy=%d, reg=%02x, value=%04x\n", __func__, 458234353Sdim phy, reg, data); 459202879Srdivacky 460202879Srdivacky ARGE_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); 461198092Srdivacky ARGE_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); 462202879Srdivacky 463202879Srdivacky i = ARGE_MII_TIMEOUT; 464203955Srdivacky while ((ARGE_READ(sc, AR71XX_MAC_MII_INDICATOR) & 465221345Sdim MAC_MII_INDICATOR_BUSY) && (i--)) 466221345Sdim DELAY(5); 467226633Sdim 468226633Sdim if (i < 0) { 469226633Sdim dprintf("%s timedout\n", __func__); 470202879Srdivacky /* XXX: return ERRNO istead? */ 471221345Sdim return (-1); 472224145Sdim } 473202879Srdivacky 474202879Srdivacky return (0); 475202879Srdivacky} 476203955Srdivacky 477202879Srdivackystatic void 478203955Srdivackyarge_miibus_statchg(device_t dev) 479202879Srdivacky{ 480202879Srdivacky struct arge_softc *sc; 481218893Sdim 482221345Sdim sc = device_get_softc(dev); 483218893Sdim taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task); 484218893Sdim} 485218893Sdim 486218893Sdimstatic void 487218893Sdimarge_link_task(void *arg, int pending) 488218893Sdim{ 489218893Sdim struct arge_softc *sc; 490224145Sdim struct mii_data *mii; 491218893Sdim struct ifnet *ifp; 492218893Sdim uint32_t media; 493224145Sdim uint32_t cfg, ifcontrol, rx_filtmask, pll, sec_cfg; 494224145Sdim 495218893Sdim sc = (struct arge_softc *)arg; 496202879Srdivacky 497224145Sdim ARGE_LOCK(sc); 498202879Srdivacky mii = device_get_softc(sc->arge_miibus); 499226633Sdim ifp = sc->arge_ifp; 500224145Sdim if (mii == NULL || ifp == NULL || 501224145Sdim (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 502202879Srdivacky ARGE_UNLOCK(sc); 503224145Sdim return; 504224145Sdim } 505224145Sdim 506224145Sdim if (mii->mii_media_status & IFM_ACTIVE) { 507224145Sdim 508226633Sdim media = IFM_SUBTYPE(mii->mii_media_active); 509224145Sdim 510224145Sdim if (media != IFM_NONE) { 511224145Sdim sc->arge_link_status = 1; 512226633Sdim 513224145Sdim cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); 514224145Sdim ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL); 515224145Sdim rx_filtmask = 516224145Sdim ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK); 517224145Sdim 518224145Sdim cfg &= ~(MAC_CFG2_IFACE_MODE_1000 519224145Sdim | MAC_CFG2_IFACE_MODE_10_100 520224145Sdim | MAC_CFG2_FULL_DUPLEX); 521224145Sdim ifcontrol &= ~MAC_IFCONTROL_SPEED; 522224145Sdim rx_filtmask &= ~FIFO_RX_FILTMASK_BYTE_MODE; 523224145Sdim 524224145Sdim switch(media) { 525224145Sdim case IFM_10_T: 526198092Srdivacky cfg |= MAC_CFG2_IFACE_MODE_10_100; 527198092Srdivacky pll = PLL_ETH_INT_CLK_10; 528212904Sdim break; 529201361Srdivacky case IFM_100_TX: 530193326Sed cfg |= MAC_CFG2_IFACE_MODE_10_100; 531203955Srdivacky ifcontrol |= MAC_IFCONTROL_SPEED; 532221345Sdim pll = PLL_ETH_INT_CLK_100; 533221345Sdim break; 534221345Sdim case IFM_1000_T: 535224145Sdim case IFM_1000_SX: 536224145Sdim cfg |= MAC_CFG2_IFACE_MODE_1000; 537224145Sdim rx_filtmask |= FIFO_RX_FILTMASK_BYTE_MODE; 538224145Sdim pll = PLL_ETH_INT_CLK_1000; 539224145Sdim break; 540234353Sdim default: 541224145Sdim pll = PLL_ETH_INT_CLK_100; 542224145Sdim device_printf(sc->arge_dev, 543226633Sdim "Unknown media %d\n", media); 544224145Sdim } 545226633Sdim 546224145Sdim ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, 547224145Sdim 0x008001ff); 548224145Sdim 549224145Sdim ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg); 550226633Sdim ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol); 551226633Sdim ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, 552224145Sdim rx_filtmask); 553224145Sdim 554224145Sdim /* set PLL registers */ 555224145Sdim sec_cfg = ATH_READ_REG(AR71XX_PLL_CPU_CONFIG); 556224145Sdim sec_cfg &= ~(3 << 17); 557224145Sdim sec_cfg |= (2 << 17); 558221345Sdim 559226633Sdim ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); 560193326Sed DELAY(100); 561221345Sdim 562221345Sdim ATH_WRITE_REG(sc->arge_pll_reg, pll); 563221345Sdim 564226633Sdim sec_cfg |= (3 << 17); 565221345Sdim ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); 566221345Sdim DELAY(100); 567221345Sdim 568221345Sdim sec_cfg &= ~(3 << 17); 569226633Sdim ATH_WRITE_REG(AR71XX_PLL_CPU_CONFIG, sec_cfg); 570203955Srdivacky DELAY(100); 571226633Sdim } 572226633Sdim } else 573226633Sdim sc->arge_link_status = 0; 574226633Sdim 575226633Sdim ARGE_UNLOCK(sc); 576226633Sdim} 577226633Sdim 578226633Sdimstatic void 579226633Sdimarge_reset_dma(struct arge_softc *sc) 580203955Srdivacky{ 581226633Sdim unsigned int i; 582226633Sdim 583234982Sdim ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0); 584226633Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0); 585226633Sdim 586226633Sdim ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0); 587226633Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0); 588226633Sdim 589226633Sdim /* Clear all possible RX interrupts */ 590226633Sdim for (i = 0; i < ARGE_RX_RING_COUNT; i++) 591226633Sdim ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); 592226633Sdim 593203955Srdivacky /* 594226633Sdim * Clear all possible TX interrupts 595226633Sdim */ 596226633Sdim for (i = 0; i < ARGE_TX_RING_COUNT; i++) 597226633Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); 598226633Sdim 599226633Sdim /* 600221345Sdim * Now Rx/Tx errors 601193326Sed */ 602203955Srdivacky ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, 603221345Sdim DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW); 604221345Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, 605226633Sdim DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN); 606226633Sdim} 607221345Sdim 608226633Sdim 609221345Sdim 610221345Sdimstatic void 611221345Sdimarge_init(void *xsc) 612221345Sdim{ 613221345Sdim struct arge_softc *sc = xsc; 614226633Sdim 615203955Srdivacky ARGE_LOCK(sc); 616203955Srdivacky arge_init_locked(sc); 617226633Sdim ARGE_UNLOCK(sc); 618203955Srdivacky} 619226633Sdim 620203955Srdivackystatic void 621193326Sedarge_init_locked(struct arge_softc *sc) 622226633Sdim{ 623198092Srdivacky struct ifnet *ifp = sc->arge_ifp; 624212904Sdim struct mii_data *mii; 625212904Sdim 626226633Sdim ARGE_LOCK_ASSERT(sc); 627203955Srdivacky 628221345Sdim mii = device_get_softc(sc->arge_miibus); 629221345Sdim 630226633Sdim arge_stop(sc); 631221345Sdim 632221345Sdim /* Init circular RX list. */ 633221345Sdim if (arge_rx_ring_init(sc) != 0) { 634221345Sdim device_printf(sc->arge_dev, 635198092Srdivacky "initialization failed: no memory for rx buffers\n"); 636210299Sed arge_stop(sc); 637210299Sed return; 638212904Sdim } 639212904Sdim 640198092Srdivacky /* Init tx descriptors. */ 641193326Sed arge_tx_ring_init(sc); 642198092Srdivacky 643221345Sdim arge_reset_dma(sc); 644221345Sdim 645221345Sdim sc->arge_link_status = 0; 646226633Sdim mii_mediachg(mii); 647221345Sdim 648221345Sdim ifp->if_drv_flags |= IFF_DRV_RUNNING; 649221345Sdim ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 650203955Srdivacky 651203955Srdivacky callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); 652203955Srdivacky ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0)); 653203955Srdivacky ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0)); 654221345Sdim 655203955Srdivacky /* Start listening */ 656203955Srdivacky ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN); 657221345Sdim 658226633Sdim /* Enable interrupts */ 659203955Srdivacky ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL); 660203955Srdivacky} 661221345Sdim 662221345Sdim/* 663221345Sdim * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 664203955Srdivacky * pointers to the fragment pointers. 665203955Srdivacky */ 666226633Sdimstatic int 667221345Sdimarge_encap(struct arge_softc *sc, struct mbuf **m_head) 668203955Srdivacky{ 669221345Sdim struct arge_txdesc *txd; 670221345Sdim struct arge_desc *desc, *prev_desc; 671221345Sdim bus_dma_segment_t txsegs[ARGE_MAXFRAGS]; 672221345Sdim int error, i, nsegs, prod, si, prev_prod; 673221345Sdim 674221345Sdim ARGE_LOCK_ASSERT(sc); 675221345Sdim 676221345Sdim prod = sc->arge_cdata.arge_tx_prod; 677221345Sdim txd = &sc->arge_cdata.arge_txdesc[prod]; 678221345Sdim error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag, 679221345Sdim txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); 680221345Sdim 681212904Sdim if (error == EFBIG) { 682203955Srdivacky panic("EFBIG"); 683218893Sdim } else if (error != 0) 684218893Sdim return (error); 685218893Sdim 686218893Sdim if (nsegs == 0) { 687218893Sdim m_freem(*m_head); 688218893Sdim *m_head = NULL; 689218893Sdim return (EIO); 690218893Sdim } 691218893Sdim 692218893Sdim /* Check number of available descriptors. */ 693218893Sdim if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) { 694218893Sdim bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); 695218893Sdim return (ENOBUFS); 696218893Sdim } 697218893Sdim 698218893Sdim txd->tx_m = *m_head; 699218893Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, 700218893Sdim BUS_DMASYNC_PREWRITE); 701218893Sdim 702218893Sdim si = prod; 703218893Sdim 704218893Sdim /* 705218893Sdim * Make a list of descriptors for this packet. DMA controller will 706218893Sdim * walk through it while arge_link is not zero. 707218893Sdim */ 708218893Sdim prev_prod = prod; 709218893Sdim desc = prev_desc = NULL; 710218893Sdim for (i = 0; i < nsegs; i++) { 711218893Sdim desc = &sc->arge_rdata.arge_tx_ring[prod]; 712218893Sdim desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len); 713218893Sdim 714218893Sdim desc->packet_addr = txsegs[i].ds_addr; 715218893Sdim /* link with previous descriptor */ 716218893Sdim if (prev_desc) 717234353Sdim prev_desc->packet_ctrl |= ARGE_DESC_MORE; 718234353Sdim 719218893Sdim sc->arge_cdata.arge_tx_cnt++; 720218893Sdim prev_desc = desc; 721218893Sdim ARGE_INC(prod, ARGE_TX_RING_COUNT); 722218893Sdim } 723218893Sdim 724218893Sdim /* Update producer index. */ 725218893Sdim sc->arge_cdata.arge_tx_prod = prod; 726218893Sdim 727218893Sdim /* Sync descriptors. */ 728218893Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, 729218893Sdim sc->arge_cdata.arge_tx_ring_map, 730218893Sdim BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 731218893Sdim 732218893Sdim /* Start transmitting */ 733218893Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN); 734218893Sdim return (0); 735218893Sdim} 736218893Sdim 737218893Sdimstatic void 738218893Sdimarge_start(struct ifnet *ifp) 739218893Sdim{ 740218893Sdim struct arge_softc *sc; 741218893Sdim 742218893Sdim sc = ifp->if_softc; 743223017Sdim 744218893Sdim ARGE_LOCK(sc); 745218893Sdim arge_start_locked(ifp); 746218893Sdim ARGE_UNLOCK(sc); 747218893Sdim} 748218893Sdim 749218893Sdimstatic void 750218893Sdimarge_start_locked(struct ifnet *ifp) 751212904Sdim{ 752212904Sdim struct arge_softc *sc; 753212904Sdim struct mbuf *m_head; 754212904Sdim int enq; 755212904Sdim 756212904Sdim sc = ifp->if_softc; 757212904Sdim 758212904Sdim ARGE_LOCK_ASSERT(sc); 759212904Sdim 760212904Sdim if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 761212904Sdim IFF_DRV_RUNNING || sc->arge_link_status == 0 ) 762212904Sdim return; 763210299Sed 764210299Sed arge_flush_ddr(sc); 765193326Sed 766193326Sed for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && 767193326Sed sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) { 768224145Sdim IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 769224145Sdim if (m_head == NULL) 770224145Sdim break; 771193326Sed 772226633Sdim /* 773224145Sdim * Fix mbuf chain, all fragments should be 4 bytes aligned and 774224145Sdim * even 4 bytes 775193326Sed */ 776193326Sed arge_fix_chain(&m_head); 777218893Sdim 778210299Sed if (m_head == NULL) { 779210299Sed dprintf("failed to adjust mbuf chain\n"); 780193326Sed } 781198092Srdivacky 782193326Sed /* 783193326Sed * Pack the data into the transmit ring. 784193326Sed */ 785193326Sed if (arge_encap(sc, &m_head)) { 786193326Sed if (m_head == NULL) 787193326Sed break; 788193326Sed IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 789193326Sed ifp->if_drv_flags |= IFF_DRV_OACTIVE; 790221345Sdim break; 791226633Sdim } 792193326Sed 793193326Sed enq++; 794221345Sdim /* 795226633Sdim * If there's a BPF listener, bounce a copy of this frame 796221345Sdim * to him. 797221345Sdim */ 798193326Sed ETHER_BPF_MTAP(ifp, m_head); 799193326Sed } 800193326Sed} 801193326Sed 802210299Sedstatic void 803210299Sedarge_stop(struct arge_softc *sc) 804218893Sdim{ 805218893Sdim struct ifnet *ifp; 806218893Sdim 807218893Sdim ARGE_LOCK_ASSERT(sc); 808218893Sdim 809218893Sdim ifp = sc->arge_ifp; 810218893Sdim ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 811218893Sdim callout_stop(&sc->arge_stat_callout); 812218893Sdim 813218893Sdim /* mask out interrupts */ 814223017Sdim ARGE_WRITE(sc, AR71XX_DMA_INTR, 0); 815218893Sdim 816218893Sdim arge_reset_dma(sc); 817218893Sdim} 818198092Srdivacky 819193326Sed 820193326Sedstatic int 821193326Sedarge_ioctl(struct ifnet *ifp, u_long command, caddr_t data) 822193326Sed{ 823199512Srdivacky struct arge_softc *sc = ifp->if_softc; 824193326Sed struct ifreq *ifr = (struct ifreq *) data; 825193326Sed struct mii_data *mii; 826193326Sed int error; 827193326Sed 828193326Sed switch (command) { 829193326Sed case SIOCSIFFLAGS: 830193326Sed printf("Implement me: SIOCSIFFLAGS\n"); 831210299Sed error = 0; 832193326Sed break; 833198092Srdivacky case SIOCADDMULTI: 834193326Sed case SIOCDELMULTI: 835210299Sed printf("Implement me: SIOCDELMULTI\n"); 836210299Sed error = 0; 837193326Sed break; 838193326Sed case SIOCGIFMEDIA: 839193326Sed case SIOCSIFMEDIA: 840210299Sed printf("Implement me: SIOCSIFMEDIA\n"); 841210299Sed mii = device_get_softc(sc->arge_miibus); 842210299Sed error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 843193326Sed break; 844193326Sed case SIOCSIFCAP: 845193326Sed error = 0; 846210299Sed ifp->if_hwassist = 0; 847210299Sed printf("Implement me: SIOCSIFCAP\n"); 848210299Sed break; 849193326Sed default: 850193326Sed error = ether_ioctl(ifp, command, data); 851193326Sed break; 852210299Sed } 853193326Sed 854193326Sed return (error); 855193326Sed} 856210299Sed 857193326Sed/* 858193326Sed * Set media options. 859193326Sed */ 860210299Sedstatic int 861193326Sedarge_ifmedia_upd(struct ifnet *ifp) 862193326Sed{ 863193326Sed struct arge_softc *sc; 864210299Sed struct mii_data *mii; 865210299Sed struct mii_softc *miisc; 866193326Sed int error; 867193326Sed 868193326Sed sc = ifp->if_softc; 869210299Sed ARGE_LOCK(sc); 870210299Sed mii = device_get_softc(sc->arge_miibus); 871193326Sed if (mii->mii_instance) { 872193326Sed LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 873193326Sed mii_phy_reset(miisc); 874210299Sed } 875193326Sed error = mii_mediachg(mii); 876193326Sed ARGE_UNLOCK(sc); 877193326Sed 878210299Sed return (error); 879193326Sed} 880193326Sed 881193326Sed/* 882193326Sed * Report current media status. 883198092Srdivacky */ 884198092Srdivackystatic void 885199512Srdivackyarge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 886210299Sed{ 887198092Srdivacky struct arge_softc *sc = ifp->if_softc; 888198092Srdivacky struct mii_data *mii; 889198092Srdivacky 890198092Srdivacky mii = device_get_softc(sc->arge_miibus); 891226633Sdim ARGE_LOCK(sc); 892198092Srdivacky mii_pollstat(mii); 893198092Srdivacky ARGE_UNLOCK(sc); 894198092Srdivacky ifmr->ifm_active = mii->mii_media_active; 895198092Srdivacky ifmr->ifm_status = mii->mii_media_status; 896198092Srdivacky} 897198092Srdivacky 898198092Srdivackystruct arge_dmamap_arg { 899198092Srdivacky bus_addr_t arge_busaddr; 900210299Sed}; 901198092Srdivacky 902210299Sedstatic void 903198092Srdivackyarge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 904210299Sed{ 905198092Srdivacky struct arge_dmamap_arg *ctx; 906210299Sed 907198092Srdivacky if (error != 0) 908210299Sed return; 909198092Srdivacky ctx = arg; 910210299Sed ctx->arge_busaddr = segs[0].ds_addr; 911198092Srdivacky} 912210299Sed 913198092Srdivackystatic int 914210299Sedarge_dma_alloc(struct arge_softc *sc) 915198092Srdivacky{ 916198092Srdivacky struct arge_dmamap_arg ctx; 917210299Sed struct arge_txdesc *txd; 918193326Sed struct arge_rxdesc *rxd; 919198092Srdivacky int error, i; 920198092Srdivacky 921198092Srdivacky /* Create parent DMA tag. */ 922210299Sed error = bus_dma_tag_create( 923198092Srdivacky bus_get_dma_tag(sc->arge_dev), /* parent */ 924210299Sed 1, 0, /* alignment, boundary */ 925198092Srdivacky BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 926210299Sed BUS_SPACE_MAXADDR, /* highaddr */ 927198092Srdivacky NULL, NULL, /* filter, filterarg */ 928210299Sed BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 929198092Srdivacky 0, /* nsegments */ 930210299Sed BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 931198092Srdivacky 0, /* flags */ 932210299Sed NULL, NULL, /* lockfunc, lockarg */ 933198092Srdivacky &sc->arge_cdata.arge_parent_tag); 934210299Sed if (error != 0) { 935193326Sed device_printf(sc->arge_dev, "failed to create parent DMA tag\n"); 936198092Srdivacky goto fail; 937210299Sed } 938198092Srdivacky /* Create tag for Tx ring. */ 939198092Srdivacky error = bus_dma_tag_create( 940210299Sed sc->arge_cdata.arge_parent_tag, /* parent */ 941198092Srdivacky ARGE_RING_ALIGN, 0, /* alignment, boundary */ 942210299Sed BUS_SPACE_MAXADDR, /* lowaddr */ 943198092Srdivacky BUS_SPACE_MAXADDR, /* highaddr */ 944210299Sed NULL, NULL, /* filter, filterarg */ 945198092Srdivacky ARGE_TX_DMA_SIZE, /* maxsize */ 946210299Sed 1, /* nsegments */ 947198092Srdivacky ARGE_TX_DMA_SIZE, /* maxsegsize */ 948210299Sed 0, /* flags */ 949198092Srdivacky NULL, NULL, /* lockfunc, lockarg */ 950210299Sed &sc->arge_cdata.arge_tx_ring_tag); 951198092Srdivacky if (error != 0) { 952198092Srdivacky device_printf(sc->arge_dev, "failed to create Tx ring DMA tag\n"); 953200583Srdivacky goto fail; 954198092Srdivacky } 955198092Srdivacky 956212904Sdim /* Create tag for Rx ring. */ 957212904Sdim error = bus_dma_tag_create( 958212904Sdim sc->arge_cdata.arge_parent_tag, /* parent */ 959212904Sdim ARGE_RING_ALIGN, 0, /* alignment, boundary */ 960212904Sdim BUS_SPACE_MAXADDR, /* lowaddr */ 961226633Sdim BUS_SPACE_MAXADDR, /* highaddr */ 962226633Sdim NULL, NULL, /* filter, filterarg */ 963226633Sdim ARGE_RX_DMA_SIZE, /* maxsize */ 964226633Sdim 1, /* nsegments */ 965226633Sdim ARGE_RX_DMA_SIZE, /* maxsegsize */ 966226633Sdim 0, /* flags */ 967226633Sdim NULL, NULL, /* lockfunc, lockarg */ 968226633Sdim &sc->arge_cdata.arge_rx_ring_tag); 969226633Sdim if (error != 0) { 970226633Sdim device_printf(sc->arge_dev, "failed to create Rx ring DMA tag\n"); 971226633Sdim goto fail; 972226633Sdim } 973226633Sdim 974226633Sdim /* Create tag for Tx buffers. */ 975226633Sdim error = bus_dma_tag_create( 976226633Sdim sc->arge_cdata.arge_parent_tag, /* parent */ 977226633Sdim sizeof(uint32_t), 0, /* alignment, boundary */ 978226633Sdim BUS_SPACE_MAXADDR, /* lowaddr */ 979226633Sdim BUS_SPACE_MAXADDR, /* highaddr */ 980226633Sdim NULL, NULL, /* filter, filterarg */ 981226633Sdim MCLBYTES * ARGE_MAXFRAGS, /* maxsize */ 982226633Sdim ARGE_MAXFRAGS, /* nsegments */ 983193326Sed MCLBYTES, /* maxsegsize */ 984198092Srdivacky 0, /* flags */ 985193326Sed NULL, NULL, /* lockfunc, lockarg */ 986198092Srdivacky &sc->arge_cdata.arge_tx_tag); 987193326Sed if (error != 0) { 988193326Sed device_printf(sc->arge_dev, "failed to create Tx DMA tag\n"); 989193326Sed goto fail; 990193326Sed } 991193326Sed 992201361Srdivacky /* Create tag for Rx buffers. */ 993201361Srdivacky error = bus_dma_tag_create( 994201361Srdivacky sc->arge_cdata.arge_parent_tag, /* parent */ 995201361Srdivacky ARGE_RX_ALIGN, 0, /* alignment, boundary */ 996201361Srdivacky BUS_SPACE_MAXADDR, /* lowaddr */ 997201361Srdivacky BUS_SPACE_MAXADDR, /* highaddr */ 998203955Srdivacky NULL, NULL, /* filter, filterarg */ 999203955Srdivacky MCLBYTES, /* maxsize */ 1000203955Srdivacky 1, /* nsegments */ 1001203955Srdivacky MCLBYTES, /* maxsegsize */ 1002203955Srdivacky 0, /* flags */ 1003203955Srdivacky NULL, NULL, /* lockfunc, lockarg */ 1004198092Srdivacky &sc->arge_cdata.arge_rx_tag); 1005193326Sed if (error != 0) { 1006193326Sed device_printf(sc->arge_dev, "failed to create Rx DMA tag\n"); 1007193326Sed goto fail; 1008198092Srdivacky } 1009193326Sed 1010193326Sed /* Allocate DMA'able memory and load the DMA map for Tx ring. */ 1011193326Sed error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag, 1012193326Sed (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK | 1013193326Sed BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_tx_ring_map); 1014221345Sdim if (error != 0) { 1015221345Sdim device_printf(sc->arge_dev, 1016221345Sdim "failed to allocate DMA'able memory for Tx ring\n"); 1017221345Sdim goto fail; 1018221345Sdim } 1019207619Srdivacky 1020207619Srdivacky ctx.arge_busaddr = 0; 1021207619Srdivacky error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag, 1022207619Srdivacky sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring, 1023207619Srdivacky ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); 1024234353Sdim if (error != 0 || ctx.arge_busaddr == 0) { 1025234353Sdim device_printf(sc->arge_dev, 1026234353Sdim "failed to load DMA'able memory for Tx ring\n"); 1027234353Sdim goto fail; 1028212904Sdim } 1029226633Sdim sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr; 1030226633Sdim 1031226633Sdim /* Allocate DMA'able memory and load the DMA map for Rx ring. */ 1032212904Sdim error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag, 1033212904Sdim (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK | 1034193326Sed BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->arge_cdata.arge_rx_ring_map); 1035193326Sed if (error != 0) { 1036193326Sed device_printf(sc->arge_dev, 1037193326Sed "failed to allocate DMA'able memory for Rx ring\n"); 1038234353Sdim goto fail; 1039234353Sdim } 1040234353Sdim 1041234353Sdim ctx.arge_busaddr = 0; 1042234353Sdim error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag, 1043234353Sdim sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring, 1044234353Sdim ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0); 1045234353Sdim if (error != 0 || ctx.arge_busaddr == 0) { 1046234353Sdim device_printf(sc->arge_dev, 1047234353Sdim "failed to load DMA'able memory for Rx ring\n"); 1048234353Sdim goto fail; 1049234353Sdim } 1050234353Sdim sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr; 1051234353Sdim 1052234353Sdim /* Create DMA maps for Tx buffers. */ 1053234353Sdim for (i = 0; i < ARGE_TX_RING_COUNT; i++) { 1054234353Sdim txd = &sc->arge_cdata.arge_txdesc[i]; 1055234353Sdim txd->tx_m = NULL; 1056234353Sdim txd->tx_dmamap = NULL; 1057234353Sdim error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0, 1058234353Sdim &txd->tx_dmamap); 1059234353Sdim if (error != 0) { 1060234353Sdim device_printf(sc->arge_dev, 1061234353Sdim "failed to create Tx dmamap\n"); 1062234353Sdim goto fail; 1063234353Sdim } 1064234353Sdim } 1065234353Sdim /* Create DMA maps for Rx buffers. */ 1066234353Sdim if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, 1067234353Sdim &sc->arge_cdata.arge_rx_sparemap)) != 0) { 1068234353Sdim device_printf(sc->arge_dev, 1069234353Sdim "failed to create spare Rx dmamap\n"); 1070234353Sdim goto fail; 1071234353Sdim } 1072234353Sdim for (i = 0; i < ARGE_RX_RING_COUNT; i++) { 1073234353Sdim rxd = &sc->arge_cdata.arge_rxdesc[i]; 1074234353Sdim rxd->rx_m = NULL; 1075234353Sdim rxd->rx_dmamap = NULL; 1076234353Sdim error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0, 1077234353Sdim &rxd->rx_dmamap); 1078234353Sdim if (error != 0) { 1079234353Sdim device_printf(sc->arge_dev, 1080234353Sdim "failed to create Rx dmamap\n"); 1081234353Sdim goto fail; 1082234353Sdim } 1083234353Sdim } 1084234353Sdim 1085234353Sdimfail: 1086234353Sdim return (error); 1087234353Sdim} 1088234353Sdim 1089234982Sdimstatic void 1090234353Sdimarge_dma_free(struct arge_softc *sc) 1091234353Sdim{ 1092234353Sdim struct arge_txdesc *txd; 1093234353Sdim struct arge_rxdesc *rxd; 1094234353Sdim int i; 1095234353Sdim 1096234353Sdim /* Tx ring. */ 1097234353Sdim if (sc->arge_cdata.arge_tx_ring_tag) { 1098234353Sdim if (sc->arge_cdata.arge_tx_ring_map) 1099234353Sdim bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag, 1100234353Sdim sc->arge_cdata.arge_tx_ring_map); 1101234353Sdim if (sc->arge_cdata.arge_tx_ring_map && 1102234353Sdim sc->arge_rdata.arge_tx_ring) 1103234353Sdim bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag, 1104234353Sdim sc->arge_rdata.arge_tx_ring, 1105234353Sdim sc->arge_cdata.arge_tx_ring_map); 1106234353Sdim sc->arge_rdata.arge_tx_ring = NULL; 1107234982Sdim sc->arge_cdata.arge_tx_ring_map = NULL; 1108234353Sdim bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag); 1109234353Sdim sc->arge_cdata.arge_tx_ring_tag = NULL; 1110234353Sdim } 1111234353Sdim /* Rx ring. */ 1112234353Sdim if (sc->arge_cdata.arge_rx_ring_tag) { 1113234353Sdim if (sc->arge_cdata.arge_rx_ring_map) 1114234353Sdim bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag, 1115234353Sdim sc->arge_cdata.arge_rx_ring_map); 1116234353Sdim if (sc->arge_cdata.arge_rx_ring_map && 1117234353Sdim sc->arge_rdata.arge_rx_ring) 1118234353Sdim bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag, 1119234353Sdim sc->arge_rdata.arge_rx_ring, 1120234353Sdim sc->arge_cdata.arge_rx_ring_map); 1121234353Sdim sc->arge_rdata.arge_rx_ring = NULL; 1122234353Sdim sc->arge_cdata.arge_rx_ring_map = NULL; 1123234353Sdim bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag); 1124234353Sdim sc->arge_cdata.arge_rx_ring_tag = NULL; 1125234353Sdim } 1126234353Sdim /* Tx buffers. */ 1127234353Sdim if (sc->arge_cdata.arge_tx_tag) { 1128234353Sdim for (i = 0; i < ARGE_TX_RING_COUNT; i++) { 1129234353Sdim txd = &sc->arge_cdata.arge_txdesc[i]; 1130234353Sdim if (txd->tx_dmamap) { 1131234353Sdim bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag, 1132234353Sdim txd->tx_dmamap); 1133234353Sdim txd->tx_dmamap = NULL; 1134234353Sdim } 1135234353Sdim } 1136234353Sdim bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag); 1137234353Sdim sc->arge_cdata.arge_tx_tag = NULL; 1138234353Sdim } 1139234353Sdim /* Rx buffers. */ 1140234353Sdim if (sc->arge_cdata.arge_rx_tag) { 1141234353Sdim for (i = 0; i < ARGE_RX_RING_COUNT; i++) { 1142234353Sdim rxd = &sc->arge_cdata.arge_rxdesc[i]; 1143234353Sdim if (rxd->rx_dmamap) { 1144234353Sdim bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, 1145234353Sdim rxd->rx_dmamap); 1146234353Sdim rxd->rx_dmamap = NULL; 1147234353Sdim } 1148234353Sdim } 1149234353Sdim if (sc->arge_cdata.arge_rx_sparemap) { 1150234353Sdim bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag, 1151234353Sdim sc->arge_cdata.arge_rx_sparemap); 1152234353Sdim sc->arge_cdata.arge_rx_sparemap = 0; 1153234353Sdim } 1154234353Sdim bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag); 1155234353Sdim sc->arge_cdata.arge_rx_tag = NULL; 1156234353Sdim } 1157234353Sdim 1158234353Sdim if (sc->arge_cdata.arge_parent_tag) { 1159234353Sdim bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag); 1160234353Sdim sc->arge_cdata.arge_parent_tag = NULL; 1161234353Sdim } 1162234353Sdim} 1163234353Sdim 1164234353Sdim/* 1165234353Sdim * Initialize the transmit descriptors. 1166234353Sdim */ 1167234353Sdimstatic int 1168234353Sdimarge_tx_ring_init(struct arge_softc *sc) 1169234353Sdim{ 1170234353Sdim struct arge_ring_data *rd; 1171234353Sdim struct arge_txdesc *txd; 1172234353Sdim bus_addr_t addr; 1173234353Sdim int i; 1174234353Sdim 1175234353Sdim sc->arge_cdata.arge_tx_prod = 0; 1176234353Sdim sc->arge_cdata.arge_tx_cons = 0; 1177234353Sdim sc->arge_cdata.arge_tx_cnt = 0; 1178234353Sdim sc->arge_cdata.arge_tx_pkts = 0; 1179234353Sdim 1180234353Sdim rd = &sc->arge_rdata; 1181234353Sdim bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring)); 1182234353Sdim for (i = 0; i < ARGE_TX_RING_COUNT; i++) { 1183234353Sdim if (i == ARGE_TX_RING_COUNT - 1) 1184234353Sdim addr = ARGE_TX_RING_ADDR(sc, 0); 1185234353Sdim else 1186234353Sdim addr = ARGE_TX_RING_ADDR(sc, i + 1); 1187234353Sdim rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; 1188234353Sdim rd->arge_tx_ring[i].next_desc = addr; 1189234353Sdim txd = &sc->arge_cdata.arge_txdesc[i]; 1190234353Sdim txd->tx_m = NULL; 1191234353Sdim } 1192234353Sdim 1193234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, 1194234353Sdim sc->arge_cdata.arge_tx_ring_map, 1195234353Sdim BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1196234353Sdim 1197234353Sdim return (0); 1198234353Sdim} 1199234353Sdim 1200234353Sdim/* 1201234353Sdim * Initialize the RX descriptors and allocate mbufs for them. Note that 1202234353Sdim * we arrange the descriptors in a closed ring, so that the last descriptor 1203234353Sdim * points back to the first. 1204234353Sdim */ 1205234353Sdimstatic int 1206234353Sdimarge_rx_ring_init(struct arge_softc *sc) 1207234353Sdim{ 1208234353Sdim struct arge_ring_data *rd; 1209234353Sdim struct arge_rxdesc *rxd; 1210234353Sdim bus_addr_t addr; 1211234353Sdim int i; 1212234353Sdim 1213234353Sdim sc->arge_cdata.arge_rx_cons = 0; 1214234353Sdim 1215234353Sdim rd = &sc->arge_rdata; 1216234353Sdim bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring)); 1217234353Sdim for (i = 0; i < ARGE_RX_RING_COUNT; i++) { 1218234353Sdim rxd = &sc->arge_cdata.arge_rxdesc[i]; 1219234353Sdim rxd->rx_m = NULL; 1220234353Sdim rxd->desc = &rd->arge_rx_ring[i]; 1221234353Sdim if (i == ARGE_RX_RING_COUNT - 1) 1222234353Sdim addr = ARGE_RX_RING_ADDR(sc, 0); 1223234353Sdim else 1224234353Sdim addr = ARGE_RX_RING_ADDR(sc, i + 1); 1225234353Sdim rd->arge_rx_ring[i].packet_ctrl = ARGE_DESC_EMPTY; 1226234353Sdim rd->arge_rx_ring[i].next_desc = addr; 1227234353Sdim if (arge_newbuf(sc, i) != 0) 1228234353Sdim return (ENOBUFS); 1229234353Sdim } 1230234353Sdim 1231234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, 1232234353Sdim sc->arge_cdata.arge_rx_ring_map, 1233234353Sdim BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1234234353Sdim 1235234353Sdim return (0); 1236234353Sdim} 1237234353Sdim 1238234353Sdim/* 1239234353Sdim * Initialize an RX descriptor and attach an MBUF cluster. 1240234353Sdim */ 1241234353Sdimstatic int 1242234353Sdimarge_newbuf(struct arge_softc *sc, int idx) 1243234353Sdim{ 1244234353Sdim struct arge_desc *desc; 1245234353Sdim struct arge_rxdesc *rxd; 1246234353Sdim struct mbuf *m; 1247234353Sdim bus_dma_segment_t segs[1]; 1248234353Sdim bus_dmamap_t map; 1249234353Sdim int nsegs; 1250234353Sdim 1251234353Sdim m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 1252234353Sdim if (m == NULL) 1253234353Sdim return (ENOBUFS); 1254234353Sdim m->m_len = m->m_pkthdr.len = MCLBYTES; 1255234353Sdim m_adj(m, sizeof(uint64_t)); 1256234353Sdim 1257234353Sdim if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag, 1258234353Sdim sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) { 1259234353Sdim m_freem(m); 1260234353Sdim return (ENOBUFS); 1261234353Sdim } 1262234353Sdim KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 1263234353Sdim 1264234353Sdim rxd = &sc->arge_cdata.arge_rxdesc[idx]; 1265234353Sdim if (rxd->rx_m != NULL) { 1266234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, 1267234353Sdim BUS_DMASYNC_POSTREAD); 1268234353Sdim bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap); 1269234353Sdim } 1270234353Sdim map = rxd->rx_dmamap; 1271234353Sdim rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap; 1272234353Sdim sc->arge_cdata.arge_rx_sparemap = map; 1273234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, 1274234353Sdim BUS_DMASYNC_PREREAD); 1275234353Sdim rxd->rx_m = m; 1276234353Sdim desc = rxd->desc; 1277234353Sdim desc->packet_addr = segs[0].ds_addr; 1278234353Sdim desc->packet_ctrl = (desc->packet_ctrl & ~ARGE_DESC_SIZE_MASK) 1279234353Sdim | ARGE_DMASIZE(segs[0].ds_len); 1280234353Sdim 1281234353Sdim return (0); 1282234353Sdim} 1283234353Sdim 1284234353Sdimstatic __inline void 1285234353Sdimarge_fixup_rx(struct mbuf *m) 1286234353Sdim{ 1287234353Sdim int i; 1288234353Sdim uint16_t *src, *dst; 1289234353Sdim 1290234353Sdim src = mtod(m, uint16_t *); 1291234353Sdim dst = src - 1; 1292234353Sdim 1293234353Sdim for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 1294234353Sdim *dst++ = *src++; 1295234353Sdim 1296234353Sdim m->m_data -= ETHER_ALIGN; 1297234353Sdim} 1298234353Sdim 1299234353Sdim 1300234353Sdimstatic void 1301234353Sdimarge_tx_locked(struct arge_softc *sc) 1302234353Sdim{ 1303234353Sdim struct arge_txdesc *txd; 1304234353Sdim struct arge_desc *cur_tx; 1305234353Sdim struct ifnet *ifp; 1306234353Sdim uint32_t ctrl; 1307234353Sdim int cons, prod; 1308234353Sdim 1309234353Sdim ARGE_LOCK_ASSERT(sc); 1310234353Sdim 1311234353Sdim cons = sc->arge_cdata.arge_tx_cons; 1312234353Sdim prod = sc->arge_cdata.arge_tx_prod; 1313234353Sdim if (cons == prod) 1314234353Sdim return; 1315234353Sdim 1316234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, 1317234353Sdim sc->arge_cdata.arge_tx_ring_map, 1318234353Sdim BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1319234353Sdim 1320234353Sdim ifp = sc->arge_ifp; 1321234353Sdim /* 1322234353Sdim * Go through our tx list and free mbufs for those 1323234353Sdim * frames that have been transmitted. 1324234353Sdim */ 1325234353Sdim for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) { 1326234353Sdim cur_tx = &sc->arge_rdata.arge_tx_ring[cons]; 1327234353Sdim ctrl = cur_tx->packet_ctrl; 1328234353Sdim /* Check if descriptor has "finished" flag */ 1329234353Sdim if ((ctrl & ARGE_DESC_EMPTY) == 0) 1330234353Sdim break; 1331234353Sdim 1332234353Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT); 1333234353Sdim 1334234353Sdim sc->arge_cdata.arge_tx_cnt--; 1335234353Sdim ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1336234353Sdim 1337234353Sdim txd = &sc->arge_cdata.arge_txdesc[cons]; 1338234353Sdim 1339234353Sdim cur_tx->packet_ctrl = ARGE_DESC_EMPTY; 1340234353Sdim 1341234353Sdim ifp->if_opackets++; 1342234353Sdim 1343234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap, 1344234353Sdim BUS_DMASYNC_POSTWRITE); 1345234353Sdim bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap); 1346234353Sdim 1347234353Sdim /* Free only if it's first descriptor in list */ 1348234353Sdim if (txd->tx_m) 1349234353Sdim m_freem(txd->tx_m); 1350234353Sdim txd->tx_m = NULL; 1351234353Sdim 1352234353Sdim /* reset descriptor */ 1353234353Sdim cur_tx->packet_ctrl = ARGE_DESC_EMPTY; 1354234353Sdim cur_tx->packet_addr = 0; 1355234353Sdim } 1356234353Sdim 1357234353Sdim sc->arge_cdata.arge_tx_cons = cons; 1358234353Sdim 1359234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag, 1360234353Sdim sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE); 1361234353Sdim} 1362234353Sdim 1363234353Sdim 1364234353Sdimstatic void 1365234353Sdimarge_rx_locked(struct arge_softc *sc) 1366234353Sdim{ 1367234353Sdim struct arge_rxdesc *rxd; 1368234353Sdim struct ifnet *ifp = sc->arge_ifp; 1369234353Sdim int cons, prog, packet_len; 1370234353Sdim struct arge_desc *cur_rx; 1371234353Sdim struct mbuf *m; 1372234353Sdim 1373234353Sdim ARGE_LOCK_ASSERT(sc); 1374234353Sdim 1375234353Sdim cons = sc->arge_cdata.arge_rx_cons; 1376234353Sdim 1377234353Sdim bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, 1378212904Sdim sc->arge_cdata.arge_rx_ring_map, 1379221345Sdim BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1380212904Sdim 1381193326Sed for (prog = 0; prog < ARGE_RX_RING_COUNT; 1382193326Sed ARGE_INC(cons, ARGE_RX_RING_COUNT)) { 1383193326Sed cur_rx = &sc->arge_rdata.arge_rx_ring[cons]; 1384193326Sed rxd = &sc->arge_cdata.arge_rxdesc[cons]; 1385193326Sed m = rxd->rx_m; 1386193326Sed 1387193326Sed if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0) 1388193326Sed break; 1389193326Sed 1390198092Srdivacky ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD); 1391221345Sdim 1392221345Sdim prog++; 1393193326Sed 1394201361Srdivacky packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl); 1395193326Sed bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap, 1396193326Sed BUS_DMASYNC_PREREAD); 1397193326Sed m = rxd->rx_m; 1398193326Sed 1399193326Sed arge_fixup_rx(m); 1400193326Sed m->m_pkthdr.rcvif = ifp; 1401193326Sed /* Skip 4 bytes of CRC */ 1402193326Sed m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN; 1403193326Sed ifp->if_ipackets++; 1404226633Sdim 1405193326Sed ARGE_UNLOCK(sc); 1406193326Sed (*ifp->if_input)(ifp, m); 1407193326Sed ARGE_LOCK(sc); 1408193326Sed 1409193326Sed /* Reinit descriptor */ 1410234353Sdim cur_rx->packet_ctrl = ARGE_DESC_EMPTY; 1411193326Sed cur_rx->packet_addr = 0; 1412193326Sed if (arge_newbuf(sc, cons) != 0) { 1413193326Sed device_printf(sc->arge_dev, 1414193326Sed "Failed to allocate buffer\n"); 1415193326Sed break; 1416193326Sed } 1417193326Sed 1418198092Srdivacky bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, 1419193326Sed sc->arge_cdata.arge_rx_ring_map, 1420193326Sed BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1421193326Sed 1422193326Sed } 1423210299Sed 1424210299Sed if (prog > 0) { 1425226633Sdim sc->arge_cdata.arge_rx_cons = cons; 1426226633Sdim 1427193326Sed bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag, 1428193326Sed sc->arge_cdata.arge_rx_ring_map, 1429193326Sed BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1430193326Sed } 1431193326Sed} 1432193326Sed 1433193326Sedstatic void 1434193326Sedarge_rx_intr(struct arge_softc *sc, uint32_t status) 1435193326Sed{ 1436193326Sed 1437193326Sed ARGE_LOCK(sc); 1438193326Sed /* interrupts are masked by filter */ 1439193326Sed arge_rx_locked(sc); 1440193326Sed 1441193326Sed /* unmask interrupts */ 1442193326Sed ARGE_SET_BITS(sc, 1443193326Sed AR71XX_DMA_INTR, DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); 1444193326Sed ARGE_UNLOCK(sc); 1445193326Sed} 1446234353Sdim 1447193326Sedstatic int 1448234353Sdimarge_intr_filter(void *arg) 1449234353Sdim{ 1450234353Sdim struct arge_softc *sc = arg; 1451234353Sdim uint32_t status, ints; 1452234353Sdim 1453234353Sdim status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS); 1454234353Sdim ints = ARGE_READ(sc, AR71XX_DMA_INTR); 1455234353Sdim 1456234353Sdim#if 0 1457234353Sdim dprintf("int mask(filter) = %b\n", ints, 1458234353Sdim "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" 1459234353Sdim "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); 1460234353Sdim dprintf("status(filter) = %b\n", status, 1461234353Sdim "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD" 1462234353Sdim "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); 1463234353Sdim#endif 1464234353Sdim 1465234353Sdim if (status & DMA_INTR_ALL) { 1466234353Sdim if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) 1467234353Sdim ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, 1468234353Sdim DMA_INTR_RX_OVERFLOW | DMA_INTR_RX_PKT_RCVD); 1469234353Sdim 1470234353Sdim if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) 1471234353Sdim ARGE_CLEAR_BITS(sc, AR71XX_DMA_INTR, 1472234353Sdim DMA_INTR_TX_UNDERRUN | DMA_INTR_TX_PKT_SENT); 1473234353Sdim 1474234353Sdim sc->arge_intr_status = status; 1475234353Sdim return (FILTER_SCHEDULE_THREAD); 1476234353Sdim } 1477234353Sdim 1478234353Sdim sc->arge_intr_status = 0; 1479234353Sdim return (FILTER_STRAY); 1480234353Sdim} 1481234353Sdim 1482234353Sdimstatic void 1483234353Sdimarge_intr(void *arg) 1484234353Sdim{ 1485234353Sdim struct arge_softc *sc = arg; 1486234353Sdim uint32_t status; 1487234353Sdim 1488234353Sdim status = sc->arge_intr_status; 1489234353Sdim 1490234353Sdim#if 0 1491234353Sdim dprintf("int status(intr) = %b\n", status, 1492234353Sdim "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD" 1493234353Sdim "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT"); 1494234353Sdim#endif 1495234353Sdim 1496234353Sdim /* 1497234353Sdim * Is it our interrupt at all? 1498234353Sdim */ 1499234353Sdim if (status == 0) 1500234353Sdim return; 1501234353Sdim 1502234353Sdim if (status & DMA_INTR_RX_BUS_ERROR) { 1503234353Sdim ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR); 1504234353Sdim device_printf(sc->arge_dev, "RX bus error"); 1505234353Sdim return; 1506234353Sdim } 1507234353Sdim 1508234353Sdim if (status & DMA_INTR_TX_BUS_ERROR) { 1509234353Sdim ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR); 1510234353Sdim device_printf(sc->arge_dev, "TX bus error"); 1511234353Sdim return; 1512234353Sdim } 1513234353Sdim 1514204793Srdivacky if (status & (DMA_INTR_RX_PKT_RCVD | DMA_INTR_RX_OVERFLOW)) 1515204793Srdivacky arge_rx_intr(sc, status); 1516204793Srdivacky 1517204793Srdivacky if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN)) 1518234353Sdim arge_tx_intr(sc, status); 1519234353Sdim} 1520204793Srdivacky 1521204793Srdivackystatic void 1522204793Srdivackyarge_tx_intr(struct arge_softc *sc, uint32_t status) 1523204793Srdivacky{ 1524204793Srdivacky ARGE_LOCK(sc); 1525204793Srdivacky 1526204793Srdivacky /* Interrupts are masked by filter */ 1527204793Srdivacky arge_tx_locked(sc); 1528204793Srdivacky 1529204793Srdivacky ARGE_UNLOCK(sc); 1530204793Srdivacky} 1531204793Srdivacky 1532204793Srdivackystatic void 1533223017Sdimarge_tick(void *xsc) 1534223017Sdim{ 1535204793Srdivacky struct arge_softc *sc = xsc; 1536204793Srdivacky struct mii_data *mii; 1537204793Srdivacky 1538204793Srdivacky ARGE_LOCK_ASSERT(sc); 1539204793Srdivacky 1540204793Srdivacky mii = device_get_softc(sc->arge_miibus); 1541204793Srdivacky mii_tick(mii); 1542204793Srdivacky callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc); 1543204793Srdivacky} 1544204793Srdivacky 1545204793Srdivacky/* 1546204793Srdivacky * Create a copy of a single mbuf. It can have either internal or 1547204793Srdivacky * external data, it may have a packet header. External data is really 1548204793Srdivacky * copied, so the new buffer is writeable. 1549223017Sdim */ 1550221345Sdimstatic struct mbuf * 1551221345Sdimcopy_mbuf(struct mbuf *m) 1552204793Srdivacky{ 1553204793Srdivacky struct mbuf *new; 1554204793Srdivacky 1555204793Srdivacky MGET(new, M_DONTWAIT, MT_DATA); 1556204793Srdivacky if (new == NULL) 1557204793Srdivacky return (NULL); 1558204793Srdivacky 1559204793Srdivacky if (m->m_flags & M_PKTHDR) { 1560204793Srdivacky M_MOVE_PKTHDR(new, m); 1561204793Srdivacky if (m->m_len > MHLEN) 1562204793Srdivacky MCLGET(new, M_WAIT); 1563226633Sdim } else { 1564204793Srdivacky if (m->m_len > MLEN) 1565204793Srdivacky MCLGET(new, M_WAIT); 1566204793Srdivacky } 1567204793Srdivacky 1568204793Srdivacky bcopy(m->m_data, new->m_data, m->m_len); 1569195341Sed new->m_len = m->m_len; 1570195341Sed new->m_flags &= ~M_RDONLY; 1571234353Sdim 1572234353Sdim return (new); 1573201361Srdivacky} 1574195341Sed 1575195341Sed 1576195341Sed 1577221345Sdimstatic int 1578221345Sdimarge_fix_chain(struct mbuf **mp) 1579195341Sed{ 1580201361Srdivacky struct mbuf *m = *mp, *prev = NULL, *next, *new; 1581195341Sed u_int mlen = 0, fill = 0; 1582195341Sed int first, off; 1583195341Sed u_char *d, *cp; 1584195341Sed 1585218893Sdim do { 1586218893Sdim next = m->m_next; 1587218893Sdim 1588218893Sdim if ((uintptr_t)mtod(m, void *) % 4 != 0 || 1589195341Sed (m->m_len % 4 != 0 && next)) { 1590195341Sed /* 1591195341Sed * Needs fixing 1592218893Sdim */ 1593218893Sdim first = (m == *mp); 1594218893Sdim 1595218893Sdim d = mtod(m, u_char *); 1596218893Sdim if ((off = (uintptr_t)(void *)d % 4) != 0) { 1597218893Sdim if (M_WRITABLE(m)) { 1598218893Sdim bcopy(d, d - off, m->m_len); 1599195341Sed m->m_data = (caddr_t)(d - off); 1600195341Sed } else { 1601195341Sed if ((new = copy_mbuf(m)) == NULL) { 1602221345Sdim goto fail; 1603195341Sed } 1604195341Sed if (prev) 1605195341Sed prev->m_next = new; 1606195341Sed new->m_next = next; 1607195341Sed m_free(m); 1608195341Sed m = new; 1609193326Sed } 1610193326Sed } 1611234353Sdim 1612234353Sdim if ((off = m->m_len % 4) != 0) { 1613212904Sdim if (!M_WRITABLE(m)) { 1614234353Sdim if ((new = copy_mbuf(m)) == NULL) { 1615234353Sdim goto fail; 1616234353Sdim } 1617234353Sdim if (prev) 1618234982Sdim prev->m_next = new; 1619234982Sdim new->m_next = next; 1620234353Sdim m_free(m); 1621234982Sdim m = new; 1622193326Sed } 1623193326Sed d = mtod(m, u_char *) + m->m_len; 1624221345Sdim off = 4 - off; 1625221345Sdim while (off) { 1626193326Sed if (next == NULL) { 1627201361Srdivacky *d++ = 0; 1628193326Sed fill++; 1629193326Sed } else if (next->m_len == 0) { 1630193326Sed next = m_free(next); 1631193326Sed continue; 1632218893Sdim } else { 1633218893Sdim cp = mtod(next, u_char *); 1634218893Sdim *d++ = *cp++; 1635218893Sdim next->m_len--; 1636193326Sed next->m_data = (caddr_t)cp; 1637193326Sed } 1638193326Sed off--; 1639193326Sed m->m_len++; 1640218893Sdim } 1641218893Sdim } 1642218893Sdim 1643218893Sdim if (first) 1644218893Sdim *mp = m; 1645193326Sed } 1646193326Sed 1647193326Sed mlen += m->m_len; 1648221345Sdim prev = m; 1649193326Sed } while ((m = next) != NULL); 1650193326Sed 1651193326Sed return (mlen - fill); 1652193326Sed 1653193326Sed fail: 1654193326Sed m_freem(*mp); 1655218893Sdim *mp = NULL; 1656218893Sdim return (0); 1657234353Sdim} 1658234353Sdim