1*d3810ff9SJared McNeill /*- 2*d3810ff9SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3*d3810ff9SJared McNeill * All rights reserved. 4*d3810ff9SJared McNeill * 5*d3810ff9SJared McNeill * Redistribution and use in source and binary forms, with or without 6*d3810ff9SJared McNeill * modification, are permitted provided that the following conditions 7*d3810ff9SJared McNeill * are met: 8*d3810ff9SJared McNeill * 1. Redistributions of source code must retain the above copyright 9*d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer. 10*d3810ff9SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright 11*d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer in the 12*d3810ff9SJared McNeill * documentation and/or other materials provided with the distribution. 13*d3810ff9SJared McNeill * 14*d3810ff9SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15*d3810ff9SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16*d3810ff9SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17*d3810ff9SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18*d3810ff9SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19*d3810ff9SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20*d3810ff9SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21*d3810ff9SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22*d3810ff9SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*d3810ff9SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*d3810ff9SJared McNeill * SUCH DAMAGE. 25*d3810ff9SJared McNeill * 26*d3810ff9SJared McNeill * $FreeBSD$ 27*d3810ff9SJared McNeill */ 28*d3810ff9SJared McNeill 29*d3810ff9SJared McNeill /* 30*d3810ff9SJared McNeill * Allwinner Gigabit Ethernet MAC (EMAC) controller 31*d3810ff9SJared McNeill */ 32*d3810ff9SJared McNeill 33*d3810ff9SJared McNeill #include <sys/cdefs.h> 34*d3810ff9SJared McNeill __FBSDID("$FreeBSD$"); 35*d3810ff9SJared McNeill 36*d3810ff9SJared McNeill #include <sys/param.h> 37*d3810ff9SJared McNeill #include <sys/systm.h> 38*d3810ff9SJared McNeill #include <sys/bus.h> 39*d3810ff9SJared McNeill #include <sys/rman.h> 40*d3810ff9SJared McNeill #include <sys/kernel.h> 41*d3810ff9SJared McNeill #include <sys/endian.h> 42*d3810ff9SJared McNeill #include <sys/mbuf.h> 43*d3810ff9SJared McNeill #include <sys/socket.h> 44*d3810ff9SJared McNeill #include <sys/sockio.h> 45*d3810ff9SJared McNeill #include <sys/module.h> 46*d3810ff9SJared McNeill #include <sys/taskqueue.h> 47*d3810ff9SJared McNeill 48*d3810ff9SJared McNeill #include <net/bpf.h> 49*d3810ff9SJared McNeill #include <net/if.h> 50*d3810ff9SJared McNeill #include <net/ethernet.h> 51*d3810ff9SJared McNeill #include <net/if_dl.h> 52*d3810ff9SJared McNeill #include <net/if_media.h> 53*d3810ff9SJared McNeill #include <net/if_types.h> 54*d3810ff9SJared McNeill #include <net/if_var.h> 55*d3810ff9SJared McNeill 56*d3810ff9SJared McNeill #include <machine/bus.h> 57*d3810ff9SJared McNeill 58*d3810ff9SJared McNeill #include <dev/ofw/ofw_bus.h> 59*d3810ff9SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 60*d3810ff9SJared McNeill 61*d3810ff9SJared McNeill #include <arm/allwinner/if_awgreg.h> 62*d3810ff9SJared McNeill #include <dev/mii/mii.h> 63*d3810ff9SJared McNeill #include <dev/mii/miivar.h> 64*d3810ff9SJared McNeill 65*d3810ff9SJared McNeill #include <dev/extres/clk/clk.h> 66*d3810ff9SJared McNeill #include <dev/extres/hwreset/hwreset.h> 67*d3810ff9SJared McNeill #include <dev/extres/regulator/regulator.h> 68*d3810ff9SJared McNeill 69*d3810ff9SJared McNeill #include "miibus_if.h" 70*d3810ff9SJared McNeill 71*d3810ff9SJared McNeill #define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) 72*d3810ff9SJared McNeill #define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) 73*d3810ff9SJared McNeill 74*d3810ff9SJared McNeill #define AWG_LOCK(sc) mtx_lock(&(sc)->mtx) 75*d3810ff9SJared McNeill #define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx); 76*d3810ff9SJared McNeill #define AWG_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 77*d3810ff9SJared McNeill #define AWG_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 78*d3810ff9SJared McNeill 79*d3810ff9SJared McNeill #define DESC_ALIGN 4 80*d3810ff9SJared McNeill #define TX_DESC_COUNT 256 81*d3810ff9SJared McNeill #define TX_DESC_SIZE (sizeof(struct emac_desc) * TX_DESC_COUNT) 82*d3810ff9SJared McNeill #define RX_DESC_COUNT 256 83*d3810ff9SJared McNeill #define RX_DESC_SIZE (sizeof(struct emac_desc) * RX_DESC_COUNT) 84*d3810ff9SJared McNeill 85*d3810ff9SJared McNeill #define DESC_OFF(n) ((n) * sizeof(struct emac_desc)) 86*d3810ff9SJared McNeill #define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1)) 87*d3810ff9SJared McNeill #define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1)) 88*d3810ff9SJared McNeill #define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1)) 89*d3810ff9SJared McNeill 90*d3810ff9SJared McNeill #define TX_MAX_SEGS 10 91*d3810ff9SJared McNeill 92*d3810ff9SJared McNeill #define SOFT_RST_RETRY 1000 93*d3810ff9SJared McNeill #define MII_BUSY_RETRY 1000 94*d3810ff9SJared McNeill #define MDIO_FREQ 2500000 95*d3810ff9SJared McNeill 96*d3810ff9SJared McNeill #define BURST_LEN_DEFAULT 8 97*d3810ff9SJared McNeill #define RX_TX_PRI_DEFAULT 0 98*d3810ff9SJared McNeill #define PAUSE_TIME_DEFAULT 0x400 99*d3810ff9SJared McNeill #define TX_INTERVAL_DEFAULT 64 100*d3810ff9SJared McNeill 101*d3810ff9SJared McNeill /* Burst length of RX and TX DMA transfers */ 102*d3810ff9SJared McNeill static int awg_burst_len = BURST_LEN_DEFAULT; 103*d3810ff9SJared McNeill TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); 104*d3810ff9SJared McNeill 105*d3810ff9SJared McNeill /* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */ 106*d3810ff9SJared McNeill static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT; 107*d3810ff9SJared McNeill TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri); 108*d3810ff9SJared McNeill 109*d3810ff9SJared McNeill /* Pause time field in the transmitted control frame */ 110*d3810ff9SJared McNeill static int awg_pause_time = PAUSE_TIME_DEFAULT; 111*d3810ff9SJared McNeill TUNABLE_INT("hw.awg.pause_time", &awg_pause_time); 112*d3810ff9SJared McNeill 113*d3810ff9SJared McNeill /* Request a TX interrupt every <n> descriptors */ 114*d3810ff9SJared McNeill static int awg_tx_interval = TX_INTERVAL_DEFAULT; 115*d3810ff9SJared McNeill TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval); 116*d3810ff9SJared McNeill 117*d3810ff9SJared McNeill static struct ofw_compat_data compat_data[] = { 118*d3810ff9SJared McNeill { "allwinner,sun8i-a83t-emac", 1 }, 119*d3810ff9SJared McNeill { NULL, 0 } 120*d3810ff9SJared McNeill }; 121*d3810ff9SJared McNeill 122*d3810ff9SJared McNeill struct awg_bufmap { 123*d3810ff9SJared McNeill bus_dmamap_t map; 124*d3810ff9SJared McNeill struct mbuf *mbuf; 125*d3810ff9SJared McNeill }; 126*d3810ff9SJared McNeill 127*d3810ff9SJared McNeill struct awg_txring { 128*d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 129*d3810ff9SJared McNeill bus_dmamap_t desc_map; 130*d3810ff9SJared McNeill struct emac_desc *desc_ring; 131*d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 132*d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 133*d3810ff9SJared McNeill struct awg_bufmap buf_map[TX_DESC_COUNT]; 134*d3810ff9SJared McNeill u_int cur, next, queued; 135*d3810ff9SJared McNeill }; 136*d3810ff9SJared McNeill 137*d3810ff9SJared McNeill struct awg_rxring { 138*d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 139*d3810ff9SJared McNeill bus_dmamap_t desc_map; 140*d3810ff9SJared McNeill struct emac_desc *desc_ring; 141*d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 142*d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 143*d3810ff9SJared McNeill struct awg_bufmap buf_map[RX_DESC_COUNT]; 144*d3810ff9SJared McNeill u_int cur; 145*d3810ff9SJared McNeill }; 146*d3810ff9SJared McNeill 147*d3810ff9SJared McNeill struct awg_softc { 148*d3810ff9SJared McNeill struct resource *res[2]; 149*d3810ff9SJared McNeill struct mtx mtx; 150*d3810ff9SJared McNeill if_t ifp; 151*d3810ff9SJared McNeill device_t miibus; 152*d3810ff9SJared McNeill struct callout stat_ch; 153*d3810ff9SJared McNeill struct task link_task; 154*d3810ff9SJared McNeill void *ih; 155*d3810ff9SJared McNeill u_int mdc_div_ratio_m; 156*d3810ff9SJared McNeill int link; 157*d3810ff9SJared McNeill int if_flags; 158*d3810ff9SJared McNeill 159*d3810ff9SJared McNeill struct awg_txring tx; 160*d3810ff9SJared McNeill struct awg_rxring rx; 161*d3810ff9SJared McNeill }; 162*d3810ff9SJared McNeill 163*d3810ff9SJared McNeill static struct resource_spec awg_spec[] = { 164*d3810ff9SJared McNeill { SYS_RES_MEMORY, 0, RF_ACTIVE }, 165*d3810ff9SJared McNeill { SYS_RES_IRQ, 0, RF_ACTIVE }, 166*d3810ff9SJared McNeill { -1, 0 } 167*d3810ff9SJared McNeill }; 168*d3810ff9SJared McNeill 169*d3810ff9SJared McNeill static int 170*d3810ff9SJared McNeill awg_miibus_readreg(device_t dev, int phy, int reg) 171*d3810ff9SJared McNeill { 172*d3810ff9SJared McNeill struct awg_softc *sc; 173*d3810ff9SJared McNeill int retry, val; 174*d3810ff9SJared McNeill 175*d3810ff9SJared McNeill sc = device_get_softc(dev); 176*d3810ff9SJared McNeill val = 0; 177*d3810ff9SJared McNeill 178*d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 179*d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 180*d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 181*d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 182*d3810ff9SJared McNeill MII_BUSY); 183*d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 184*d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) { 185*d3810ff9SJared McNeill val = RD4(sc, EMAC_MII_DATA); 186*d3810ff9SJared McNeill break; 187*d3810ff9SJared McNeill } 188*d3810ff9SJared McNeill DELAY(10); 189*d3810ff9SJared McNeill } 190*d3810ff9SJared McNeill 191*d3810ff9SJared McNeill if (retry == 0) 192*d3810ff9SJared McNeill device_printf(dev, "phy read timeout, phy=%d reg=%d\n", 193*d3810ff9SJared McNeill phy, reg); 194*d3810ff9SJared McNeill 195*d3810ff9SJared McNeill return (val); 196*d3810ff9SJared McNeill } 197*d3810ff9SJared McNeill 198*d3810ff9SJared McNeill static int 199*d3810ff9SJared McNeill awg_miibus_writereg(device_t dev, int phy, int reg, int val) 200*d3810ff9SJared McNeill { 201*d3810ff9SJared McNeill struct awg_softc *sc; 202*d3810ff9SJared McNeill int retry; 203*d3810ff9SJared McNeill 204*d3810ff9SJared McNeill sc = device_get_softc(dev); 205*d3810ff9SJared McNeill 206*d3810ff9SJared McNeill WR4(sc, EMAC_MII_DATA, val); 207*d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 208*d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 209*d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 210*d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 211*d3810ff9SJared McNeill MII_WR | MII_BUSY); 212*d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 213*d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) 214*d3810ff9SJared McNeill break; 215*d3810ff9SJared McNeill DELAY(10); 216*d3810ff9SJared McNeill } 217*d3810ff9SJared McNeill 218*d3810ff9SJared McNeill if (retry == 0) 219*d3810ff9SJared McNeill device_printf(dev, "phy write timeout, phy=%d reg=%d\n", 220*d3810ff9SJared McNeill phy, reg); 221*d3810ff9SJared McNeill 222*d3810ff9SJared McNeill return (0); 223*d3810ff9SJared McNeill } 224*d3810ff9SJared McNeill 225*d3810ff9SJared McNeill static void 226*d3810ff9SJared McNeill awg_update_link_locked(struct awg_softc *sc) 227*d3810ff9SJared McNeill { 228*d3810ff9SJared McNeill struct mii_data *mii; 229*d3810ff9SJared McNeill uint32_t val; 230*d3810ff9SJared McNeill 231*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 232*d3810ff9SJared McNeill 233*d3810ff9SJared McNeill if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0) 234*d3810ff9SJared McNeill return; 235*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 236*d3810ff9SJared McNeill 237*d3810ff9SJared McNeill if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 238*d3810ff9SJared McNeill (IFM_ACTIVE | IFM_AVALID)) { 239*d3810ff9SJared McNeill switch (IFM_SUBTYPE(mii->mii_media_active)) { 240*d3810ff9SJared McNeill case IFM_1000_T: 241*d3810ff9SJared McNeill case IFM_1000_SX: 242*d3810ff9SJared McNeill case IFM_100_TX: 243*d3810ff9SJared McNeill case IFM_10_T: 244*d3810ff9SJared McNeill sc->link = 1; 245*d3810ff9SJared McNeill break; 246*d3810ff9SJared McNeill default: 247*d3810ff9SJared McNeill sc->link = 0; 248*d3810ff9SJared McNeill break; 249*d3810ff9SJared McNeill } 250*d3810ff9SJared McNeill } else 251*d3810ff9SJared McNeill sc->link = 0; 252*d3810ff9SJared McNeill 253*d3810ff9SJared McNeill if (sc->link == 0) 254*d3810ff9SJared McNeill return; 255*d3810ff9SJared McNeill 256*d3810ff9SJared McNeill val = RD4(sc, EMAC_BASIC_CTL_0); 257*d3810ff9SJared McNeill val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX); 258*d3810ff9SJared McNeill 259*d3810ff9SJared McNeill if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || 260*d3810ff9SJared McNeill IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) 261*d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT; 262*d3810ff9SJared McNeill else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 263*d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT; 264*d3810ff9SJared McNeill else 265*d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT; 266*d3810ff9SJared McNeill 267*d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 268*d3810ff9SJared McNeill val |= BASIC_CTL_DUPLEX; 269*d3810ff9SJared McNeill 270*d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_0, val); 271*d3810ff9SJared McNeill 272*d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 273*d3810ff9SJared McNeill val &= ~RX_FLOW_CTL_EN; 274*d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 275*d3810ff9SJared McNeill val |= RX_FLOW_CTL_EN; 276*d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val); 277*d3810ff9SJared McNeill 278*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_FLOW_CTL); 279*d3810ff9SJared McNeill val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN); 280*d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 281*d3810ff9SJared McNeill val |= TX_FLOW_CTL_EN; 282*d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 283*d3810ff9SJared McNeill val |= awg_pause_time << PAUSE_TIME_SHIFT; 284*d3810ff9SJared McNeill WR4(sc, EMAC_TX_FLOW_CTL, val); 285*d3810ff9SJared McNeill } 286*d3810ff9SJared McNeill 287*d3810ff9SJared McNeill static void 288*d3810ff9SJared McNeill awg_link_task(void *arg, int pending) 289*d3810ff9SJared McNeill { 290*d3810ff9SJared McNeill struct awg_softc *sc; 291*d3810ff9SJared McNeill 292*d3810ff9SJared McNeill sc = arg; 293*d3810ff9SJared McNeill 294*d3810ff9SJared McNeill AWG_LOCK(sc); 295*d3810ff9SJared McNeill awg_update_link_locked(sc); 296*d3810ff9SJared McNeill AWG_UNLOCK(sc); 297*d3810ff9SJared McNeill } 298*d3810ff9SJared McNeill 299*d3810ff9SJared McNeill static void 300*d3810ff9SJared McNeill awg_miibus_statchg(device_t dev) 301*d3810ff9SJared McNeill { 302*d3810ff9SJared McNeill struct awg_softc *sc; 303*d3810ff9SJared McNeill 304*d3810ff9SJared McNeill sc = device_get_softc(dev); 305*d3810ff9SJared McNeill 306*d3810ff9SJared McNeill taskqueue_enqueue(taskqueue_swi, &sc->link_task); 307*d3810ff9SJared McNeill } 308*d3810ff9SJared McNeill 309*d3810ff9SJared McNeill static void 310*d3810ff9SJared McNeill awg_media_status(if_t ifp, struct ifmediareq *ifmr) 311*d3810ff9SJared McNeill { 312*d3810ff9SJared McNeill struct awg_softc *sc; 313*d3810ff9SJared McNeill struct mii_data *mii; 314*d3810ff9SJared McNeill 315*d3810ff9SJared McNeill sc = if_getsoftc(ifp); 316*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 317*d3810ff9SJared McNeill 318*d3810ff9SJared McNeill AWG_LOCK(sc); 319*d3810ff9SJared McNeill mii_pollstat(mii); 320*d3810ff9SJared McNeill ifmr->ifm_active = mii->mii_media_active; 321*d3810ff9SJared McNeill ifmr->ifm_status = mii->mii_media_status; 322*d3810ff9SJared McNeill AWG_UNLOCK(sc); 323*d3810ff9SJared McNeill } 324*d3810ff9SJared McNeill 325*d3810ff9SJared McNeill static int 326*d3810ff9SJared McNeill awg_media_change(if_t ifp) 327*d3810ff9SJared McNeill { 328*d3810ff9SJared McNeill struct awg_softc *sc; 329*d3810ff9SJared McNeill struct mii_data *mii; 330*d3810ff9SJared McNeill int error; 331*d3810ff9SJared McNeill 332*d3810ff9SJared McNeill sc = if_getsoftc(ifp); 333*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 334*d3810ff9SJared McNeill 335*d3810ff9SJared McNeill AWG_LOCK(sc); 336*d3810ff9SJared McNeill error = mii_mediachg(mii); 337*d3810ff9SJared McNeill AWG_UNLOCK(sc); 338*d3810ff9SJared McNeill 339*d3810ff9SJared McNeill return (error); 340*d3810ff9SJared McNeill } 341*d3810ff9SJared McNeill 342*d3810ff9SJared McNeill static void 343*d3810ff9SJared McNeill awg_setup_txdesc(struct awg_softc *sc, int index, int flags, bus_addr_t paddr, 344*d3810ff9SJared McNeill u_int len) 345*d3810ff9SJared McNeill { 346*d3810ff9SJared McNeill uint32_t status, size; 347*d3810ff9SJared McNeill 348*d3810ff9SJared McNeill if (paddr == 0 || len == 0) { 349*d3810ff9SJared McNeill status = 0; 350*d3810ff9SJared McNeill size = 0; 351*d3810ff9SJared McNeill --sc->tx.queued; 352*d3810ff9SJared McNeill } else { 353*d3810ff9SJared McNeill status = TX_DESC_CTL; 354*d3810ff9SJared McNeill size = flags | len; 355*d3810ff9SJared McNeill if ((index & (awg_tx_interval - 1)) == 0) 356*d3810ff9SJared McNeill size |= htole32(TX_INT_CTL); 357*d3810ff9SJared McNeill ++sc->tx.queued; 358*d3810ff9SJared McNeill } 359*d3810ff9SJared McNeill 360*d3810ff9SJared McNeill sc->tx.desc_ring[index].addr = htole32((uint32_t)paddr); 361*d3810ff9SJared McNeill sc->tx.desc_ring[index].size = htole32(size); 362*d3810ff9SJared McNeill sc->tx.desc_ring[index].status = htole32(status); 363*d3810ff9SJared McNeill } 364*d3810ff9SJared McNeill 365*d3810ff9SJared McNeill static int 366*d3810ff9SJared McNeill awg_setup_txbuf(struct awg_softc *sc, int index, struct mbuf **mp) 367*d3810ff9SJared McNeill { 368*d3810ff9SJared McNeill bus_dma_segment_t segs[TX_MAX_SEGS]; 369*d3810ff9SJared McNeill int error, nsegs, cur, i, flags; 370*d3810ff9SJared McNeill u_int csum_flags; 371*d3810ff9SJared McNeill struct mbuf *m; 372*d3810ff9SJared McNeill 373*d3810ff9SJared McNeill m = *mp; 374*d3810ff9SJared McNeill error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, 375*d3810ff9SJared McNeill sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); 376*d3810ff9SJared McNeill if (error == EFBIG) { 377*d3810ff9SJared McNeill m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); 378*d3810ff9SJared McNeill if (m == NULL) 379*d3810ff9SJared McNeill return (0); 380*d3810ff9SJared McNeill *mp = m; 381*d3810ff9SJared McNeill error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, 382*d3810ff9SJared McNeill sc->tx.buf_map[index].map, m, segs, &nsegs, BUS_DMA_NOWAIT); 383*d3810ff9SJared McNeill } 384*d3810ff9SJared McNeill if (error != 0) 385*d3810ff9SJared McNeill return (0); 386*d3810ff9SJared McNeill 387*d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.buf_tag, sc->tx.buf_map[index].map, 388*d3810ff9SJared McNeill BUS_DMASYNC_PREWRITE); 389*d3810ff9SJared McNeill 390*d3810ff9SJared McNeill flags = TX_FIR_DESC; 391*d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 392*d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) 393*d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_FULL; 394*d3810ff9SJared McNeill else 395*d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_IP; 396*d3810ff9SJared McNeill flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); 397*d3810ff9SJared McNeill } 398*d3810ff9SJared McNeill 399*d3810ff9SJared McNeill for (cur = index, i = 0; i < nsegs; i++) { 400*d3810ff9SJared McNeill sc->tx.buf_map[cur].mbuf = (i == 0 ? m : NULL); 401*d3810ff9SJared McNeill if (i == nsegs - 1) 402*d3810ff9SJared McNeill flags |= TX_LAST_DESC; 403*d3810ff9SJared McNeill awg_setup_txdesc(sc, cur, flags, segs[i].ds_addr, 404*d3810ff9SJared McNeill segs[i].ds_len); 405*d3810ff9SJared McNeill flags &= ~TX_FIR_DESC; 406*d3810ff9SJared McNeill cur = TX_NEXT(cur); 407*d3810ff9SJared McNeill } 408*d3810ff9SJared McNeill 409*d3810ff9SJared McNeill return (nsegs); 410*d3810ff9SJared McNeill } 411*d3810ff9SJared McNeill 412*d3810ff9SJared McNeill static void 413*d3810ff9SJared McNeill awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr) 414*d3810ff9SJared McNeill { 415*d3810ff9SJared McNeill uint32_t status, size; 416*d3810ff9SJared McNeill 417*d3810ff9SJared McNeill status = RX_DESC_CTL; 418*d3810ff9SJared McNeill size = MCLBYTES - 1; 419*d3810ff9SJared McNeill 420*d3810ff9SJared McNeill sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr); 421*d3810ff9SJared McNeill sc->rx.desc_ring[index].size = htole32(size); 422*d3810ff9SJared McNeill sc->rx.desc_ring[index].next = 423*d3810ff9SJared McNeill htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(index))); 424*d3810ff9SJared McNeill sc->rx.desc_ring[index].status = htole32(status); 425*d3810ff9SJared McNeill } 426*d3810ff9SJared McNeill 427*d3810ff9SJared McNeill static int 428*d3810ff9SJared McNeill awg_setup_rxbuf(struct awg_softc *sc, int index, struct mbuf *m) 429*d3810ff9SJared McNeill { 430*d3810ff9SJared McNeill bus_dma_segment_t seg; 431*d3810ff9SJared McNeill int error, nsegs; 432*d3810ff9SJared McNeill 433*d3810ff9SJared McNeill m_adj(m, ETHER_ALIGN); 434*d3810ff9SJared McNeill 435*d3810ff9SJared McNeill error = bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, 436*d3810ff9SJared McNeill sc->rx.buf_map[index].map, m, &seg, &nsegs, 0); 437*d3810ff9SJared McNeill if (error != 0) 438*d3810ff9SJared McNeill return (error); 439*d3810ff9SJared McNeill 440*d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 441*d3810ff9SJared McNeill BUS_DMASYNC_PREREAD); 442*d3810ff9SJared McNeill 443*d3810ff9SJared McNeill sc->rx.buf_map[index].mbuf = m; 444*d3810ff9SJared McNeill awg_setup_rxdesc(sc, index, seg.ds_addr); 445*d3810ff9SJared McNeill 446*d3810ff9SJared McNeill return (0); 447*d3810ff9SJared McNeill } 448*d3810ff9SJared McNeill 449*d3810ff9SJared McNeill static struct mbuf * 450*d3810ff9SJared McNeill awg_alloc_mbufcl(struct awg_softc *sc) 451*d3810ff9SJared McNeill { 452*d3810ff9SJared McNeill struct mbuf *m; 453*d3810ff9SJared McNeill 454*d3810ff9SJared McNeill m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 455*d3810ff9SJared McNeill if (m != NULL) 456*d3810ff9SJared McNeill m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 457*d3810ff9SJared McNeill 458*d3810ff9SJared McNeill return (m); 459*d3810ff9SJared McNeill } 460*d3810ff9SJared McNeill 461*d3810ff9SJared McNeill static void 462*d3810ff9SJared McNeill awg_start_locked(struct awg_softc *sc) 463*d3810ff9SJared McNeill { 464*d3810ff9SJared McNeill struct mbuf *m; 465*d3810ff9SJared McNeill uint32_t val; 466*d3810ff9SJared McNeill if_t ifp; 467*d3810ff9SJared McNeill int cnt, nsegs; 468*d3810ff9SJared McNeill 469*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 470*d3810ff9SJared McNeill 471*d3810ff9SJared McNeill if (!sc->link) 472*d3810ff9SJared McNeill return; 473*d3810ff9SJared McNeill 474*d3810ff9SJared McNeill ifp = sc->ifp; 475*d3810ff9SJared McNeill 476*d3810ff9SJared McNeill if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 477*d3810ff9SJared McNeill IFF_DRV_RUNNING) 478*d3810ff9SJared McNeill return; 479*d3810ff9SJared McNeill 480*d3810ff9SJared McNeill for (cnt = 0; ; cnt++) { 481*d3810ff9SJared McNeill if (sc->tx.queued >= TX_DESC_COUNT - TX_MAX_SEGS) { 482*d3810ff9SJared McNeill if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 483*d3810ff9SJared McNeill break; 484*d3810ff9SJared McNeill } 485*d3810ff9SJared McNeill 486*d3810ff9SJared McNeill m = if_dequeue(ifp); 487*d3810ff9SJared McNeill if (m == NULL) 488*d3810ff9SJared McNeill break; 489*d3810ff9SJared McNeill 490*d3810ff9SJared McNeill nsegs = awg_setup_txbuf(sc, sc->tx.cur, &m); 491*d3810ff9SJared McNeill if (nsegs == 0) { 492*d3810ff9SJared McNeill if_sendq_prepend(ifp, m); 493*d3810ff9SJared McNeill break; 494*d3810ff9SJared McNeill } 495*d3810ff9SJared McNeill if_bpfmtap(ifp, m); 496*d3810ff9SJared McNeill sc->tx.cur = TX_SKIP(sc->tx.cur, nsegs); 497*d3810ff9SJared McNeill } 498*d3810ff9SJared McNeill 499*d3810ff9SJared McNeill if (cnt != 0) { 500*d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 501*d3810ff9SJared McNeill BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 502*d3810ff9SJared McNeill 503*d3810ff9SJared McNeill /* Start and run TX DMA */ 504*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 505*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START); 506*d3810ff9SJared McNeill } 507*d3810ff9SJared McNeill } 508*d3810ff9SJared McNeill 509*d3810ff9SJared McNeill static void 510*d3810ff9SJared McNeill awg_start(if_t ifp) 511*d3810ff9SJared McNeill { 512*d3810ff9SJared McNeill struct awg_softc *sc; 513*d3810ff9SJared McNeill 514*d3810ff9SJared McNeill sc = if_getsoftc(ifp); 515*d3810ff9SJared McNeill 516*d3810ff9SJared McNeill AWG_LOCK(sc); 517*d3810ff9SJared McNeill awg_start_locked(sc); 518*d3810ff9SJared McNeill AWG_UNLOCK(sc); 519*d3810ff9SJared McNeill } 520*d3810ff9SJared McNeill 521*d3810ff9SJared McNeill static void 522*d3810ff9SJared McNeill awg_tick(void *softc) 523*d3810ff9SJared McNeill { 524*d3810ff9SJared McNeill struct awg_softc *sc; 525*d3810ff9SJared McNeill struct mii_data *mii; 526*d3810ff9SJared McNeill if_t ifp; 527*d3810ff9SJared McNeill int link; 528*d3810ff9SJared McNeill 529*d3810ff9SJared McNeill sc = softc; 530*d3810ff9SJared McNeill ifp = sc->ifp; 531*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 532*d3810ff9SJared McNeill 533*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 534*d3810ff9SJared McNeill 535*d3810ff9SJared McNeill if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 536*d3810ff9SJared McNeill return; 537*d3810ff9SJared McNeill 538*d3810ff9SJared McNeill link = sc->link; 539*d3810ff9SJared McNeill mii_tick(mii); 540*d3810ff9SJared McNeill if (sc->link && !link) 541*d3810ff9SJared McNeill awg_start_locked(sc); 542*d3810ff9SJared McNeill 543*d3810ff9SJared McNeill callout_reset(&sc->stat_ch, hz, awg_tick, sc); 544*d3810ff9SJared McNeill } 545*d3810ff9SJared McNeill 546*d3810ff9SJared McNeill /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */ 547*d3810ff9SJared McNeill static uint32_t 548*d3810ff9SJared McNeill bitrev32(uint32_t x) 549*d3810ff9SJared McNeill { 550*d3810ff9SJared McNeill x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); 551*d3810ff9SJared McNeill x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); 552*d3810ff9SJared McNeill x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); 553*d3810ff9SJared McNeill x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); 554*d3810ff9SJared McNeill 555*d3810ff9SJared McNeill return (x >> 16) | (x << 16); 556*d3810ff9SJared McNeill } 557*d3810ff9SJared McNeill 558*d3810ff9SJared McNeill static void 559*d3810ff9SJared McNeill awg_setup_rxfilter(struct awg_softc *sc) 560*d3810ff9SJared McNeill { 561*d3810ff9SJared McNeill uint32_t val, crc, hashreg, hashbit, hash[2], machi, maclo; 562*d3810ff9SJared McNeill int mc_count, mcnt, i; 563*d3810ff9SJared McNeill uint8_t *eaddr, *mta; 564*d3810ff9SJared McNeill if_t ifp; 565*d3810ff9SJared McNeill 566*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 567*d3810ff9SJared McNeill 568*d3810ff9SJared McNeill ifp = sc->ifp; 569*d3810ff9SJared McNeill val = 0; 570*d3810ff9SJared McNeill hash[0] = hash[1] = 0; 571*d3810ff9SJared McNeill 572*d3810ff9SJared McNeill mc_count = if_multiaddr_count(ifp, -1); 573*d3810ff9SJared McNeill 574*d3810ff9SJared McNeill if (if_getflags(ifp) & IFF_PROMISC) 575*d3810ff9SJared McNeill val |= DIS_ADDR_FILTER; 576*d3810ff9SJared McNeill else if (if_getflags(ifp) & IFF_ALLMULTI) { 577*d3810ff9SJared McNeill val |= RX_ALL_MULTICAST; 578*d3810ff9SJared McNeill hash[0] = hash[1] = ~0; 579*d3810ff9SJared McNeill } else if (mc_count > 0) { 580*d3810ff9SJared McNeill val |= HASH_MULTICAST; 581*d3810ff9SJared McNeill 582*d3810ff9SJared McNeill mta = malloc(sizeof(unsigned char) * ETHER_ADDR_LEN * mc_count, 583*d3810ff9SJared McNeill M_DEVBUF, M_NOWAIT); 584*d3810ff9SJared McNeill if (mta == NULL) { 585*d3810ff9SJared McNeill if_printf(ifp, 586*d3810ff9SJared McNeill "failed to allocate temporary multicast list\n"); 587*d3810ff9SJared McNeill return; 588*d3810ff9SJared McNeill } 589*d3810ff9SJared McNeill 590*d3810ff9SJared McNeill if_multiaddr_array(ifp, mta, &mcnt, mc_count); 591*d3810ff9SJared McNeill for (i = 0; i < mcnt; i++) { 592*d3810ff9SJared McNeill crc = ether_crc32_le(mta + (i * ETHER_ADDR_LEN), 593*d3810ff9SJared McNeill ETHER_ADDR_LEN) & 0x7f; 594*d3810ff9SJared McNeill crc = bitrev32(~crc) >> 26; 595*d3810ff9SJared McNeill hashreg = (crc >> 5); 596*d3810ff9SJared McNeill hashbit = (crc & 0x1f); 597*d3810ff9SJared McNeill hash[hashreg] |= (1 << hashbit); 598*d3810ff9SJared McNeill } 599*d3810ff9SJared McNeill 600*d3810ff9SJared McNeill free(mta, M_DEVBUF); 601*d3810ff9SJared McNeill } 602*d3810ff9SJared McNeill 603*d3810ff9SJared McNeill /* Write our unicast address */ 604*d3810ff9SJared McNeill eaddr = IF_LLADDR(ifp); 605*d3810ff9SJared McNeill machi = (eaddr[5] << 8) | eaddr[4]; 606*d3810ff9SJared McNeill maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | 607*d3810ff9SJared McNeill (eaddr[0] << 0); 608*d3810ff9SJared McNeill WR4(sc, EMAC_ADDR_HIGH(0), machi); 609*d3810ff9SJared McNeill WR4(sc, EMAC_ADDR_LOW(0), maclo); 610*d3810ff9SJared McNeill 611*d3810ff9SJared McNeill /* Multicast hash filters */ 612*d3810ff9SJared McNeill WR4(sc, EMAC_RX_HASH_0, hash[1]); 613*d3810ff9SJared McNeill WR4(sc, EMAC_RX_HASH_1, hash[0]); 614*d3810ff9SJared McNeill 615*d3810ff9SJared McNeill /* RX frame filter config */ 616*d3810ff9SJared McNeill WR4(sc, EMAC_RX_FRM_FLT, val); 617*d3810ff9SJared McNeill } 618*d3810ff9SJared McNeill 619*d3810ff9SJared McNeill static void 620*d3810ff9SJared McNeill awg_init_locked(struct awg_softc *sc) 621*d3810ff9SJared McNeill { 622*d3810ff9SJared McNeill struct mii_data *mii; 623*d3810ff9SJared McNeill uint32_t val; 624*d3810ff9SJared McNeill if_t ifp; 625*d3810ff9SJared McNeill 626*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 627*d3810ff9SJared McNeill ifp = sc->ifp; 628*d3810ff9SJared McNeill 629*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 630*d3810ff9SJared McNeill 631*d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 632*d3810ff9SJared McNeill return; 633*d3810ff9SJared McNeill 634*d3810ff9SJared McNeill awg_setup_rxfilter(sc); 635*d3810ff9SJared McNeill 636*d3810ff9SJared McNeill /* Configure DMA burst length and priorities */ 637*d3810ff9SJared McNeill val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT; 638*d3810ff9SJared McNeill if (awg_rx_tx_pri) 639*d3810ff9SJared McNeill val |= BASIC_CTL_RX_TX_PRI; 640*d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_1, val); 641*d3810ff9SJared McNeill 642*d3810ff9SJared McNeill /* Enable interrupts */ 643*d3810ff9SJared McNeill WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN); 644*d3810ff9SJared McNeill 645*d3810ff9SJared McNeill /* Enable transmit DMA */ 646*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 647*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD); 648*d3810ff9SJared McNeill 649*d3810ff9SJared McNeill /* Enable receive DMA */ 650*d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_1); 651*d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD); 652*d3810ff9SJared McNeill 653*d3810ff9SJared McNeill /* Enable transmitter */ 654*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_0); 655*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_0, val | TX_EN); 656*d3810ff9SJared McNeill 657*d3810ff9SJared McNeill /* Enable receiver */ 658*d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 659*d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val | RX_EN | CHECK_CRC); 660*d3810ff9SJared McNeill 661*d3810ff9SJared McNeill if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 662*d3810ff9SJared McNeill 663*d3810ff9SJared McNeill mii_mediachg(mii); 664*d3810ff9SJared McNeill callout_reset(&sc->stat_ch, hz, awg_tick, sc); 665*d3810ff9SJared McNeill } 666*d3810ff9SJared McNeill 667*d3810ff9SJared McNeill static void 668*d3810ff9SJared McNeill awg_init(void *softc) 669*d3810ff9SJared McNeill { 670*d3810ff9SJared McNeill struct awg_softc *sc; 671*d3810ff9SJared McNeill 672*d3810ff9SJared McNeill sc = softc; 673*d3810ff9SJared McNeill 674*d3810ff9SJared McNeill AWG_LOCK(sc); 675*d3810ff9SJared McNeill awg_init_locked(sc); 676*d3810ff9SJared McNeill AWG_UNLOCK(sc); 677*d3810ff9SJared McNeill } 678*d3810ff9SJared McNeill 679*d3810ff9SJared McNeill static void 680*d3810ff9SJared McNeill awg_stop(struct awg_softc *sc) 681*d3810ff9SJared McNeill { 682*d3810ff9SJared McNeill if_t ifp; 683*d3810ff9SJared McNeill uint32_t val; 684*d3810ff9SJared McNeill 685*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 686*d3810ff9SJared McNeill 687*d3810ff9SJared McNeill ifp = sc->ifp; 688*d3810ff9SJared McNeill 689*d3810ff9SJared McNeill callout_stop(&sc->stat_ch); 690*d3810ff9SJared McNeill 691*d3810ff9SJared McNeill /* Stop transmit DMA and flush data in the TX FIFO */ 692*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 693*d3810ff9SJared McNeill val &= ~TX_DMA_EN; 694*d3810ff9SJared McNeill val |= FLUSH_TX_FIFO; 695*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val); 696*d3810ff9SJared McNeill 697*d3810ff9SJared McNeill /* Disable transmitter */ 698*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_0); 699*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_0, val & ~TX_EN); 700*d3810ff9SJared McNeill 701*d3810ff9SJared McNeill /* Disable receiver */ 702*d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 703*d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val & ~RX_EN); 704*d3810ff9SJared McNeill 705*d3810ff9SJared McNeill /* Disable interrupts */ 706*d3810ff9SJared McNeill WR4(sc, EMAC_INT_EN, 0); 707*d3810ff9SJared McNeill 708*d3810ff9SJared McNeill /* Disable transmit DMA */ 709*d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 710*d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN); 711*d3810ff9SJared McNeill 712*d3810ff9SJared McNeill /* Disable receive DMA */ 713*d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_1); 714*d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN); 715*d3810ff9SJared McNeill 716*d3810ff9SJared McNeill sc->link = 0; 717*d3810ff9SJared McNeill 718*d3810ff9SJared McNeill if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 719*d3810ff9SJared McNeill } 720*d3810ff9SJared McNeill 721*d3810ff9SJared McNeill static void 722*d3810ff9SJared McNeill awg_rxintr(struct awg_softc *sc) 723*d3810ff9SJared McNeill { 724*d3810ff9SJared McNeill if_t ifp; 725*d3810ff9SJared McNeill struct mbuf *m, *m0; 726*d3810ff9SJared McNeill int error, index, len; 727*d3810ff9SJared McNeill uint32_t status; 728*d3810ff9SJared McNeill 729*d3810ff9SJared McNeill ifp = sc->ifp; 730*d3810ff9SJared McNeill 731*d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 732*d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 733*d3810ff9SJared McNeill 734*d3810ff9SJared McNeill for (index = sc->rx.cur; ; index = RX_NEXT(index)) { 735*d3810ff9SJared McNeill status = le32toh(sc->rx.desc_ring[index].status); 736*d3810ff9SJared McNeill if ((status & RX_DESC_CTL) != 0) 737*d3810ff9SJared McNeill break; 738*d3810ff9SJared McNeill 739*d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 740*d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD); 741*d3810ff9SJared McNeill bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map); 742*d3810ff9SJared McNeill 743*d3810ff9SJared McNeill len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; 744*d3810ff9SJared McNeill if (len != 0) { 745*d3810ff9SJared McNeill m = sc->rx.buf_map[index].mbuf; 746*d3810ff9SJared McNeill m->m_pkthdr.rcvif = ifp; 747*d3810ff9SJared McNeill m->m_pkthdr.len = len; 748*d3810ff9SJared McNeill m->m_len = len; 749*d3810ff9SJared McNeill if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 750*d3810ff9SJared McNeill 751*d3810ff9SJared McNeill if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 752*d3810ff9SJared McNeill (status & RX_FRM_TYPE) != 0) { 753*d3810ff9SJared McNeill m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 754*d3810ff9SJared McNeill if ((status & RX_HEADER_ERR) == 0) 755*d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 756*d3810ff9SJared McNeill if ((status & RX_PAYLOAD_ERR) == 0) { 757*d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= 758*d3810ff9SJared McNeill CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 759*d3810ff9SJared McNeill m->m_pkthdr.csum_data = 0xffff; 760*d3810ff9SJared McNeill } 761*d3810ff9SJared McNeill } 762*d3810ff9SJared McNeill 763*d3810ff9SJared McNeill AWG_UNLOCK(sc); 764*d3810ff9SJared McNeill if_input(ifp, m); 765*d3810ff9SJared McNeill AWG_LOCK(sc); 766*d3810ff9SJared McNeill } 767*d3810ff9SJared McNeill 768*d3810ff9SJared McNeill if ((m0 = awg_alloc_mbufcl(sc)) != NULL) { 769*d3810ff9SJared McNeill error = awg_setup_rxbuf(sc, index, m0); 770*d3810ff9SJared McNeill if (error != 0) { 771*d3810ff9SJared McNeill /* XXX hole in RX ring */ 772*d3810ff9SJared McNeill } 773*d3810ff9SJared McNeill } else 774*d3810ff9SJared McNeill if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 775*d3810ff9SJared McNeill } 776*d3810ff9SJared McNeill 777*d3810ff9SJared McNeill if (index != sc->rx.cur) { 778*d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 779*d3810ff9SJared McNeill BUS_DMASYNC_PREWRITE); 780*d3810ff9SJared McNeill } 781*d3810ff9SJared McNeill 782*d3810ff9SJared McNeill sc->rx.cur = index; 783*d3810ff9SJared McNeill } 784*d3810ff9SJared McNeill 785*d3810ff9SJared McNeill static void 786*d3810ff9SJared McNeill awg_txintr(struct awg_softc *sc) 787*d3810ff9SJared McNeill { 788*d3810ff9SJared McNeill struct awg_bufmap *bmap; 789*d3810ff9SJared McNeill struct emac_desc *desc; 790*d3810ff9SJared McNeill uint32_t status; 791*d3810ff9SJared McNeill if_t ifp; 792*d3810ff9SJared McNeill int i; 793*d3810ff9SJared McNeill 794*d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 795*d3810ff9SJared McNeill 796*d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 797*d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 798*d3810ff9SJared McNeill 799*d3810ff9SJared McNeill ifp = sc->ifp; 800*d3810ff9SJared McNeill for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { 801*d3810ff9SJared McNeill desc = &sc->tx.desc_ring[i]; 802*d3810ff9SJared McNeill status = le32toh(desc->status); 803*d3810ff9SJared McNeill if ((status & TX_DESC_CTL) != 0) 804*d3810ff9SJared McNeill break; 805*d3810ff9SJared McNeill bmap = &sc->tx.buf_map[i]; 806*d3810ff9SJared McNeill if (bmap->mbuf != NULL) { 807*d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.buf_tag, bmap->map, 808*d3810ff9SJared McNeill BUS_DMASYNC_POSTWRITE); 809*d3810ff9SJared McNeill bus_dmamap_unload(sc->tx.buf_tag, bmap->map); 810*d3810ff9SJared McNeill m_freem(bmap->mbuf); 811*d3810ff9SJared McNeill bmap->mbuf = NULL; 812*d3810ff9SJared McNeill } 813*d3810ff9SJared McNeill awg_setup_txdesc(sc, i, 0, 0, 0); 814*d3810ff9SJared McNeill if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 815*d3810ff9SJared McNeill if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 816*d3810ff9SJared McNeill } 817*d3810ff9SJared McNeill 818*d3810ff9SJared McNeill sc->tx.next = i; 819*d3810ff9SJared McNeill 820*d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 821*d3810ff9SJared McNeill BUS_DMASYNC_PREWRITE); 822*d3810ff9SJared McNeill } 823*d3810ff9SJared McNeill 824*d3810ff9SJared McNeill static void 825*d3810ff9SJared McNeill awg_intr(void *arg) 826*d3810ff9SJared McNeill { 827*d3810ff9SJared McNeill struct awg_softc *sc; 828*d3810ff9SJared McNeill uint32_t val; 829*d3810ff9SJared McNeill 830*d3810ff9SJared McNeill sc = arg; 831*d3810ff9SJared McNeill 832*d3810ff9SJared McNeill AWG_LOCK(sc); 833*d3810ff9SJared McNeill val = RD4(sc, EMAC_INT_STA); 834*d3810ff9SJared McNeill WR4(sc, EMAC_INT_STA, val); 835*d3810ff9SJared McNeill 836*d3810ff9SJared McNeill if (val & RX_INT) 837*d3810ff9SJared McNeill awg_rxintr(sc); 838*d3810ff9SJared McNeill 839*d3810ff9SJared McNeill if (val & (TX_INT|TX_BUF_UA_INT)) { 840*d3810ff9SJared McNeill awg_txintr(sc); 841*d3810ff9SJared McNeill if (!if_sendq_empty(sc->ifp)) 842*d3810ff9SJared McNeill awg_start_locked(sc); 843*d3810ff9SJared McNeill } 844*d3810ff9SJared McNeill 845*d3810ff9SJared McNeill AWG_UNLOCK(sc); 846*d3810ff9SJared McNeill } 847*d3810ff9SJared McNeill 848*d3810ff9SJared McNeill static int 849*d3810ff9SJared McNeill awg_ioctl(if_t ifp, u_long cmd, caddr_t data) 850*d3810ff9SJared McNeill { 851*d3810ff9SJared McNeill struct awg_softc *sc; 852*d3810ff9SJared McNeill struct mii_data *mii; 853*d3810ff9SJared McNeill struct ifreq *ifr; 854*d3810ff9SJared McNeill int flags, mask, error; 855*d3810ff9SJared McNeill 856*d3810ff9SJared McNeill sc = if_getsoftc(ifp); 857*d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 858*d3810ff9SJared McNeill ifr = (struct ifreq *)data; 859*d3810ff9SJared McNeill error = 0; 860*d3810ff9SJared McNeill 861*d3810ff9SJared McNeill switch (cmd) { 862*d3810ff9SJared McNeill case SIOCSIFFLAGS: 863*d3810ff9SJared McNeill AWG_LOCK(sc); 864*d3810ff9SJared McNeill if (if_getflags(ifp) & IFF_UP) { 865*d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 866*d3810ff9SJared McNeill flags = if_getflags(ifp) ^ sc->if_flags; 867*d3810ff9SJared McNeill if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 868*d3810ff9SJared McNeill awg_setup_rxfilter(sc); 869*d3810ff9SJared McNeill } else 870*d3810ff9SJared McNeill awg_init_locked(sc); 871*d3810ff9SJared McNeill } else { 872*d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 873*d3810ff9SJared McNeill awg_stop(sc); 874*d3810ff9SJared McNeill } 875*d3810ff9SJared McNeill sc->if_flags = if_getflags(ifp); 876*d3810ff9SJared McNeill AWG_UNLOCK(sc); 877*d3810ff9SJared McNeill break; 878*d3810ff9SJared McNeill case SIOCADDMULTI: 879*d3810ff9SJared McNeill case SIOCDELMULTI: 880*d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 881*d3810ff9SJared McNeill AWG_LOCK(sc); 882*d3810ff9SJared McNeill awg_setup_rxfilter(sc); 883*d3810ff9SJared McNeill AWG_UNLOCK(sc); 884*d3810ff9SJared McNeill } 885*d3810ff9SJared McNeill break; 886*d3810ff9SJared McNeill case SIOCSIFMEDIA: 887*d3810ff9SJared McNeill case SIOCGIFMEDIA: 888*d3810ff9SJared McNeill error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 889*d3810ff9SJared McNeill break; 890*d3810ff9SJared McNeill case SIOCSIFCAP: 891*d3810ff9SJared McNeill mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 892*d3810ff9SJared McNeill if (mask & IFCAP_VLAN_MTU) 893*d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_VLAN_MTU); 894*d3810ff9SJared McNeill if (mask & IFCAP_RXCSUM) 895*d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_RXCSUM); 896*d3810ff9SJared McNeill if (mask & IFCAP_TXCSUM) 897*d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_TXCSUM); 898*d3810ff9SJared McNeill if ((if_getcapenable(ifp) & (IFCAP_RXCSUM|IFCAP_TXCSUM)) != 0) 899*d3810ff9SJared McNeill if_sethwassistbits(ifp, CSUM_IP, 0); 900*d3810ff9SJared McNeill else 901*d3810ff9SJared McNeill if_sethwassistbits(ifp, 0, CSUM_IP); 902*d3810ff9SJared McNeill break; 903*d3810ff9SJared McNeill default: 904*d3810ff9SJared McNeill error = ether_ioctl(ifp, cmd, data); 905*d3810ff9SJared McNeill break; 906*d3810ff9SJared McNeill } 907*d3810ff9SJared McNeill 908*d3810ff9SJared McNeill return (error); 909*d3810ff9SJared McNeill } 910*d3810ff9SJared McNeill 911*d3810ff9SJared McNeill static int 912*d3810ff9SJared McNeill awg_setup_extres(device_t dev) 913*d3810ff9SJared McNeill { 914*d3810ff9SJared McNeill struct awg_softc *sc; 915*d3810ff9SJared McNeill hwreset_t rst_ahb; 916*d3810ff9SJared McNeill clk_t clk_ahb, clk_tx, clk_tx_parent; 917*d3810ff9SJared McNeill regulator_t reg; 918*d3810ff9SJared McNeill const char *tx_parent_name; 919*d3810ff9SJared McNeill char *phy_type; 920*d3810ff9SJared McNeill phandle_t node; 921*d3810ff9SJared McNeill uint64_t freq; 922*d3810ff9SJared McNeill int error, div; 923*d3810ff9SJared McNeill 924*d3810ff9SJared McNeill sc = device_get_softc(dev); 925*d3810ff9SJared McNeill node = ofw_bus_get_node(dev); 926*d3810ff9SJared McNeill rst_ahb = NULL; 927*d3810ff9SJared McNeill clk_ahb = NULL; 928*d3810ff9SJared McNeill clk_tx = NULL; 929*d3810ff9SJared McNeill clk_tx_parent = NULL; 930*d3810ff9SJared McNeill reg = NULL; 931*d3810ff9SJared McNeill phy_type = NULL; 932*d3810ff9SJared McNeill 933*d3810ff9SJared McNeill /* Get AHB clock and reset resources */ 934*d3810ff9SJared McNeill error = hwreset_get_by_ofw_name(dev, "ahb", &rst_ahb); 935*d3810ff9SJared McNeill if (error != 0) { 936*d3810ff9SJared McNeill device_printf(dev, "cannot get ahb reset\n"); 937*d3810ff9SJared McNeill goto fail; 938*d3810ff9SJared McNeill } 939*d3810ff9SJared McNeill error = clk_get_by_ofw_name(dev, "ahb", &clk_ahb); 940*d3810ff9SJared McNeill if (error != 0) { 941*d3810ff9SJared McNeill device_printf(dev, "cannot get ahb clock\n"); 942*d3810ff9SJared McNeill goto fail; 943*d3810ff9SJared McNeill } 944*d3810ff9SJared McNeill 945*d3810ff9SJared McNeill /* Configure PHY for MII or RGMII mode */ 946*d3810ff9SJared McNeill if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type)) { 947*d3810ff9SJared McNeill if (bootverbose) 948*d3810ff9SJared McNeill device_printf(dev, "PHY type: %s\n", phy_type); 949*d3810ff9SJared McNeill 950*d3810ff9SJared McNeill if (strcmp(phy_type, "rgmii") == 0) 951*d3810ff9SJared McNeill tx_parent_name = "emac_int_tx"; 952*d3810ff9SJared McNeill else 953*d3810ff9SJared McNeill tx_parent_name = "mii_phy_tx"; 954*d3810ff9SJared McNeill free(phy_type, M_OFWPROP); 955*d3810ff9SJared McNeill 956*d3810ff9SJared McNeill /* Get the TX clock */ 957*d3810ff9SJared McNeill error = clk_get_by_ofw_name(dev, "tx", &clk_tx); 958*d3810ff9SJared McNeill if (error != 0) { 959*d3810ff9SJared McNeill device_printf(dev, "cannot get tx clock\n"); 960*d3810ff9SJared McNeill goto fail; 961*d3810ff9SJared McNeill } 962*d3810ff9SJared McNeill 963*d3810ff9SJared McNeill /* Find the desired parent clock based on phy-mode property */ 964*d3810ff9SJared McNeill error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); 965*d3810ff9SJared McNeill if (error != 0) { 966*d3810ff9SJared McNeill device_printf(dev, "cannot get clock '%s'\n", 967*d3810ff9SJared McNeill tx_parent_name); 968*d3810ff9SJared McNeill goto fail; 969*d3810ff9SJared McNeill } 970*d3810ff9SJared McNeill 971*d3810ff9SJared McNeill /* Set TX clock parent */ 972*d3810ff9SJared McNeill error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); 973*d3810ff9SJared McNeill if (error != 0) { 974*d3810ff9SJared McNeill device_printf(dev, "cannot set tx clock parent\n"); 975*d3810ff9SJared McNeill goto fail; 976*d3810ff9SJared McNeill } 977*d3810ff9SJared McNeill 978*d3810ff9SJared McNeill /* Enable TX clock */ 979*d3810ff9SJared McNeill error = clk_enable(clk_tx); 980*d3810ff9SJared McNeill if (error != 0) { 981*d3810ff9SJared McNeill device_printf(dev, "cannot enable tx clock\n"); 982*d3810ff9SJared McNeill goto fail; 983*d3810ff9SJared McNeill } 984*d3810ff9SJared McNeill } 985*d3810ff9SJared McNeill 986*d3810ff9SJared McNeill /* Enable AHB clock */ 987*d3810ff9SJared McNeill error = clk_enable(clk_ahb); 988*d3810ff9SJared McNeill if (error != 0) { 989*d3810ff9SJared McNeill device_printf(dev, "cannot enable ahb clock\n"); 990*d3810ff9SJared McNeill goto fail; 991*d3810ff9SJared McNeill } 992*d3810ff9SJared McNeill 993*d3810ff9SJared McNeill /* De-assert reset */ 994*d3810ff9SJared McNeill error = hwreset_deassert(rst_ahb); 995*d3810ff9SJared McNeill if (error != 0) { 996*d3810ff9SJared McNeill device_printf(dev, "cannot de-assert ahb reset\n"); 997*d3810ff9SJared McNeill goto fail; 998*d3810ff9SJared McNeill } 999*d3810ff9SJared McNeill 1000*d3810ff9SJared McNeill /* Enable PHY regulator if applicable */ 1001*d3810ff9SJared McNeill if (regulator_get_by_ofw_property(dev, "phy-supply", ®) == 0) { 1002*d3810ff9SJared McNeill error = regulator_enable(reg); 1003*d3810ff9SJared McNeill if (error != 0) { 1004*d3810ff9SJared McNeill device_printf(dev, "cannot enable PHY regulator\n"); 1005*d3810ff9SJared McNeill goto fail; 1006*d3810ff9SJared McNeill } 1007*d3810ff9SJared McNeill } 1008*d3810ff9SJared McNeill 1009*d3810ff9SJared McNeill /* Determine MDC clock divide ratio based on AHB clock */ 1010*d3810ff9SJared McNeill error = clk_get_freq(clk_ahb, &freq); 1011*d3810ff9SJared McNeill if (error != 0) { 1012*d3810ff9SJared McNeill device_printf(dev, "cannot get AHB clock frequency\n"); 1013*d3810ff9SJared McNeill goto fail; 1014*d3810ff9SJared McNeill } 1015*d3810ff9SJared McNeill div = freq / MDIO_FREQ; 1016*d3810ff9SJared McNeill if (div <= 16) 1017*d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16; 1018*d3810ff9SJared McNeill else if (div <= 32) 1019*d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32; 1020*d3810ff9SJared McNeill else if (div <= 64) 1021*d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64; 1022*d3810ff9SJared McNeill else if (div <= 128) 1023*d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128; 1024*d3810ff9SJared McNeill else { 1025*d3810ff9SJared McNeill device_printf(dev, "cannot determine MDC clock divide ratio\n"); 1026*d3810ff9SJared McNeill error = ENXIO; 1027*d3810ff9SJared McNeill goto fail; 1028*d3810ff9SJared McNeill } 1029*d3810ff9SJared McNeill 1030*d3810ff9SJared McNeill if (bootverbose) 1031*d3810ff9SJared McNeill device_printf(dev, "AHB frequency %llu Hz, MDC div: 0x%x\n", 1032*d3810ff9SJared McNeill freq, sc->mdc_div_ratio_m); 1033*d3810ff9SJared McNeill 1034*d3810ff9SJared McNeill return (0); 1035*d3810ff9SJared McNeill 1036*d3810ff9SJared McNeill fail: 1037*d3810ff9SJared McNeill free(phy_type, M_OFWPROP); 1038*d3810ff9SJared McNeill 1039*d3810ff9SJared McNeill if (reg != NULL) 1040*d3810ff9SJared McNeill regulator_release(reg); 1041*d3810ff9SJared McNeill if (clk_tx_parent != NULL) 1042*d3810ff9SJared McNeill clk_release(clk_tx_parent); 1043*d3810ff9SJared McNeill if (clk_tx != NULL) 1044*d3810ff9SJared McNeill clk_release(clk_tx); 1045*d3810ff9SJared McNeill if (clk_ahb != NULL) 1046*d3810ff9SJared McNeill clk_release(clk_ahb); 1047*d3810ff9SJared McNeill if (rst_ahb != NULL) 1048*d3810ff9SJared McNeill hwreset_release(rst_ahb); 1049*d3810ff9SJared McNeill return (error); 1050*d3810ff9SJared McNeill } 1051*d3810ff9SJared McNeill 1052*d3810ff9SJared McNeill static void 1053*d3810ff9SJared McNeill awg_get_eaddr(device_t dev, uint8_t *eaddr) 1054*d3810ff9SJared McNeill { 1055*d3810ff9SJared McNeill struct awg_softc *sc; 1056*d3810ff9SJared McNeill uint32_t maclo, machi, rnd; 1057*d3810ff9SJared McNeill 1058*d3810ff9SJared McNeill sc = device_get_softc(dev); 1059*d3810ff9SJared McNeill 1060*d3810ff9SJared McNeill machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff; 1061*d3810ff9SJared McNeill maclo = RD4(sc, EMAC_ADDR_LOW(0)); 1062*d3810ff9SJared McNeill 1063*d3810ff9SJared McNeill if (maclo == 0xffffffff && machi == 0xffff) { 1064*d3810ff9SJared McNeill /* MAC address in hardware is invalid, create one */ 1065*d3810ff9SJared McNeill rnd = arc4random(); 1066*d3810ff9SJared McNeill maclo = 0x00f2 | (rnd & 0xffff0000); 1067*d3810ff9SJared McNeill machi = rnd & 0xffff; 1068*d3810ff9SJared McNeill } 1069*d3810ff9SJared McNeill 1070*d3810ff9SJared McNeill eaddr[0] = maclo & 0xff; 1071*d3810ff9SJared McNeill eaddr[1] = (maclo >> 8) & 0xff; 1072*d3810ff9SJared McNeill eaddr[2] = (maclo >> 16) & 0xff; 1073*d3810ff9SJared McNeill eaddr[3] = (maclo >> 24) & 0xff; 1074*d3810ff9SJared McNeill eaddr[4] = machi & 0xff; 1075*d3810ff9SJared McNeill eaddr[5] = (machi >> 8) & 0xff; 1076*d3810ff9SJared McNeill } 1077*d3810ff9SJared McNeill 1078*d3810ff9SJared McNeill #ifdef AWG_DEBUG 1079*d3810ff9SJared McNeill static void 1080*d3810ff9SJared McNeill awg_dump_regs(device_t dev) 1081*d3810ff9SJared McNeill { 1082*d3810ff9SJared McNeill static const struct { 1083*d3810ff9SJared McNeill const char *name; 1084*d3810ff9SJared McNeill u_int reg; 1085*d3810ff9SJared McNeill } regs[] = { 1086*d3810ff9SJared McNeill { "BASIC_CTL_0", EMAC_BASIC_CTL_0 }, 1087*d3810ff9SJared McNeill { "BASIC_CTL_1", EMAC_BASIC_CTL_1 }, 1088*d3810ff9SJared McNeill { "INT_STA", EMAC_INT_STA }, 1089*d3810ff9SJared McNeill { "INT_EN", EMAC_INT_EN }, 1090*d3810ff9SJared McNeill { "TX_CTL_0", EMAC_TX_CTL_0 }, 1091*d3810ff9SJared McNeill { "TX_CTL_1", EMAC_TX_CTL_1 }, 1092*d3810ff9SJared McNeill { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL }, 1093*d3810ff9SJared McNeill { "TX_DMA_LIST", EMAC_TX_DMA_LIST }, 1094*d3810ff9SJared McNeill { "RX_CTL_0", EMAC_RX_CTL_0 }, 1095*d3810ff9SJared McNeill { "RX_CTL_1", EMAC_RX_CTL_1 }, 1096*d3810ff9SJared McNeill { "RX_DMA_LIST", EMAC_RX_DMA_LIST }, 1097*d3810ff9SJared McNeill { "RX_FRM_FLT", EMAC_RX_FRM_FLT }, 1098*d3810ff9SJared McNeill { "RX_HASH_0", EMAC_RX_HASH_0 }, 1099*d3810ff9SJared McNeill { "RX_HASH_1", EMAC_RX_HASH_1 }, 1100*d3810ff9SJared McNeill { "MII_CMD", EMAC_MII_CMD }, 1101*d3810ff9SJared McNeill { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) }, 1102*d3810ff9SJared McNeill { "ADDR_LOW0", EMAC_ADDR_LOW(0) }, 1103*d3810ff9SJared McNeill { "TX_DMA_STA", EMAC_TX_DMA_STA }, 1104*d3810ff9SJared McNeill { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC }, 1105*d3810ff9SJared McNeill { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF }, 1106*d3810ff9SJared McNeill { "RX_DMA_STA", EMAC_RX_DMA_STA }, 1107*d3810ff9SJared McNeill { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC }, 1108*d3810ff9SJared McNeill { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF }, 1109*d3810ff9SJared McNeill { "RGMII_STA", EMAC_RGMII_STA }, 1110*d3810ff9SJared McNeill }; 1111*d3810ff9SJared McNeill struct awg_softc *sc; 1112*d3810ff9SJared McNeill unsigned int n; 1113*d3810ff9SJared McNeill 1114*d3810ff9SJared McNeill sc = device_get_softc(dev); 1115*d3810ff9SJared McNeill 1116*d3810ff9SJared McNeill for (n = 0; n < nitems(regs); n++) 1117*d3810ff9SJared McNeill device_printf(dev, " %-20s %08x\n", regs[n].name, 1118*d3810ff9SJared McNeill RD4(sc, regs[n].reg)); 1119*d3810ff9SJared McNeill } 1120*d3810ff9SJared McNeill #endif 1121*d3810ff9SJared McNeill 1122*d3810ff9SJared McNeill static int 1123*d3810ff9SJared McNeill awg_reset(device_t dev) 1124*d3810ff9SJared McNeill { 1125*d3810ff9SJared McNeill struct awg_softc *sc; 1126*d3810ff9SJared McNeill int retry; 1127*d3810ff9SJared McNeill 1128*d3810ff9SJared McNeill sc = device_get_softc(dev); 1129*d3810ff9SJared McNeill 1130*d3810ff9SJared McNeill /* Soft reset all registers and logic */ 1131*d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); 1132*d3810ff9SJared McNeill 1133*d3810ff9SJared McNeill /* Wait for soft reset bit to self-clear */ 1134*d3810ff9SJared McNeill for (retry = SOFT_RST_RETRY; retry > 0; retry--) { 1135*d3810ff9SJared McNeill if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0) 1136*d3810ff9SJared McNeill break; 1137*d3810ff9SJared McNeill DELAY(10); 1138*d3810ff9SJared McNeill } 1139*d3810ff9SJared McNeill if (retry == 0) { 1140*d3810ff9SJared McNeill device_printf(dev, "soft reset timed out\n"); 1141*d3810ff9SJared McNeill #ifdef AWG_DEBUG 1142*d3810ff9SJared McNeill awg_dump_regs(dev); 1143*d3810ff9SJared McNeill #endif 1144*d3810ff9SJared McNeill return (ETIMEDOUT); 1145*d3810ff9SJared McNeill } 1146*d3810ff9SJared McNeill 1147*d3810ff9SJared McNeill return (0); 1148*d3810ff9SJared McNeill } 1149*d3810ff9SJared McNeill 1150*d3810ff9SJared McNeill static void 1151*d3810ff9SJared McNeill awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 1152*d3810ff9SJared McNeill { 1153*d3810ff9SJared McNeill if (error != 0) 1154*d3810ff9SJared McNeill return; 1155*d3810ff9SJared McNeill *(bus_addr_t *)arg = segs[0].ds_addr; 1156*d3810ff9SJared McNeill } 1157*d3810ff9SJared McNeill 1158*d3810ff9SJared McNeill static int 1159*d3810ff9SJared McNeill awg_setup_dma(device_t dev) 1160*d3810ff9SJared McNeill { 1161*d3810ff9SJared McNeill struct awg_softc *sc; 1162*d3810ff9SJared McNeill struct mbuf *m; 1163*d3810ff9SJared McNeill int error, i; 1164*d3810ff9SJared McNeill 1165*d3810ff9SJared McNeill sc = device_get_softc(dev); 1166*d3810ff9SJared McNeill 1167*d3810ff9SJared McNeill /* Setup TX ring */ 1168*d3810ff9SJared McNeill error = bus_dma_tag_create( 1169*d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1170*d3810ff9SJared McNeill DESC_ALIGN, 0, /* alignment, boundary */ 1171*d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1172*d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1173*d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1174*d3810ff9SJared McNeill TX_DESC_SIZE, 1, /* maxsize, nsegs */ 1175*d3810ff9SJared McNeill TX_DESC_SIZE, /* maxsegsize */ 1176*d3810ff9SJared McNeill 0, /* flags */ 1177*d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1178*d3810ff9SJared McNeill &sc->tx.desc_tag); 1179*d3810ff9SJared McNeill if (error != 0) { 1180*d3810ff9SJared McNeill device_printf(dev, "cannot create TX descriptor ring tag\n"); 1181*d3810ff9SJared McNeill return (error); 1182*d3810ff9SJared McNeill } 1183*d3810ff9SJared McNeill 1184*d3810ff9SJared McNeill error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring, 1185*d3810ff9SJared McNeill BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map); 1186*d3810ff9SJared McNeill if (error != 0) { 1187*d3810ff9SJared McNeill device_printf(dev, "cannot allocate TX descriptor ring\n"); 1188*d3810ff9SJared McNeill return (error); 1189*d3810ff9SJared McNeill } 1190*d3810ff9SJared McNeill 1191*d3810ff9SJared McNeill error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map, 1192*d3810ff9SJared McNeill sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb, 1193*d3810ff9SJared McNeill &sc->tx.desc_ring_paddr, 0); 1194*d3810ff9SJared McNeill if (error != 0) { 1195*d3810ff9SJared McNeill device_printf(dev, "cannot load TX descriptor ring\n"); 1196*d3810ff9SJared McNeill return (error); 1197*d3810ff9SJared McNeill } 1198*d3810ff9SJared McNeill 1199*d3810ff9SJared McNeill for (i = 0; i < TX_DESC_COUNT; i++) 1200*d3810ff9SJared McNeill sc->tx.desc_ring[i].next = 1201*d3810ff9SJared McNeill htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i))); 1202*d3810ff9SJared McNeill 1203*d3810ff9SJared McNeill error = bus_dma_tag_create( 1204*d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1205*d3810ff9SJared McNeill 1, 0, /* alignment, boundary */ 1206*d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1207*d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1208*d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1209*d3810ff9SJared McNeill MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ 1210*d3810ff9SJared McNeill MCLBYTES, /* maxsegsize */ 1211*d3810ff9SJared McNeill 0, /* flags */ 1212*d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1213*d3810ff9SJared McNeill &sc->tx.buf_tag); 1214*d3810ff9SJared McNeill if (error != 0) { 1215*d3810ff9SJared McNeill device_printf(dev, "cannot create TX buffer tag\n"); 1216*d3810ff9SJared McNeill return (error); 1217*d3810ff9SJared McNeill } 1218*d3810ff9SJared McNeill 1219*d3810ff9SJared McNeill sc->tx.queued = TX_DESC_COUNT; 1220*d3810ff9SJared McNeill for (i = 0; i < TX_DESC_COUNT; i++) { 1221*d3810ff9SJared McNeill error = bus_dmamap_create(sc->tx.buf_tag, 0, 1222*d3810ff9SJared McNeill &sc->tx.buf_map[i].map); 1223*d3810ff9SJared McNeill if (error != 0) { 1224*d3810ff9SJared McNeill device_printf(dev, "cannot create TX buffer map\n"); 1225*d3810ff9SJared McNeill return (error); 1226*d3810ff9SJared McNeill } 1227*d3810ff9SJared McNeill awg_setup_txdesc(sc, i, 0, 0, 0); 1228*d3810ff9SJared McNeill } 1229*d3810ff9SJared McNeill 1230*d3810ff9SJared McNeill /* Setup RX ring */ 1231*d3810ff9SJared McNeill error = bus_dma_tag_create( 1232*d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1233*d3810ff9SJared McNeill DESC_ALIGN, 0, /* alignment, boundary */ 1234*d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1235*d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1236*d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1237*d3810ff9SJared McNeill RX_DESC_SIZE, 1, /* maxsize, nsegs */ 1238*d3810ff9SJared McNeill RX_DESC_SIZE, /* maxsegsize */ 1239*d3810ff9SJared McNeill 0, /* flags */ 1240*d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1241*d3810ff9SJared McNeill &sc->rx.desc_tag); 1242*d3810ff9SJared McNeill if (error != 0) { 1243*d3810ff9SJared McNeill device_printf(dev, "cannot create RX descriptor ring tag\n"); 1244*d3810ff9SJared McNeill return (error); 1245*d3810ff9SJared McNeill } 1246*d3810ff9SJared McNeill 1247*d3810ff9SJared McNeill error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring, 1248*d3810ff9SJared McNeill BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map); 1249*d3810ff9SJared McNeill if (error != 0) { 1250*d3810ff9SJared McNeill device_printf(dev, "cannot allocate RX descriptor ring\n"); 1251*d3810ff9SJared McNeill return (error); 1252*d3810ff9SJared McNeill } 1253*d3810ff9SJared McNeill 1254*d3810ff9SJared McNeill error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map, 1255*d3810ff9SJared McNeill sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb, 1256*d3810ff9SJared McNeill &sc->rx.desc_ring_paddr, 0); 1257*d3810ff9SJared McNeill if (error != 0) { 1258*d3810ff9SJared McNeill device_printf(dev, "cannot load RX descriptor ring\n"); 1259*d3810ff9SJared McNeill return (error); 1260*d3810ff9SJared McNeill } 1261*d3810ff9SJared McNeill 1262*d3810ff9SJared McNeill error = bus_dma_tag_create( 1263*d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1264*d3810ff9SJared McNeill 1, 0, /* alignment, boundary */ 1265*d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1266*d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1267*d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1268*d3810ff9SJared McNeill MCLBYTES, 1, /* maxsize, nsegs */ 1269*d3810ff9SJared McNeill MCLBYTES, /* maxsegsize */ 1270*d3810ff9SJared McNeill 0, /* flags */ 1271*d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1272*d3810ff9SJared McNeill &sc->rx.buf_tag); 1273*d3810ff9SJared McNeill if (error != 0) { 1274*d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer tag\n"); 1275*d3810ff9SJared McNeill return (error); 1276*d3810ff9SJared McNeill } 1277*d3810ff9SJared McNeill 1278*d3810ff9SJared McNeill for (i = 0; i < RX_DESC_COUNT; i++) { 1279*d3810ff9SJared McNeill error = bus_dmamap_create(sc->rx.buf_tag, 0, 1280*d3810ff9SJared McNeill &sc->rx.buf_map[i].map); 1281*d3810ff9SJared McNeill if (error != 0) { 1282*d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer map\n"); 1283*d3810ff9SJared McNeill return (error); 1284*d3810ff9SJared McNeill } 1285*d3810ff9SJared McNeill if ((m = awg_alloc_mbufcl(sc)) == NULL) { 1286*d3810ff9SJared McNeill device_printf(dev, "cannot allocate RX mbuf\n"); 1287*d3810ff9SJared McNeill return (ENOMEM); 1288*d3810ff9SJared McNeill } 1289*d3810ff9SJared McNeill error = awg_setup_rxbuf(sc, i, m); 1290*d3810ff9SJared McNeill if (error != 0) { 1291*d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer\n"); 1292*d3810ff9SJared McNeill return (error); 1293*d3810ff9SJared McNeill } 1294*d3810ff9SJared McNeill } 1295*d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 1296*d3810ff9SJared McNeill BUS_DMASYNC_PREWRITE); 1297*d3810ff9SJared McNeill 1298*d3810ff9SJared McNeill /* Write transmit and receive descriptor base address registers */ 1299*d3810ff9SJared McNeill WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr); 1300*d3810ff9SJared McNeill WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr); 1301*d3810ff9SJared McNeill 1302*d3810ff9SJared McNeill return (0); 1303*d3810ff9SJared McNeill } 1304*d3810ff9SJared McNeill 1305*d3810ff9SJared McNeill static int 1306*d3810ff9SJared McNeill awg_probe(device_t dev) 1307*d3810ff9SJared McNeill { 1308*d3810ff9SJared McNeill if (!ofw_bus_status_okay(dev)) 1309*d3810ff9SJared McNeill return (ENXIO); 1310*d3810ff9SJared McNeill 1311*d3810ff9SJared McNeill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1312*d3810ff9SJared McNeill return (ENXIO); 1313*d3810ff9SJared McNeill 1314*d3810ff9SJared McNeill device_set_desc(dev, "Allwinner Gigabit Ethernet"); 1315*d3810ff9SJared McNeill return (BUS_PROBE_DEFAULT); 1316*d3810ff9SJared McNeill } 1317*d3810ff9SJared McNeill 1318*d3810ff9SJared McNeill static int 1319*d3810ff9SJared McNeill awg_attach(device_t dev) 1320*d3810ff9SJared McNeill { 1321*d3810ff9SJared McNeill uint8_t eaddr[ETHER_ADDR_LEN]; 1322*d3810ff9SJared McNeill struct awg_softc *sc; 1323*d3810ff9SJared McNeill phandle_t node; 1324*d3810ff9SJared McNeill int error; 1325*d3810ff9SJared McNeill 1326*d3810ff9SJared McNeill sc = device_get_softc(dev); 1327*d3810ff9SJared McNeill node = ofw_bus_get_node(dev); 1328*d3810ff9SJared McNeill 1329*d3810ff9SJared McNeill if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { 1330*d3810ff9SJared McNeill device_printf(dev, "cannot allocate resources for device\n"); 1331*d3810ff9SJared McNeill return (ENXIO); 1332*d3810ff9SJared McNeill } 1333*d3810ff9SJared McNeill 1334*d3810ff9SJared McNeill mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); 1335*d3810ff9SJared McNeill callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); 1336*d3810ff9SJared McNeill TASK_INIT(&sc->link_task, 0, awg_link_task, sc); 1337*d3810ff9SJared McNeill 1338*d3810ff9SJared McNeill /* Setup clocks and regulators */ 1339*d3810ff9SJared McNeill error = awg_setup_extres(dev); 1340*d3810ff9SJared McNeill if (error != 0) 1341*d3810ff9SJared McNeill return (error); 1342*d3810ff9SJared McNeill 1343*d3810ff9SJared McNeill /* Read MAC address before resetting the chip */ 1344*d3810ff9SJared McNeill awg_get_eaddr(dev, eaddr); 1345*d3810ff9SJared McNeill 1346*d3810ff9SJared McNeill /* Soft reset EMAC core */ 1347*d3810ff9SJared McNeill error = awg_reset(dev); 1348*d3810ff9SJared McNeill if (error != 0) 1349*d3810ff9SJared McNeill return (error); 1350*d3810ff9SJared McNeill 1351*d3810ff9SJared McNeill /* Setup DMA descriptors */ 1352*d3810ff9SJared McNeill error = awg_setup_dma(dev); 1353*d3810ff9SJared McNeill if (error != 0) 1354*d3810ff9SJared McNeill return (error); 1355*d3810ff9SJared McNeill 1356*d3810ff9SJared McNeill /* Install interrupt handler */ 1357*d3810ff9SJared McNeill error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1358*d3810ff9SJared McNeill NULL, awg_intr, sc, &sc->ih); 1359*d3810ff9SJared McNeill if (error != 0) { 1360*d3810ff9SJared McNeill device_printf(dev, "cannot setup interrupt handler\n"); 1361*d3810ff9SJared McNeill return (error); 1362*d3810ff9SJared McNeill } 1363*d3810ff9SJared McNeill 1364*d3810ff9SJared McNeill /* Setup ethernet interface */ 1365*d3810ff9SJared McNeill sc->ifp = if_alloc(IFT_ETHER); 1366*d3810ff9SJared McNeill if_setsoftc(sc->ifp, sc); 1367*d3810ff9SJared McNeill if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); 1368*d3810ff9SJared McNeill if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 1369*d3810ff9SJared McNeill if_setstartfn(sc->ifp, awg_start); 1370*d3810ff9SJared McNeill if_setioctlfn(sc->ifp, awg_ioctl); 1371*d3810ff9SJared McNeill if_setinitfn(sc->ifp, awg_init); 1372*d3810ff9SJared McNeill if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); 1373*d3810ff9SJared McNeill if_setsendqready(sc->ifp); 1374*d3810ff9SJared McNeill if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 1375*d3810ff9SJared McNeill if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 1376*d3810ff9SJared McNeill if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 1377*d3810ff9SJared McNeill 1378*d3810ff9SJared McNeill /* Attach MII driver */ 1379*d3810ff9SJared McNeill error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change, 1380*d3810ff9SJared McNeill awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 1381*d3810ff9SJared McNeill MIIF_DOPAUSE); 1382*d3810ff9SJared McNeill if (error != 0) { 1383*d3810ff9SJared McNeill device_printf(dev, "cannot attach PHY\n"); 1384*d3810ff9SJared McNeill return (error); 1385*d3810ff9SJared McNeill } 1386*d3810ff9SJared McNeill 1387*d3810ff9SJared McNeill /* Attach ethernet interface */ 1388*d3810ff9SJared McNeill ether_ifattach(sc->ifp, eaddr); 1389*d3810ff9SJared McNeill 1390*d3810ff9SJared McNeill return (0); 1391*d3810ff9SJared McNeill } 1392*d3810ff9SJared McNeill 1393*d3810ff9SJared McNeill static device_method_t awg_methods[] = { 1394*d3810ff9SJared McNeill /* Device interface */ 1395*d3810ff9SJared McNeill DEVMETHOD(device_probe, awg_probe), 1396*d3810ff9SJared McNeill DEVMETHOD(device_attach, awg_attach), 1397*d3810ff9SJared McNeill 1398*d3810ff9SJared McNeill /* MII interface */ 1399*d3810ff9SJared McNeill DEVMETHOD(miibus_readreg, awg_miibus_readreg), 1400*d3810ff9SJared McNeill DEVMETHOD(miibus_writereg, awg_miibus_writereg), 1401*d3810ff9SJared McNeill DEVMETHOD(miibus_statchg, awg_miibus_statchg), 1402*d3810ff9SJared McNeill 1403*d3810ff9SJared McNeill DEVMETHOD_END 1404*d3810ff9SJared McNeill }; 1405*d3810ff9SJared McNeill 1406*d3810ff9SJared McNeill static driver_t awg_driver = { 1407*d3810ff9SJared McNeill "awg", 1408*d3810ff9SJared McNeill awg_methods, 1409*d3810ff9SJared McNeill sizeof(struct awg_softc), 1410*d3810ff9SJared McNeill }; 1411*d3810ff9SJared McNeill 1412*d3810ff9SJared McNeill static devclass_t awg_devclass; 1413*d3810ff9SJared McNeill 1414*d3810ff9SJared McNeill DRIVER_MODULE(awg, simplebus, awg_driver, awg_devclass, 0, 0); 1415*d3810ff9SJared McNeill DRIVER_MODULE(miibus, awg, miibus_driver, miibus_devclass, 0, 0); 1416*d3810ff9SJared McNeill 1417*d3810ff9SJared McNeill MODULE_DEPEND(awg, ether, 1, 1, 1); 1418*d3810ff9SJared McNeill MODULE_DEPEND(awg, miibus, 1, 1, 1); 1419