1d3810ff9SJared McNeill /*- 2d3810ff9SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3d3810ff9SJared McNeill * All rights reserved. 4d3810ff9SJared McNeill * 5d3810ff9SJared McNeill * Redistribution and use in source and binary forms, with or without 6d3810ff9SJared McNeill * modification, are permitted provided that the following conditions 7d3810ff9SJared McNeill * are met: 8d3810ff9SJared McNeill * 1. Redistributions of source code must retain the above copyright 9d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer. 10d3810ff9SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright 11d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer in the 12d3810ff9SJared McNeill * documentation and/or other materials provided with the distribution. 13d3810ff9SJared McNeill * 14d3810ff9SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15d3810ff9SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16d3810ff9SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17d3810ff9SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18d3810ff9SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19d3810ff9SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20d3810ff9SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21d3810ff9SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22d3810ff9SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23d3810ff9SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24d3810ff9SJared McNeill * SUCH DAMAGE. 25d3810ff9SJared McNeill * 26d3810ff9SJared McNeill * $FreeBSD$ 27d3810ff9SJared McNeill */ 28d3810ff9SJared McNeill 29d3810ff9SJared McNeill /* 30d3810ff9SJared McNeill * Allwinner Gigabit Ethernet MAC (EMAC) controller 31d3810ff9SJared McNeill */ 32d3810ff9SJared McNeill 3316928528SJared McNeill #include "opt_device_polling.h" 3416928528SJared McNeill 35d3810ff9SJared McNeill #include <sys/cdefs.h> 36d3810ff9SJared McNeill __FBSDID("$FreeBSD$"); 37d3810ff9SJared McNeill 38d3810ff9SJared McNeill #include <sys/param.h> 39d3810ff9SJared McNeill #include <sys/systm.h> 40d3810ff9SJared McNeill #include <sys/bus.h> 41d3810ff9SJared McNeill #include <sys/rman.h> 42d3810ff9SJared McNeill #include <sys/kernel.h> 43d3810ff9SJared McNeill #include <sys/endian.h> 44d3810ff9SJared McNeill #include <sys/mbuf.h> 45d3810ff9SJared McNeill #include <sys/socket.h> 46d3810ff9SJared McNeill #include <sys/sockio.h> 47d3810ff9SJared McNeill #include <sys/module.h> 48d3810ff9SJared McNeill #include <sys/taskqueue.h> 4901a469b8SJared McNeill #include <sys/gpio.h> 50d3810ff9SJared McNeill 51d3810ff9SJared McNeill #include <net/bpf.h> 52d3810ff9SJared McNeill #include <net/if.h> 53d3810ff9SJared McNeill #include <net/ethernet.h> 54d3810ff9SJared McNeill #include <net/if_dl.h> 55d3810ff9SJared McNeill #include <net/if_media.h> 56d3810ff9SJared McNeill #include <net/if_types.h> 57d3810ff9SJared McNeill #include <net/if_var.h> 58d3810ff9SJared McNeill 59d3810ff9SJared McNeill #include <machine/bus.h> 60d3810ff9SJared McNeill 61d3810ff9SJared McNeill #include <dev/ofw/ofw_bus.h> 62d3810ff9SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 63d3810ff9SJared McNeill 64d3810ff9SJared McNeill #include <arm/allwinner/if_awgreg.h> 651403e695SJared McNeill #include <arm/allwinner/aw_sid.h> 66d3810ff9SJared McNeill #include <dev/mii/mii.h> 67d3810ff9SJared McNeill #include <dev/mii/miivar.h> 68d3810ff9SJared McNeill 69d3810ff9SJared McNeill #include <dev/extres/clk/clk.h> 70d3810ff9SJared McNeill #include <dev/extres/hwreset/hwreset.h> 71d3810ff9SJared McNeill #include <dev/extres/regulator/regulator.h> 72d3810ff9SJared McNeill 73d3810ff9SJared McNeill #include "miibus_if.h" 7401a469b8SJared McNeill #include "gpio_if.h" 75d3810ff9SJared McNeill 7601a469b8SJared McNeill #define RD4(sc, reg) bus_read_4((sc)->res[_RES_EMAC], (reg)) 7701a469b8SJared McNeill #define WR4(sc, reg, val) bus_write_4((sc)->res[_RES_EMAC], (reg), (val)) 78d3810ff9SJared McNeill 79d3810ff9SJared McNeill #define AWG_LOCK(sc) mtx_lock(&(sc)->mtx) 80d3810ff9SJared McNeill #define AWG_UNLOCK(sc) mtx_unlock(&(sc)->mtx); 81d3810ff9SJared McNeill #define AWG_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 82d3810ff9SJared McNeill #define AWG_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 83d3810ff9SJared McNeill 84d3810ff9SJared McNeill #define DESC_ALIGN 4 8516928528SJared McNeill #define TX_DESC_COUNT 1024 86d3810ff9SJared McNeill #define TX_DESC_SIZE (sizeof(struct emac_desc) * TX_DESC_COUNT) 87d3810ff9SJared McNeill #define RX_DESC_COUNT 256 88d3810ff9SJared McNeill #define RX_DESC_SIZE (sizeof(struct emac_desc) * RX_DESC_COUNT) 89d3810ff9SJared McNeill 90d3810ff9SJared McNeill #define DESC_OFF(n) ((n) * sizeof(struct emac_desc)) 91d3810ff9SJared McNeill #define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1)) 92d3810ff9SJared McNeill #define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1)) 93d3810ff9SJared McNeill #define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1)) 94d3810ff9SJared McNeill 95031d5777SOleksandr Tymoshenko #define TX_MAX_SEGS 20 96d3810ff9SJared McNeill 97d3810ff9SJared McNeill #define SOFT_RST_RETRY 1000 98d3810ff9SJared McNeill #define MII_BUSY_RETRY 1000 99d3810ff9SJared McNeill #define MDIO_FREQ 2500000 100d3810ff9SJared McNeill 101d3810ff9SJared McNeill #define BURST_LEN_DEFAULT 8 102d3810ff9SJared McNeill #define RX_TX_PRI_DEFAULT 0 103d3810ff9SJared McNeill #define PAUSE_TIME_DEFAULT 0x400 104d3810ff9SJared McNeill #define TX_INTERVAL_DEFAULT 64 10516928528SJared McNeill #define RX_BATCH_DEFAULT 64 106d3810ff9SJared McNeill 10701a469b8SJared McNeill /* syscon EMAC clock register */ 10801a469b8SJared McNeill #define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */ 10901a469b8SJared McNeill #define EMAC_CLK_EPHY_ADDR_SHIFT 20 11001a469b8SJared McNeill #define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */ 11101a469b8SJared McNeill #define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */ 11201a469b8SJared McNeill #define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */ 11301a469b8SJared McNeill #define EMAC_CLK_RMII_EN (1 << 13) 11401a469b8SJared McNeill #define EMAC_CLK_ETXDC (0x7 << 10) 11501a469b8SJared McNeill #define EMAC_CLK_ETXDC_SHIFT 10 11601a469b8SJared McNeill #define EMAC_CLK_ERXDC (0x1f << 5) 11701a469b8SJared McNeill #define EMAC_CLK_ERXDC_SHIFT 5 11801a469b8SJared McNeill #define EMAC_CLK_PIT (0x1 << 2) 11901a469b8SJared McNeill #define EMAC_CLK_PIT_MII (0 << 2) 12001a469b8SJared McNeill #define EMAC_CLK_PIT_RGMII (1 << 2) 12101a469b8SJared McNeill #define EMAC_CLK_SRC (0x3 << 0) 12201a469b8SJared McNeill #define EMAC_CLK_SRC_MII (0 << 0) 12301a469b8SJared McNeill #define EMAC_CLK_SRC_EXT_RGMII (1 << 0) 12401a469b8SJared McNeill #define EMAC_CLK_SRC_RGMII (2 << 0) 12501a469b8SJared McNeill 126d3810ff9SJared McNeill /* Burst length of RX and TX DMA transfers */ 127d3810ff9SJared McNeill static int awg_burst_len = BURST_LEN_DEFAULT; 128d3810ff9SJared McNeill TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); 129d3810ff9SJared McNeill 130d3810ff9SJared McNeill /* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */ 131d3810ff9SJared McNeill static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT; 132d3810ff9SJared McNeill TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri); 133d3810ff9SJared McNeill 134d3810ff9SJared McNeill /* Pause time field in the transmitted control frame */ 135d3810ff9SJared McNeill static int awg_pause_time = PAUSE_TIME_DEFAULT; 136d3810ff9SJared McNeill TUNABLE_INT("hw.awg.pause_time", &awg_pause_time); 137d3810ff9SJared McNeill 138d3810ff9SJared McNeill /* Request a TX interrupt every <n> descriptors */ 139d3810ff9SJared McNeill static int awg_tx_interval = TX_INTERVAL_DEFAULT; 140d3810ff9SJared McNeill TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval); 141d3810ff9SJared McNeill 14216928528SJared McNeill /* Maximum number of mbufs to send to if_input */ 14316928528SJared McNeill static int awg_rx_batch = RX_BATCH_DEFAULT; 14416928528SJared McNeill TUNABLE_INT("hw.awg.rx_batch", &awg_rx_batch); 14516928528SJared McNeill 14601a469b8SJared McNeill enum awg_type { 14701a469b8SJared McNeill EMAC_A83T = 1, 14801a469b8SJared McNeill EMAC_H3, 14950bb2d50SEmmanuel Vadot EMAC_A64, 15001a469b8SJared McNeill }; 15101a469b8SJared McNeill 152d3810ff9SJared McNeill static struct ofw_compat_data compat_data[] = { 15301a469b8SJared McNeill { "allwinner,sun8i-a83t-emac", EMAC_A83T }, 15401a469b8SJared McNeill { "allwinner,sun8i-h3-emac", EMAC_H3 }, 15550bb2d50SEmmanuel Vadot { "allwinner,sun50i-a64-emac", EMAC_A64 }, 156d3810ff9SJared McNeill { NULL, 0 } 157d3810ff9SJared McNeill }; 158d3810ff9SJared McNeill 159d3810ff9SJared McNeill struct awg_bufmap { 160d3810ff9SJared McNeill bus_dmamap_t map; 161d3810ff9SJared McNeill struct mbuf *mbuf; 162d3810ff9SJared McNeill }; 163d3810ff9SJared McNeill 164d3810ff9SJared McNeill struct awg_txring { 165d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 166d3810ff9SJared McNeill bus_dmamap_t desc_map; 167d3810ff9SJared McNeill struct emac_desc *desc_ring; 168d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 169d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 170d3810ff9SJared McNeill struct awg_bufmap buf_map[TX_DESC_COUNT]; 171d3810ff9SJared McNeill u_int cur, next, queued; 1721ee5a3d3SEmmanuel Vadot u_int segs; 173d3810ff9SJared McNeill }; 174d3810ff9SJared McNeill 175d3810ff9SJared McNeill struct awg_rxring { 176d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 177d3810ff9SJared McNeill bus_dmamap_t desc_map; 178d3810ff9SJared McNeill struct emac_desc *desc_ring; 179d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 180d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 181d3810ff9SJared McNeill struct awg_bufmap buf_map[RX_DESC_COUNT]; 182*bd906329SEmmanuel Vadot bus_dmamap_t buf_spare_map; 183d3810ff9SJared McNeill u_int cur; 184d3810ff9SJared McNeill }; 185d3810ff9SJared McNeill 18601a469b8SJared McNeill enum { 18701a469b8SJared McNeill _RES_EMAC, 18801a469b8SJared McNeill _RES_IRQ, 18901a469b8SJared McNeill _RES_SYSCON, 19001a469b8SJared McNeill _RES_NITEMS 19101a469b8SJared McNeill }; 19201a469b8SJared McNeill 193d3810ff9SJared McNeill struct awg_softc { 19401a469b8SJared McNeill struct resource *res[_RES_NITEMS]; 195d3810ff9SJared McNeill struct mtx mtx; 196d3810ff9SJared McNeill if_t ifp; 197031d5777SOleksandr Tymoshenko device_t dev; 198d3810ff9SJared McNeill device_t miibus; 199d3810ff9SJared McNeill struct callout stat_ch; 200d3810ff9SJared McNeill struct task link_task; 201d3810ff9SJared McNeill void *ih; 202d3810ff9SJared McNeill u_int mdc_div_ratio_m; 203d3810ff9SJared McNeill int link; 204d3810ff9SJared McNeill int if_flags; 20501a469b8SJared McNeill enum awg_type type; 206d3810ff9SJared McNeill 207d3810ff9SJared McNeill struct awg_txring tx; 208d3810ff9SJared McNeill struct awg_rxring rx; 209d3810ff9SJared McNeill }; 210d3810ff9SJared McNeill 211d3810ff9SJared McNeill static struct resource_spec awg_spec[] = { 212d3810ff9SJared McNeill { SYS_RES_MEMORY, 0, RF_ACTIVE }, 213d3810ff9SJared McNeill { SYS_RES_IRQ, 0, RF_ACTIVE }, 21401a469b8SJared McNeill { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, 215d3810ff9SJared McNeill { -1, 0 } 216d3810ff9SJared McNeill }; 217d3810ff9SJared McNeill 218d3810ff9SJared McNeill static int 219d3810ff9SJared McNeill awg_miibus_readreg(device_t dev, int phy, int reg) 220d3810ff9SJared McNeill { 221d3810ff9SJared McNeill struct awg_softc *sc; 222d3810ff9SJared McNeill int retry, val; 223d3810ff9SJared McNeill 224d3810ff9SJared McNeill sc = device_get_softc(dev); 225d3810ff9SJared McNeill val = 0; 226d3810ff9SJared McNeill 227d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 228d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 229d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 230d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 231d3810ff9SJared McNeill MII_BUSY); 232d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 233d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) { 234d3810ff9SJared McNeill val = RD4(sc, EMAC_MII_DATA); 235d3810ff9SJared McNeill break; 236d3810ff9SJared McNeill } 237d3810ff9SJared McNeill DELAY(10); 238d3810ff9SJared McNeill } 239d3810ff9SJared McNeill 240d3810ff9SJared McNeill if (retry == 0) 241d3810ff9SJared McNeill device_printf(dev, "phy read timeout, phy=%d reg=%d\n", 242d3810ff9SJared McNeill phy, reg); 243d3810ff9SJared McNeill 244d3810ff9SJared McNeill return (val); 245d3810ff9SJared McNeill } 246d3810ff9SJared McNeill 247d3810ff9SJared McNeill static int 248d3810ff9SJared McNeill awg_miibus_writereg(device_t dev, int phy, int reg, int val) 249d3810ff9SJared McNeill { 250d3810ff9SJared McNeill struct awg_softc *sc; 251d3810ff9SJared McNeill int retry; 252d3810ff9SJared McNeill 253d3810ff9SJared McNeill sc = device_get_softc(dev); 254d3810ff9SJared McNeill 255d3810ff9SJared McNeill WR4(sc, EMAC_MII_DATA, val); 256d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 257d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 258d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 259d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 260d3810ff9SJared McNeill MII_WR | MII_BUSY); 261d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 262d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) 263d3810ff9SJared McNeill break; 264d3810ff9SJared McNeill DELAY(10); 265d3810ff9SJared McNeill } 266d3810ff9SJared McNeill 267d3810ff9SJared McNeill if (retry == 0) 268d3810ff9SJared McNeill device_printf(dev, "phy write timeout, phy=%d reg=%d\n", 269d3810ff9SJared McNeill phy, reg); 270d3810ff9SJared McNeill 271d3810ff9SJared McNeill return (0); 272d3810ff9SJared McNeill } 273d3810ff9SJared McNeill 274d3810ff9SJared McNeill static void 275d3810ff9SJared McNeill awg_update_link_locked(struct awg_softc *sc) 276d3810ff9SJared McNeill { 277d3810ff9SJared McNeill struct mii_data *mii; 278d3810ff9SJared McNeill uint32_t val; 279d3810ff9SJared McNeill 280d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 281d3810ff9SJared McNeill 282d3810ff9SJared McNeill if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0) 283d3810ff9SJared McNeill return; 284d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 285d3810ff9SJared McNeill 286d3810ff9SJared McNeill if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 287d3810ff9SJared McNeill (IFM_ACTIVE | IFM_AVALID)) { 288d3810ff9SJared McNeill switch (IFM_SUBTYPE(mii->mii_media_active)) { 289d3810ff9SJared McNeill case IFM_1000_T: 290d3810ff9SJared McNeill case IFM_1000_SX: 291d3810ff9SJared McNeill case IFM_100_TX: 292d3810ff9SJared McNeill case IFM_10_T: 293d3810ff9SJared McNeill sc->link = 1; 294d3810ff9SJared McNeill break; 295d3810ff9SJared McNeill default: 296d3810ff9SJared McNeill sc->link = 0; 297d3810ff9SJared McNeill break; 298d3810ff9SJared McNeill } 299d3810ff9SJared McNeill } else 300d3810ff9SJared McNeill sc->link = 0; 301d3810ff9SJared McNeill 302d3810ff9SJared McNeill if (sc->link == 0) 303d3810ff9SJared McNeill return; 304d3810ff9SJared McNeill 305d3810ff9SJared McNeill val = RD4(sc, EMAC_BASIC_CTL_0); 306d3810ff9SJared McNeill val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX); 307d3810ff9SJared McNeill 308d3810ff9SJared McNeill if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || 309d3810ff9SJared McNeill IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) 310d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT; 311d3810ff9SJared McNeill else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 312d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT; 313d3810ff9SJared McNeill else 314d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT; 315d3810ff9SJared McNeill 316d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 317d3810ff9SJared McNeill val |= BASIC_CTL_DUPLEX; 318d3810ff9SJared McNeill 319d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_0, val); 320d3810ff9SJared McNeill 321d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 322d3810ff9SJared McNeill val &= ~RX_FLOW_CTL_EN; 323d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 324d3810ff9SJared McNeill val |= RX_FLOW_CTL_EN; 325d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val); 326d3810ff9SJared McNeill 327d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_FLOW_CTL); 328d3810ff9SJared McNeill val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN); 329d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 330d3810ff9SJared McNeill val |= TX_FLOW_CTL_EN; 331d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 332d3810ff9SJared McNeill val |= awg_pause_time << PAUSE_TIME_SHIFT; 333d3810ff9SJared McNeill WR4(sc, EMAC_TX_FLOW_CTL, val); 334d3810ff9SJared McNeill } 335d3810ff9SJared McNeill 336d3810ff9SJared McNeill static void 337d3810ff9SJared McNeill awg_link_task(void *arg, int pending) 338d3810ff9SJared McNeill { 339d3810ff9SJared McNeill struct awg_softc *sc; 340d3810ff9SJared McNeill 341d3810ff9SJared McNeill sc = arg; 342d3810ff9SJared McNeill 343d3810ff9SJared McNeill AWG_LOCK(sc); 344d3810ff9SJared McNeill awg_update_link_locked(sc); 345d3810ff9SJared McNeill AWG_UNLOCK(sc); 346d3810ff9SJared McNeill } 347d3810ff9SJared McNeill 348d3810ff9SJared McNeill static void 349d3810ff9SJared McNeill awg_miibus_statchg(device_t dev) 350d3810ff9SJared McNeill { 351d3810ff9SJared McNeill struct awg_softc *sc; 352d3810ff9SJared McNeill 353d3810ff9SJared McNeill sc = device_get_softc(dev); 354d3810ff9SJared McNeill 355d3810ff9SJared McNeill taskqueue_enqueue(taskqueue_swi, &sc->link_task); 356d3810ff9SJared McNeill } 357d3810ff9SJared McNeill 358d3810ff9SJared McNeill static void 359d3810ff9SJared McNeill awg_media_status(if_t ifp, struct ifmediareq *ifmr) 360d3810ff9SJared McNeill { 361d3810ff9SJared McNeill struct awg_softc *sc; 362d3810ff9SJared McNeill struct mii_data *mii; 363d3810ff9SJared McNeill 364d3810ff9SJared McNeill sc = if_getsoftc(ifp); 365d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 366d3810ff9SJared McNeill 367d3810ff9SJared McNeill AWG_LOCK(sc); 368d3810ff9SJared McNeill mii_pollstat(mii); 369d3810ff9SJared McNeill ifmr->ifm_active = mii->mii_media_active; 370d3810ff9SJared McNeill ifmr->ifm_status = mii->mii_media_status; 371d3810ff9SJared McNeill AWG_UNLOCK(sc); 372d3810ff9SJared McNeill } 373d3810ff9SJared McNeill 374d3810ff9SJared McNeill static int 375d3810ff9SJared McNeill awg_media_change(if_t ifp) 376d3810ff9SJared McNeill { 377d3810ff9SJared McNeill struct awg_softc *sc; 378d3810ff9SJared McNeill struct mii_data *mii; 379d3810ff9SJared McNeill int error; 380d3810ff9SJared McNeill 381d3810ff9SJared McNeill sc = if_getsoftc(ifp); 382d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 383d3810ff9SJared McNeill 384d3810ff9SJared McNeill AWG_LOCK(sc); 385d3810ff9SJared McNeill error = mii_mediachg(mii); 386d3810ff9SJared McNeill AWG_UNLOCK(sc); 387d3810ff9SJared McNeill 388d3810ff9SJared McNeill return (error); 389d3810ff9SJared McNeill } 390d3810ff9SJared McNeill 391d3810ff9SJared McNeill static int 392337c6940SEmmanuel Vadot awg_encap(struct awg_softc *sc, struct mbuf **mp) 393d3810ff9SJared McNeill { 394fce9d29fSEmmanuel Vadot bus_dmamap_t map; 395d3810ff9SJared McNeill bus_dma_segment_t segs[TX_MAX_SEGS]; 396fce9d29fSEmmanuel Vadot int error, nsegs, cur, first, last, i; 397d3810ff9SJared McNeill u_int csum_flags; 398c6110e75SEmmanuel Vadot uint32_t flags, status; 399d3810ff9SJared McNeill struct mbuf *m; 400d3810ff9SJared McNeill 401337c6940SEmmanuel Vadot cur = first = sc->tx.cur; 402fce9d29fSEmmanuel Vadot map = sc->tx.buf_map[first].map; 403c6110e75SEmmanuel Vadot 404d3810ff9SJared McNeill m = *mp; 405fce9d29fSEmmanuel Vadot error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs, 406fce9d29fSEmmanuel Vadot &nsegs, BUS_DMA_NOWAIT); 407d3810ff9SJared McNeill if (error == EFBIG) { 408d3810ff9SJared McNeill m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); 409031d5777SOleksandr Tymoshenko if (m == NULL) { 410337c6940SEmmanuel Vadot device_printf(sc->dev, "awg_encap: m_collapse failed\n"); 411337c6940SEmmanuel Vadot m_freem(*mp); 412337c6940SEmmanuel Vadot *mp = NULL; 413337c6940SEmmanuel Vadot return (ENOMEM); 414031d5777SOleksandr Tymoshenko } 415d3810ff9SJared McNeill *mp = m; 416fce9d29fSEmmanuel Vadot error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, 417fce9d29fSEmmanuel Vadot segs, &nsegs, BUS_DMA_NOWAIT); 418337c6940SEmmanuel Vadot if (error != 0) { 419337c6940SEmmanuel Vadot m_freem(*mp); 420337c6940SEmmanuel Vadot *mp = NULL; 421337c6940SEmmanuel Vadot } 422d3810ff9SJared McNeill } 423031d5777SOleksandr Tymoshenko if (error != 0) { 424337c6940SEmmanuel Vadot device_printf(sc->dev, "awg_encap: bus_dmamap_load_mbuf_sg failed\n"); 425337c6940SEmmanuel Vadot return (error); 426337c6940SEmmanuel Vadot } 427337c6940SEmmanuel Vadot if (nsegs == 0) { 428337c6940SEmmanuel Vadot m_freem(*mp); 429337c6940SEmmanuel Vadot *mp = NULL; 430337c6940SEmmanuel Vadot return (EIO); 431337c6940SEmmanuel Vadot } 432337c6940SEmmanuel Vadot 433337c6940SEmmanuel Vadot if (sc->tx.queued + nsegs > TX_DESC_COUNT) { 434337c6940SEmmanuel Vadot bus_dmamap_unload(sc->tx.buf_tag, map); 435337c6940SEmmanuel Vadot return (ENOBUFS); 436031d5777SOleksandr Tymoshenko } 437d3810ff9SJared McNeill 438fce9d29fSEmmanuel Vadot bus_dmamap_sync(sc->tx.buf_tag, map, BUS_DMASYNC_PREWRITE); 439d3810ff9SJared McNeill 440d3810ff9SJared McNeill flags = TX_FIR_DESC; 441c6110e75SEmmanuel Vadot status = 0; 442d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 443d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) 444d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_FULL; 445d3810ff9SJared McNeill else 446d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_IP; 447d3810ff9SJared McNeill flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); 448d3810ff9SJared McNeill } 449d3810ff9SJared McNeill 450c6110e75SEmmanuel Vadot for (i = 0; i < nsegs; i++) { 4511ee5a3d3SEmmanuel Vadot sc->tx.segs++; 4521ee5a3d3SEmmanuel Vadot if (i == nsegs - 1) { 453d3810ff9SJared McNeill flags |= TX_LAST_DESC; 4541ee5a3d3SEmmanuel Vadot /* 4551ee5a3d3SEmmanuel Vadot * Can only request TX completion 4561ee5a3d3SEmmanuel Vadot * interrupt on last descriptor. 4571ee5a3d3SEmmanuel Vadot */ 4581ee5a3d3SEmmanuel Vadot if (sc->tx.segs >= awg_tx_interval) { 4591ee5a3d3SEmmanuel Vadot sc->tx.segs = 0; 4601ee5a3d3SEmmanuel Vadot flags |= TX_INT_CTL; 4611ee5a3d3SEmmanuel Vadot } 4621ee5a3d3SEmmanuel Vadot } 463c6110e75SEmmanuel Vadot 464c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr); 465c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len); 466c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].status = htole32(status); 467c6110e75SEmmanuel Vadot 468d3810ff9SJared McNeill flags &= ~TX_FIR_DESC; 469c6110e75SEmmanuel Vadot /* 470c6110e75SEmmanuel Vadot * Setting of the valid bit in the first descriptor is 471c6110e75SEmmanuel Vadot * deferred until the whole chain is fully set up. 472c6110e75SEmmanuel Vadot */ 473c6110e75SEmmanuel Vadot status = TX_DESC_CTL; 474c6110e75SEmmanuel Vadot 475c6110e75SEmmanuel Vadot ++sc->tx.queued; 476d3810ff9SJared McNeill cur = TX_NEXT(cur); 477d3810ff9SJared McNeill } 478d3810ff9SJared McNeill 479337c6940SEmmanuel Vadot sc->tx.cur = cur; 480337c6940SEmmanuel Vadot 481fce9d29fSEmmanuel Vadot /* Store mapping and mbuf in the last segment */ 482fce9d29fSEmmanuel Vadot last = TX_SKIP(cur, TX_DESC_COUNT - 1); 483fce9d29fSEmmanuel Vadot sc->tx.buf_map[first].map = sc->tx.buf_map[last].map; 484fce9d29fSEmmanuel Vadot sc->tx.buf_map[last].map = map; 485fce9d29fSEmmanuel Vadot sc->tx.buf_map[last].mbuf = m; 486c6110e75SEmmanuel Vadot 487c6110e75SEmmanuel Vadot /* 488c6110e75SEmmanuel Vadot * The whole mbuf chain has been DMA mapped, 489c6110e75SEmmanuel Vadot * fix the first descriptor. 490c6110e75SEmmanuel Vadot */ 491c6110e75SEmmanuel Vadot sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL); 492c6110e75SEmmanuel Vadot 493337c6940SEmmanuel Vadot return (0); 494d3810ff9SJared McNeill } 495d3810ff9SJared McNeill 496d3810ff9SJared McNeill static void 497c6110e75SEmmanuel Vadot awg_clean_txbuf(struct awg_softc *sc, int index) 498c6110e75SEmmanuel Vadot { 499c6110e75SEmmanuel Vadot struct awg_bufmap *bmap; 500c6110e75SEmmanuel Vadot 501c6110e75SEmmanuel Vadot --sc->tx.queued; 502c6110e75SEmmanuel Vadot 503c6110e75SEmmanuel Vadot bmap = &sc->tx.buf_map[index]; 504c6110e75SEmmanuel Vadot if (bmap->mbuf != NULL) { 505c6110e75SEmmanuel Vadot bus_dmamap_sync(sc->tx.buf_tag, bmap->map, 506c6110e75SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 507c6110e75SEmmanuel Vadot bus_dmamap_unload(sc->tx.buf_tag, bmap->map); 508c6110e75SEmmanuel Vadot m_freem(bmap->mbuf); 509c6110e75SEmmanuel Vadot bmap->mbuf = NULL; 510c6110e75SEmmanuel Vadot } 511c6110e75SEmmanuel Vadot } 512c6110e75SEmmanuel Vadot 513c6110e75SEmmanuel Vadot static void 514d3810ff9SJared McNeill awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr) 515d3810ff9SJared McNeill { 516d3810ff9SJared McNeill uint32_t status, size; 517d3810ff9SJared McNeill 518d3810ff9SJared McNeill status = RX_DESC_CTL; 519d3810ff9SJared McNeill size = MCLBYTES - 1; 520d3810ff9SJared McNeill 521d3810ff9SJared McNeill sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr); 522d3810ff9SJared McNeill sc->rx.desc_ring[index].size = htole32(size); 523d3810ff9SJared McNeill sc->rx.desc_ring[index].status = htole32(status); 524d3810ff9SJared McNeill } 525d3810ff9SJared McNeill 526*bd906329SEmmanuel Vadot static void 527*bd906329SEmmanuel Vadot awg_reuse_rxdesc(struct awg_softc *sc, int index) 528d3810ff9SJared McNeill { 529d3810ff9SJared McNeill 530*bd906329SEmmanuel Vadot sc->rx.desc_ring[index].status = htole32(RX_DESC_CTL); 531*bd906329SEmmanuel Vadot } 532*bd906329SEmmanuel Vadot 533*bd906329SEmmanuel Vadot static int 534*bd906329SEmmanuel Vadot awg_newbuf_rx(struct awg_softc *sc, int index) 535*bd906329SEmmanuel Vadot { 536*bd906329SEmmanuel Vadot struct mbuf *m; 537*bd906329SEmmanuel Vadot bus_dma_segment_t seg; 538*bd906329SEmmanuel Vadot bus_dmamap_t map; 539*bd906329SEmmanuel Vadot int nsegs; 540*bd906329SEmmanuel Vadot 541*bd906329SEmmanuel Vadot m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 542*bd906329SEmmanuel Vadot if (m == NULL) 543*bd906329SEmmanuel Vadot return (ENOBUFS); 544*bd906329SEmmanuel Vadot 545*bd906329SEmmanuel Vadot m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 546d3810ff9SJared McNeill m_adj(m, ETHER_ALIGN); 547d3810ff9SJared McNeill 548*bd906329SEmmanuel Vadot if (bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, sc->rx.buf_spare_map, 549*bd906329SEmmanuel Vadot m, &seg, &nsegs, BUS_DMA_NOWAIT) != 0) { 550*bd906329SEmmanuel Vadot m_freem(m); 551*bd906329SEmmanuel Vadot return (ENOBUFS); 552*bd906329SEmmanuel Vadot } 553d3810ff9SJared McNeill 554*bd906329SEmmanuel Vadot if (sc->rx.buf_map[index].mbuf != NULL) { 555*bd906329SEmmanuel Vadot bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 556*bd906329SEmmanuel Vadot BUS_DMASYNC_POSTREAD); 557*bd906329SEmmanuel Vadot bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map); 558*bd906329SEmmanuel Vadot } 559*bd906329SEmmanuel Vadot map = sc->rx.buf_map[index].map; 560*bd906329SEmmanuel Vadot sc->rx.buf_map[index].map = sc->rx.buf_spare_map; 561*bd906329SEmmanuel Vadot sc->rx.buf_spare_map = map; 562d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 563d3810ff9SJared McNeill BUS_DMASYNC_PREREAD); 564d3810ff9SJared McNeill 565d3810ff9SJared McNeill sc->rx.buf_map[index].mbuf = m; 566d3810ff9SJared McNeill awg_setup_rxdesc(sc, index, seg.ds_addr); 567d3810ff9SJared McNeill 568d3810ff9SJared McNeill return (0); 569d3810ff9SJared McNeill } 570d3810ff9SJared McNeill 571d3810ff9SJared McNeill static void 572d3810ff9SJared McNeill awg_start_locked(struct awg_softc *sc) 573d3810ff9SJared McNeill { 574d3810ff9SJared McNeill struct mbuf *m; 575d3810ff9SJared McNeill uint32_t val; 576d3810ff9SJared McNeill if_t ifp; 577337c6940SEmmanuel Vadot int cnt, err; 578d3810ff9SJared McNeill 579d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 580d3810ff9SJared McNeill 581d3810ff9SJared McNeill if (!sc->link) 582d3810ff9SJared McNeill return; 583d3810ff9SJared McNeill 584d3810ff9SJared McNeill ifp = sc->ifp; 585d3810ff9SJared McNeill 586d3810ff9SJared McNeill if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 587d3810ff9SJared McNeill IFF_DRV_RUNNING) 588d3810ff9SJared McNeill return; 589d3810ff9SJared McNeill 590d3810ff9SJared McNeill for (cnt = 0; ; cnt++) { 591d3810ff9SJared McNeill m = if_dequeue(ifp); 592d3810ff9SJared McNeill if (m == NULL) 593d3810ff9SJared McNeill break; 594d3810ff9SJared McNeill 595337c6940SEmmanuel Vadot err = awg_encap(sc, &m); 596337c6940SEmmanuel Vadot if (err != 0) { 597337c6940SEmmanuel Vadot if (err == ENOBUFS) 598337c6940SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 599337c6940SEmmanuel Vadot if (m != NULL) 600d3810ff9SJared McNeill if_sendq_prepend(ifp, m); 601d3810ff9SJared McNeill break; 602d3810ff9SJared McNeill } 603d3810ff9SJared McNeill if_bpfmtap(ifp, m); 604d3810ff9SJared McNeill } 605d3810ff9SJared McNeill 606d3810ff9SJared McNeill if (cnt != 0) { 607d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 608d3810ff9SJared McNeill BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 609d3810ff9SJared McNeill 610d3810ff9SJared McNeill /* Start and run TX DMA */ 611d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 612d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START); 613d3810ff9SJared McNeill } 614d3810ff9SJared McNeill } 615d3810ff9SJared McNeill 616d3810ff9SJared McNeill static void 617d3810ff9SJared McNeill awg_start(if_t ifp) 618d3810ff9SJared McNeill { 619d3810ff9SJared McNeill struct awg_softc *sc; 620d3810ff9SJared McNeill 621d3810ff9SJared McNeill sc = if_getsoftc(ifp); 622d3810ff9SJared McNeill 623d3810ff9SJared McNeill AWG_LOCK(sc); 624d3810ff9SJared McNeill awg_start_locked(sc); 625d3810ff9SJared McNeill AWG_UNLOCK(sc); 626d3810ff9SJared McNeill } 627d3810ff9SJared McNeill 628d3810ff9SJared McNeill static void 629d3810ff9SJared McNeill awg_tick(void *softc) 630d3810ff9SJared McNeill { 631d3810ff9SJared McNeill struct awg_softc *sc; 632d3810ff9SJared McNeill struct mii_data *mii; 633d3810ff9SJared McNeill if_t ifp; 634d3810ff9SJared McNeill int link; 635d3810ff9SJared McNeill 636d3810ff9SJared McNeill sc = softc; 637d3810ff9SJared McNeill ifp = sc->ifp; 638d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 639d3810ff9SJared McNeill 640d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 641d3810ff9SJared McNeill 642d3810ff9SJared McNeill if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 643d3810ff9SJared McNeill return; 644d3810ff9SJared McNeill 645d3810ff9SJared McNeill link = sc->link; 646d3810ff9SJared McNeill mii_tick(mii); 647d3810ff9SJared McNeill if (sc->link && !link) 648d3810ff9SJared McNeill awg_start_locked(sc); 649d3810ff9SJared McNeill 650d3810ff9SJared McNeill callout_reset(&sc->stat_ch, hz, awg_tick, sc); 651d3810ff9SJared McNeill } 652d3810ff9SJared McNeill 653d3810ff9SJared McNeill /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */ 654d3810ff9SJared McNeill static uint32_t 655d3810ff9SJared McNeill bitrev32(uint32_t x) 656d3810ff9SJared McNeill { 657d3810ff9SJared McNeill x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); 658d3810ff9SJared McNeill x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); 659d3810ff9SJared McNeill x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); 660d3810ff9SJared McNeill x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); 661d3810ff9SJared McNeill 662d3810ff9SJared McNeill return (x >> 16) | (x << 16); 663d3810ff9SJared McNeill } 664d3810ff9SJared McNeill 665d3810ff9SJared McNeill static void 666d3810ff9SJared McNeill awg_setup_rxfilter(struct awg_softc *sc) 667d3810ff9SJared McNeill { 668d3810ff9SJared McNeill uint32_t val, crc, hashreg, hashbit, hash[2], machi, maclo; 669d3810ff9SJared McNeill int mc_count, mcnt, i; 670d3810ff9SJared McNeill uint8_t *eaddr, *mta; 671d3810ff9SJared McNeill if_t ifp; 672d3810ff9SJared McNeill 673d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 674d3810ff9SJared McNeill 675d3810ff9SJared McNeill ifp = sc->ifp; 676d3810ff9SJared McNeill val = 0; 677d3810ff9SJared McNeill hash[0] = hash[1] = 0; 678d3810ff9SJared McNeill 679d3810ff9SJared McNeill mc_count = if_multiaddr_count(ifp, -1); 680d3810ff9SJared McNeill 681d3810ff9SJared McNeill if (if_getflags(ifp) & IFF_PROMISC) 682d3810ff9SJared McNeill val |= DIS_ADDR_FILTER; 683d3810ff9SJared McNeill else if (if_getflags(ifp) & IFF_ALLMULTI) { 684d3810ff9SJared McNeill val |= RX_ALL_MULTICAST; 685d3810ff9SJared McNeill hash[0] = hash[1] = ~0; 686d3810ff9SJared McNeill } else if (mc_count > 0) { 687d3810ff9SJared McNeill val |= HASH_MULTICAST; 688d3810ff9SJared McNeill 689d3810ff9SJared McNeill mta = malloc(sizeof(unsigned char) * ETHER_ADDR_LEN * mc_count, 690d3810ff9SJared McNeill M_DEVBUF, M_NOWAIT); 691d3810ff9SJared McNeill if (mta == NULL) { 692d3810ff9SJared McNeill if_printf(ifp, 693d3810ff9SJared McNeill "failed to allocate temporary multicast list\n"); 694d3810ff9SJared McNeill return; 695d3810ff9SJared McNeill } 696d3810ff9SJared McNeill 697d3810ff9SJared McNeill if_multiaddr_array(ifp, mta, &mcnt, mc_count); 698d3810ff9SJared McNeill for (i = 0; i < mcnt; i++) { 699d3810ff9SJared McNeill crc = ether_crc32_le(mta + (i * ETHER_ADDR_LEN), 700d3810ff9SJared McNeill ETHER_ADDR_LEN) & 0x7f; 701d3810ff9SJared McNeill crc = bitrev32(~crc) >> 26; 702d3810ff9SJared McNeill hashreg = (crc >> 5); 703d3810ff9SJared McNeill hashbit = (crc & 0x1f); 704d3810ff9SJared McNeill hash[hashreg] |= (1 << hashbit); 705d3810ff9SJared McNeill } 706d3810ff9SJared McNeill 707d3810ff9SJared McNeill free(mta, M_DEVBUF); 708d3810ff9SJared McNeill } 709d3810ff9SJared McNeill 710d3810ff9SJared McNeill /* Write our unicast address */ 711d3810ff9SJared McNeill eaddr = IF_LLADDR(ifp); 712d3810ff9SJared McNeill machi = (eaddr[5] << 8) | eaddr[4]; 713d3810ff9SJared McNeill maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | 714d3810ff9SJared McNeill (eaddr[0] << 0); 715d3810ff9SJared McNeill WR4(sc, EMAC_ADDR_HIGH(0), machi); 716d3810ff9SJared McNeill WR4(sc, EMAC_ADDR_LOW(0), maclo); 717d3810ff9SJared McNeill 718d3810ff9SJared McNeill /* Multicast hash filters */ 719d3810ff9SJared McNeill WR4(sc, EMAC_RX_HASH_0, hash[1]); 720d3810ff9SJared McNeill WR4(sc, EMAC_RX_HASH_1, hash[0]); 721d3810ff9SJared McNeill 722d3810ff9SJared McNeill /* RX frame filter config */ 723d3810ff9SJared McNeill WR4(sc, EMAC_RX_FRM_FLT, val); 724d3810ff9SJared McNeill } 725d3810ff9SJared McNeill 726d3810ff9SJared McNeill static void 72716928528SJared McNeill awg_enable_intr(struct awg_softc *sc) 72816928528SJared McNeill { 72916928528SJared McNeill /* Enable interrupts */ 73016928528SJared McNeill WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN); 73116928528SJared McNeill } 73216928528SJared McNeill 73316928528SJared McNeill static void 73416928528SJared McNeill awg_disable_intr(struct awg_softc *sc) 73516928528SJared McNeill { 73616928528SJared McNeill /* Disable interrupts */ 73716928528SJared McNeill WR4(sc, EMAC_INT_EN, 0); 73816928528SJared McNeill } 73916928528SJared McNeill 74016928528SJared McNeill static void 741d3810ff9SJared McNeill awg_init_locked(struct awg_softc *sc) 742d3810ff9SJared McNeill { 743d3810ff9SJared McNeill struct mii_data *mii; 744d3810ff9SJared McNeill uint32_t val; 745d3810ff9SJared McNeill if_t ifp; 746d3810ff9SJared McNeill 747d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 748d3810ff9SJared McNeill ifp = sc->ifp; 749d3810ff9SJared McNeill 750d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 751d3810ff9SJared McNeill 752d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 753d3810ff9SJared McNeill return; 754d3810ff9SJared McNeill 755d3810ff9SJared McNeill awg_setup_rxfilter(sc); 756d3810ff9SJared McNeill 757d3810ff9SJared McNeill /* Configure DMA burst length and priorities */ 758d3810ff9SJared McNeill val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT; 759d3810ff9SJared McNeill if (awg_rx_tx_pri) 760d3810ff9SJared McNeill val |= BASIC_CTL_RX_TX_PRI; 761d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_1, val); 762d3810ff9SJared McNeill 763d3810ff9SJared McNeill /* Enable interrupts */ 76416928528SJared McNeill #ifdef DEVICE_POLLING 76516928528SJared McNeill if ((if_getcapenable(ifp) & IFCAP_POLLING) == 0) 76616928528SJared McNeill awg_enable_intr(sc); 76716928528SJared McNeill else 76816928528SJared McNeill awg_disable_intr(sc); 76916928528SJared McNeill #else 77016928528SJared McNeill awg_enable_intr(sc); 77116928528SJared McNeill #endif 772d3810ff9SJared McNeill 773d3810ff9SJared McNeill /* Enable transmit DMA */ 774d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 77516928528SJared McNeill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD | TX_NEXT_FRAME); 776d3810ff9SJared McNeill 777d3810ff9SJared McNeill /* Enable receive DMA */ 778d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_1); 779d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD); 780d3810ff9SJared McNeill 781d3810ff9SJared McNeill /* Enable transmitter */ 782d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_0); 783d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_0, val | TX_EN); 784d3810ff9SJared McNeill 785d3810ff9SJared McNeill /* Enable receiver */ 786d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 787d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val | RX_EN | CHECK_CRC); 788d3810ff9SJared McNeill 789d3810ff9SJared McNeill if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 790d3810ff9SJared McNeill 791d3810ff9SJared McNeill mii_mediachg(mii); 792d3810ff9SJared McNeill callout_reset(&sc->stat_ch, hz, awg_tick, sc); 793d3810ff9SJared McNeill } 794d3810ff9SJared McNeill 795d3810ff9SJared McNeill static void 796d3810ff9SJared McNeill awg_init(void *softc) 797d3810ff9SJared McNeill { 798d3810ff9SJared McNeill struct awg_softc *sc; 799d3810ff9SJared McNeill 800d3810ff9SJared McNeill sc = softc; 801d3810ff9SJared McNeill 802d3810ff9SJared McNeill AWG_LOCK(sc); 803d3810ff9SJared McNeill awg_init_locked(sc); 804d3810ff9SJared McNeill AWG_UNLOCK(sc); 805d3810ff9SJared McNeill } 806d3810ff9SJared McNeill 807d3810ff9SJared McNeill static void 808d3810ff9SJared McNeill awg_stop(struct awg_softc *sc) 809d3810ff9SJared McNeill { 810d3810ff9SJared McNeill if_t ifp; 811d3810ff9SJared McNeill uint32_t val; 812d3810ff9SJared McNeill 813d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 814d3810ff9SJared McNeill 815d3810ff9SJared McNeill ifp = sc->ifp; 816d3810ff9SJared McNeill 817d3810ff9SJared McNeill callout_stop(&sc->stat_ch); 818d3810ff9SJared McNeill 819d3810ff9SJared McNeill /* Stop transmit DMA and flush data in the TX FIFO */ 820d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 821d3810ff9SJared McNeill val &= ~TX_DMA_EN; 822d3810ff9SJared McNeill val |= FLUSH_TX_FIFO; 823d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val); 824d3810ff9SJared McNeill 825d3810ff9SJared McNeill /* Disable transmitter */ 826d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_0); 827d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_0, val & ~TX_EN); 828d3810ff9SJared McNeill 829d3810ff9SJared McNeill /* Disable receiver */ 830d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 831d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val & ~RX_EN); 832d3810ff9SJared McNeill 833d3810ff9SJared McNeill /* Disable interrupts */ 83416928528SJared McNeill awg_disable_intr(sc); 835d3810ff9SJared McNeill 836d3810ff9SJared McNeill /* Disable transmit DMA */ 837d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 838d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN); 839d3810ff9SJared McNeill 840d3810ff9SJared McNeill /* Disable receive DMA */ 841d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_1); 842d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN); 843d3810ff9SJared McNeill 844d3810ff9SJared McNeill sc->link = 0; 845d3810ff9SJared McNeill 846d3810ff9SJared McNeill if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 847d3810ff9SJared McNeill } 848d3810ff9SJared McNeill 84916928528SJared McNeill static int 850d3810ff9SJared McNeill awg_rxintr(struct awg_softc *sc) 851d3810ff9SJared McNeill { 852d3810ff9SJared McNeill if_t ifp; 853*bd906329SEmmanuel Vadot struct mbuf *m, *mh, *mt; 85416928528SJared McNeill int error, index, len, cnt, npkt; 855d3810ff9SJared McNeill uint32_t status; 856d3810ff9SJared McNeill 857d3810ff9SJared McNeill ifp = sc->ifp; 85816928528SJared McNeill mh = mt = NULL; 85916928528SJared McNeill cnt = 0; 86016928528SJared McNeill npkt = 0; 861d3810ff9SJared McNeill 862d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 863d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 864d3810ff9SJared McNeill 865d3810ff9SJared McNeill for (index = sc->rx.cur; ; index = RX_NEXT(index)) { 866d3810ff9SJared McNeill status = le32toh(sc->rx.desc_ring[index].status); 867d3810ff9SJared McNeill if ((status & RX_DESC_CTL) != 0) 868d3810ff9SJared McNeill break; 869d3810ff9SJared McNeill 870d3810ff9SJared McNeill len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; 871*bd906329SEmmanuel Vadot 872*bd906329SEmmanuel Vadot if (len == 0) { 873*bd906329SEmmanuel Vadot if ((status & (RX_NO_ENOUGH_BUF_ERR | RX_OVERFLOW_ERR)) != 0) 874*bd906329SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 875*bd906329SEmmanuel Vadot awg_reuse_rxdesc(sc, index); 876*bd906329SEmmanuel Vadot continue; 877*bd906329SEmmanuel Vadot } 878*bd906329SEmmanuel Vadot 879d3810ff9SJared McNeill m = sc->rx.buf_map[index].mbuf; 880*bd906329SEmmanuel Vadot 881*bd906329SEmmanuel Vadot error = awg_newbuf_rx(sc, index); 882*bd906329SEmmanuel Vadot if (error != 0) { 883*bd906329SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 884*bd906329SEmmanuel Vadot awg_reuse_rxdesc(sc, index); 885*bd906329SEmmanuel Vadot continue; 886*bd906329SEmmanuel Vadot } 887*bd906329SEmmanuel Vadot 888d3810ff9SJared McNeill m->m_pkthdr.rcvif = ifp; 889d3810ff9SJared McNeill m->m_pkthdr.len = len; 890d3810ff9SJared McNeill m->m_len = len; 891d3810ff9SJared McNeill if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 892d3810ff9SJared McNeill 893d3810ff9SJared McNeill if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 894d3810ff9SJared McNeill (status & RX_FRM_TYPE) != 0) { 895d3810ff9SJared McNeill m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 896d3810ff9SJared McNeill if ((status & RX_HEADER_ERR) == 0) 897d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 898d3810ff9SJared McNeill if ((status & RX_PAYLOAD_ERR) == 0) { 899d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= 900d3810ff9SJared McNeill CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 901d3810ff9SJared McNeill m->m_pkthdr.csum_data = 0xffff; 902d3810ff9SJared McNeill } 903d3810ff9SJared McNeill } 904d3810ff9SJared McNeill 90516928528SJared McNeill m->m_nextpkt = NULL; 90616928528SJared McNeill if (mh == NULL) 90716928528SJared McNeill mh = m; 90816928528SJared McNeill else 90916928528SJared McNeill mt->m_nextpkt = m; 91016928528SJared McNeill mt = m; 91116928528SJared McNeill ++cnt; 91216928528SJared McNeill ++npkt; 91316928528SJared McNeill 91416928528SJared McNeill if (cnt == awg_rx_batch) { 915d3810ff9SJared McNeill AWG_UNLOCK(sc); 91616928528SJared McNeill if_input(ifp, mh); 917d3810ff9SJared McNeill AWG_LOCK(sc); 91816928528SJared McNeill mh = mt = NULL; 91916928528SJared McNeill cnt = 0; 92016928528SJared McNeill } 921d3810ff9SJared McNeill } 922d3810ff9SJared McNeill 923d3810ff9SJared McNeill if (index != sc->rx.cur) { 924d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 925*bd906329SEmmanuel Vadot BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 926d3810ff9SJared McNeill } 927d3810ff9SJared McNeill 92816928528SJared McNeill if (mh != NULL) { 92916928528SJared McNeill AWG_UNLOCK(sc); 93016928528SJared McNeill if_input(ifp, mh); 93116928528SJared McNeill AWG_LOCK(sc); 93216928528SJared McNeill } 93316928528SJared McNeill 934d3810ff9SJared McNeill sc->rx.cur = index; 93516928528SJared McNeill 93616928528SJared McNeill return (npkt); 937d3810ff9SJared McNeill } 938d3810ff9SJared McNeill 939d3810ff9SJared McNeill static void 940337c6940SEmmanuel Vadot awg_txeof(struct awg_softc *sc) 941d3810ff9SJared McNeill { 942d3810ff9SJared McNeill struct emac_desc *desc; 94309e2285cSEmmanuel Vadot uint32_t status, size; 944d3810ff9SJared McNeill if_t ifp; 945f179ed05SEmmanuel Vadot int i, prog; 946d3810ff9SJared McNeill 947d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 948d3810ff9SJared McNeill 949d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 950d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 951d3810ff9SJared McNeill 952d3810ff9SJared McNeill ifp = sc->ifp; 953f179ed05SEmmanuel Vadot 954f179ed05SEmmanuel Vadot prog = 0; 955d3810ff9SJared McNeill for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { 956d3810ff9SJared McNeill desc = &sc->tx.desc_ring[i]; 957d3810ff9SJared McNeill status = le32toh(desc->status); 958d3810ff9SJared McNeill if ((status & TX_DESC_CTL) != 0) 959d3810ff9SJared McNeill break; 96009e2285cSEmmanuel Vadot size = le32toh(desc->size); 96109e2285cSEmmanuel Vadot if (size & TX_LAST_DESC) { 96209e2285cSEmmanuel Vadot if ((status & (TX_HEADER_ERR | TX_PAYLOAD_ERR)) != 0) 96309e2285cSEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 96409e2285cSEmmanuel Vadot else 96509e2285cSEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 96609e2285cSEmmanuel Vadot } 967f179ed05SEmmanuel Vadot prog++; 968c6110e75SEmmanuel Vadot awg_clean_txbuf(sc, i); 969d3810ff9SJared McNeill } 970d3810ff9SJared McNeill 971f179ed05SEmmanuel Vadot if (prog > 0) { 972d3810ff9SJared McNeill sc->tx.next = i; 973f179ed05SEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 974f179ed05SEmmanuel Vadot } 975d3810ff9SJared McNeill } 976d3810ff9SJared McNeill 977d3810ff9SJared McNeill static void 978d3810ff9SJared McNeill awg_intr(void *arg) 979d3810ff9SJared McNeill { 980d3810ff9SJared McNeill struct awg_softc *sc; 981d3810ff9SJared McNeill uint32_t val; 982d3810ff9SJared McNeill 983d3810ff9SJared McNeill sc = arg; 984d3810ff9SJared McNeill 985d3810ff9SJared McNeill AWG_LOCK(sc); 986d3810ff9SJared McNeill val = RD4(sc, EMAC_INT_STA); 987d3810ff9SJared McNeill WR4(sc, EMAC_INT_STA, val); 988d3810ff9SJared McNeill 989d3810ff9SJared McNeill if (val & RX_INT) 990d3810ff9SJared McNeill awg_rxintr(sc); 991d3810ff9SJared McNeill 9920d2abe1eSEmmanuel Vadot if (val & TX_INT) 993337c6940SEmmanuel Vadot awg_txeof(sc); 9940d2abe1eSEmmanuel Vadot 9950d2abe1eSEmmanuel Vadot if (val & (TX_INT | TX_BUF_UA_INT)) { 996d3810ff9SJared McNeill if (!if_sendq_empty(sc->ifp)) 997d3810ff9SJared McNeill awg_start_locked(sc); 998d3810ff9SJared McNeill } 999d3810ff9SJared McNeill 1000d3810ff9SJared McNeill AWG_UNLOCK(sc); 1001d3810ff9SJared McNeill } 1002d3810ff9SJared McNeill 100316928528SJared McNeill #ifdef DEVICE_POLLING 100416928528SJared McNeill static int 100516928528SJared McNeill awg_poll(if_t ifp, enum poll_cmd cmd, int count) 100616928528SJared McNeill { 100716928528SJared McNeill struct awg_softc *sc; 100816928528SJared McNeill uint32_t val; 100916928528SJared McNeill int rx_npkts; 101016928528SJared McNeill 101116928528SJared McNeill sc = if_getsoftc(ifp); 101216928528SJared McNeill rx_npkts = 0; 101316928528SJared McNeill 101416928528SJared McNeill AWG_LOCK(sc); 101516928528SJared McNeill 101616928528SJared McNeill if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) { 101716928528SJared McNeill AWG_UNLOCK(sc); 101816928528SJared McNeill return (0); 101916928528SJared McNeill } 102016928528SJared McNeill 102116928528SJared McNeill rx_npkts = awg_rxintr(sc); 1022337c6940SEmmanuel Vadot awg_txeof(sc); 102316928528SJared McNeill if (!if_sendq_empty(ifp)) 102416928528SJared McNeill awg_start_locked(sc); 102516928528SJared McNeill 102616928528SJared McNeill if (cmd == POLL_AND_CHECK_STATUS) { 102716928528SJared McNeill val = RD4(sc, EMAC_INT_STA); 102816928528SJared McNeill if (val != 0) 102916928528SJared McNeill WR4(sc, EMAC_INT_STA, val); 103016928528SJared McNeill } 103116928528SJared McNeill 103216928528SJared McNeill AWG_UNLOCK(sc); 103316928528SJared McNeill 103416928528SJared McNeill return (rx_npkts); 103516928528SJared McNeill } 103616928528SJared McNeill #endif 103716928528SJared McNeill 1038d3810ff9SJared McNeill static int 1039d3810ff9SJared McNeill awg_ioctl(if_t ifp, u_long cmd, caddr_t data) 1040d3810ff9SJared McNeill { 1041d3810ff9SJared McNeill struct awg_softc *sc; 1042d3810ff9SJared McNeill struct mii_data *mii; 1043d3810ff9SJared McNeill struct ifreq *ifr; 1044d3810ff9SJared McNeill int flags, mask, error; 1045d3810ff9SJared McNeill 1046d3810ff9SJared McNeill sc = if_getsoftc(ifp); 1047d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 1048d3810ff9SJared McNeill ifr = (struct ifreq *)data; 1049d3810ff9SJared McNeill error = 0; 1050d3810ff9SJared McNeill 1051d3810ff9SJared McNeill switch (cmd) { 1052d3810ff9SJared McNeill case SIOCSIFFLAGS: 1053d3810ff9SJared McNeill AWG_LOCK(sc); 1054d3810ff9SJared McNeill if (if_getflags(ifp) & IFF_UP) { 1055d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 1056d3810ff9SJared McNeill flags = if_getflags(ifp) ^ sc->if_flags; 1057d3810ff9SJared McNeill if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 1058d3810ff9SJared McNeill awg_setup_rxfilter(sc); 1059d3810ff9SJared McNeill } else 1060d3810ff9SJared McNeill awg_init_locked(sc); 1061d3810ff9SJared McNeill } else { 1062d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 1063d3810ff9SJared McNeill awg_stop(sc); 1064d3810ff9SJared McNeill } 1065d3810ff9SJared McNeill sc->if_flags = if_getflags(ifp); 1066d3810ff9SJared McNeill AWG_UNLOCK(sc); 1067d3810ff9SJared McNeill break; 1068d3810ff9SJared McNeill case SIOCADDMULTI: 1069d3810ff9SJared McNeill case SIOCDELMULTI: 1070d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 1071d3810ff9SJared McNeill AWG_LOCK(sc); 1072d3810ff9SJared McNeill awg_setup_rxfilter(sc); 1073d3810ff9SJared McNeill AWG_UNLOCK(sc); 1074d3810ff9SJared McNeill } 1075d3810ff9SJared McNeill break; 1076d3810ff9SJared McNeill case SIOCSIFMEDIA: 1077d3810ff9SJared McNeill case SIOCGIFMEDIA: 1078d3810ff9SJared McNeill error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 1079d3810ff9SJared McNeill break; 1080d3810ff9SJared McNeill case SIOCSIFCAP: 1081d3810ff9SJared McNeill mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 108216928528SJared McNeill #ifdef DEVICE_POLLING 108316928528SJared McNeill if (mask & IFCAP_POLLING) { 108416928528SJared McNeill if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) { 108516928528SJared McNeill error = ether_poll_register(awg_poll, ifp); 108616928528SJared McNeill if (error != 0) 108716928528SJared McNeill break; 108816928528SJared McNeill AWG_LOCK(sc); 108916928528SJared McNeill awg_disable_intr(sc); 109016928528SJared McNeill if_setcapenablebit(ifp, IFCAP_POLLING, 0); 109116928528SJared McNeill AWG_UNLOCK(sc); 109216928528SJared McNeill } else { 109316928528SJared McNeill error = ether_poll_deregister(ifp); 109416928528SJared McNeill AWG_LOCK(sc); 109516928528SJared McNeill awg_enable_intr(sc); 109616928528SJared McNeill if_setcapenablebit(ifp, 0, IFCAP_POLLING); 109716928528SJared McNeill AWG_UNLOCK(sc); 109816928528SJared McNeill } 109916928528SJared McNeill } 110016928528SJared McNeill #endif 1101d3810ff9SJared McNeill if (mask & IFCAP_VLAN_MTU) 1102d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_VLAN_MTU); 1103d3810ff9SJared McNeill if (mask & IFCAP_RXCSUM) 1104d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_RXCSUM); 1105d3810ff9SJared McNeill if (mask & IFCAP_TXCSUM) 1106d3810ff9SJared McNeill if_togglecapenable(ifp, IFCAP_TXCSUM); 11072a811fc0SJared McNeill if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 11082a811fc0SJared McNeill if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 1109d3810ff9SJared McNeill else 11102a811fc0SJared McNeill if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 1111d3810ff9SJared McNeill break; 1112d3810ff9SJared McNeill default: 1113d3810ff9SJared McNeill error = ether_ioctl(ifp, cmd, data); 1114d3810ff9SJared McNeill break; 1115d3810ff9SJared McNeill } 1116d3810ff9SJared McNeill 1117d3810ff9SJared McNeill return (error); 1118d3810ff9SJared McNeill } 1119d3810ff9SJared McNeill 1120d3810ff9SJared McNeill static int 112101a469b8SJared McNeill awg_setup_phy(device_t dev) 1122d3810ff9SJared McNeill { 1123d3810ff9SJared McNeill struct awg_softc *sc; 112401a469b8SJared McNeill clk_t clk_tx, clk_tx_parent; 1125d3810ff9SJared McNeill const char *tx_parent_name; 1126d3810ff9SJared McNeill char *phy_type; 1127d3810ff9SJared McNeill phandle_t node; 112801a469b8SJared McNeill uint32_t reg, tx_delay, rx_delay; 112901a469b8SJared McNeill int error; 1130d3810ff9SJared McNeill 1131d3810ff9SJared McNeill sc = device_get_softc(dev); 1132d3810ff9SJared McNeill node = ofw_bus_get_node(dev); 1133d3810ff9SJared McNeill 113401a469b8SJared McNeill if (OF_getprop_alloc(node, "phy-mode", 1, (void **)&phy_type) == 0) 113501a469b8SJared McNeill return (0); 1136d3810ff9SJared McNeill 1137d3810ff9SJared McNeill if (bootverbose) 113801a469b8SJared McNeill device_printf(dev, "PHY type: %s, conf mode: %s\n", phy_type, 113901a469b8SJared McNeill sc->res[_RES_SYSCON] != NULL ? "reg" : "clk"); 1140d3810ff9SJared McNeill 114101a469b8SJared McNeill if (sc->res[_RES_SYSCON] != NULL) { 114201a469b8SJared McNeill reg = bus_read_4(sc->res[_RES_SYSCON], 0); 114301a469b8SJared McNeill reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN); 114401a469b8SJared McNeill if (strcmp(phy_type, "rgmii") == 0) 114501a469b8SJared McNeill reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII; 114601a469b8SJared McNeill else if (strcmp(phy_type, "rmii") == 0) 114701a469b8SJared McNeill reg |= EMAC_CLK_RMII_EN; 114801a469b8SJared McNeill else 114901a469b8SJared McNeill reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII; 115001a469b8SJared McNeill 115101a469b8SJared McNeill if (OF_getencprop(node, "tx-delay", &tx_delay, 115201a469b8SJared McNeill sizeof(tx_delay)) > 0) { 115301a469b8SJared McNeill reg &= ~EMAC_CLK_ETXDC; 115401a469b8SJared McNeill reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT); 115501a469b8SJared McNeill } 115601a469b8SJared McNeill if (OF_getencprop(node, "rx-delay", &rx_delay, 115701a469b8SJared McNeill sizeof(rx_delay)) > 0) { 115801a469b8SJared McNeill reg &= ~EMAC_CLK_ERXDC; 115901a469b8SJared McNeill reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT); 116001a469b8SJared McNeill } 116101a469b8SJared McNeill 116201a469b8SJared McNeill if (sc->type == EMAC_H3) { 116301a469b8SJared McNeill if (OF_hasprop(node, "allwinner,use-internal-phy")) { 116401a469b8SJared McNeill reg |= EMAC_CLK_EPHY_SELECT; 116501a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_SHUTDOWN; 116601a469b8SJared McNeill if (OF_hasprop(node, 116701a469b8SJared McNeill "allwinner,leds-active-low")) 116801a469b8SJared McNeill reg |= EMAC_CLK_EPHY_LED_POL; 116901a469b8SJared McNeill else 117001a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_LED_POL; 117101a469b8SJared McNeill 117201a469b8SJared McNeill /* Set internal PHY addr to 1 */ 117301a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_ADDR; 117401a469b8SJared McNeill reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT); 117501a469b8SJared McNeill } else { 117601a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_SELECT; 117701a469b8SJared McNeill } 117801a469b8SJared McNeill } 117901a469b8SJared McNeill 118001a469b8SJared McNeill if (bootverbose) 118101a469b8SJared McNeill device_printf(dev, "EMAC clock: 0x%08x\n", reg); 118201a469b8SJared McNeill bus_write_4(sc->res[_RES_SYSCON], 0, reg); 118301a469b8SJared McNeill } else { 1184d3810ff9SJared McNeill if (strcmp(phy_type, "rgmii") == 0) 1185d3810ff9SJared McNeill tx_parent_name = "emac_int_tx"; 1186d3810ff9SJared McNeill else 1187d3810ff9SJared McNeill tx_parent_name = "mii_phy_tx"; 1188d3810ff9SJared McNeill 1189d3810ff9SJared McNeill /* Get the TX clock */ 1190dac93553SMichal Meloun error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx); 1191d3810ff9SJared McNeill if (error != 0) { 1192d3810ff9SJared McNeill device_printf(dev, "cannot get tx clock\n"); 1193d3810ff9SJared McNeill goto fail; 1194d3810ff9SJared McNeill } 1195d3810ff9SJared McNeill 1196d3810ff9SJared McNeill /* Find the desired parent clock based on phy-mode property */ 1197d3810ff9SJared McNeill error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); 1198d3810ff9SJared McNeill if (error != 0) { 1199d3810ff9SJared McNeill device_printf(dev, "cannot get clock '%s'\n", 1200d3810ff9SJared McNeill tx_parent_name); 1201d3810ff9SJared McNeill goto fail; 1202d3810ff9SJared McNeill } 1203d3810ff9SJared McNeill 1204d3810ff9SJared McNeill /* Set TX clock parent */ 1205d3810ff9SJared McNeill error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); 1206d3810ff9SJared McNeill if (error != 0) { 1207d3810ff9SJared McNeill device_printf(dev, "cannot set tx clock parent\n"); 1208d3810ff9SJared McNeill goto fail; 1209d3810ff9SJared McNeill } 1210d3810ff9SJared McNeill 1211d3810ff9SJared McNeill /* Enable TX clock */ 1212d3810ff9SJared McNeill error = clk_enable(clk_tx); 1213d3810ff9SJared McNeill if (error != 0) { 1214d3810ff9SJared McNeill device_printf(dev, "cannot enable tx clock\n"); 1215d3810ff9SJared McNeill goto fail; 1216d3810ff9SJared McNeill } 1217d3810ff9SJared McNeill } 1218d3810ff9SJared McNeill 121901a469b8SJared McNeill error = 0; 122001a469b8SJared McNeill 122101a469b8SJared McNeill fail: 122201a469b8SJared McNeill OF_prop_free(phy_type); 122301a469b8SJared McNeill return (error); 122401a469b8SJared McNeill } 122501a469b8SJared McNeill 122601a469b8SJared McNeill static int 122701a469b8SJared McNeill awg_setup_extres(device_t dev) 122801a469b8SJared McNeill { 122901a469b8SJared McNeill struct awg_softc *sc; 123001a469b8SJared McNeill hwreset_t rst_ahb, rst_ephy; 123101a469b8SJared McNeill clk_t clk_ahb, clk_ephy; 123201a469b8SJared McNeill regulator_t reg; 123301a469b8SJared McNeill phandle_t node; 123401a469b8SJared McNeill uint64_t freq; 123501a469b8SJared McNeill int error, div; 123601a469b8SJared McNeill 123701a469b8SJared McNeill sc = device_get_softc(dev); 123801a469b8SJared McNeill node = ofw_bus_get_node(dev); 123901a469b8SJared McNeill rst_ahb = rst_ephy = NULL; 124001a469b8SJared McNeill clk_ahb = clk_ephy = NULL; 124101a469b8SJared McNeill reg = NULL; 124201a469b8SJared McNeill 124301a469b8SJared McNeill /* Get AHB clock and reset resources */ 124401a469b8SJared McNeill error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); 124501a469b8SJared McNeill if (error != 0) { 124601a469b8SJared McNeill device_printf(dev, "cannot get ahb reset\n"); 124701a469b8SJared McNeill goto fail; 124801a469b8SJared McNeill } 124901a469b8SJared McNeill if (hwreset_get_by_ofw_name(dev, 0, "ephy", &rst_ephy) != 0) 125001a469b8SJared McNeill rst_ephy = NULL; 125101a469b8SJared McNeill error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); 125201a469b8SJared McNeill if (error != 0) { 125301a469b8SJared McNeill device_printf(dev, "cannot get ahb clock\n"); 125401a469b8SJared McNeill goto fail; 125501a469b8SJared McNeill } 125601a469b8SJared McNeill if (clk_get_by_ofw_name(dev, 0, "ephy", &clk_ephy) != 0) 125701a469b8SJared McNeill clk_ephy = NULL; 125801a469b8SJared McNeill 125901a469b8SJared McNeill /* Configure PHY for MII or RGMII mode */ 126001a469b8SJared McNeill if (awg_setup_phy(dev) != 0) 126101a469b8SJared McNeill goto fail; 126201a469b8SJared McNeill 126301a469b8SJared McNeill /* Enable clocks */ 1264d3810ff9SJared McNeill error = clk_enable(clk_ahb); 1265d3810ff9SJared McNeill if (error != 0) { 1266d3810ff9SJared McNeill device_printf(dev, "cannot enable ahb clock\n"); 1267d3810ff9SJared McNeill goto fail; 1268d3810ff9SJared McNeill } 126901a469b8SJared McNeill if (clk_ephy != NULL) { 127001a469b8SJared McNeill error = clk_enable(clk_ephy); 127101a469b8SJared McNeill if (error != 0) { 127201a469b8SJared McNeill device_printf(dev, "cannot enable ephy clock\n"); 127301a469b8SJared McNeill goto fail; 127401a469b8SJared McNeill } 127501a469b8SJared McNeill } 1276d3810ff9SJared McNeill 1277d3810ff9SJared McNeill /* De-assert reset */ 1278d3810ff9SJared McNeill error = hwreset_deassert(rst_ahb); 1279d3810ff9SJared McNeill if (error != 0) { 1280d3810ff9SJared McNeill device_printf(dev, "cannot de-assert ahb reset\n"); 1281d3810ff9SJared McNeill goto fail; 1282d3810ff9SJared McNeill } 128301a469b8SJared McNeill if (rst_ephy != NULL) { 128401a469b8SJared McNeill error = hwreset_deassert(rst_ephy); 128501a469b8SJared McNeill if (error != 0) { 128601a469b8SJared McNeill device_printf(dev, "cannot de-assert ephy reset\n"); 128701a469b8SJared McNeill goto fail; 128801a469b8SJared McNeill } 128901a469b8SJared McNeill } 1290d3810ff9SJared McNeill 1291d3810ff9SJared McNeill /* Enable PHY regulator if applicable */ 1292dac93553SMichal Meloun if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { 1293d3810ff9SJared McNeill error = regulator_enable(reg); 1294d3810ff9SJared McNeill if (error != 0) { 1295d3810ff9SJared McNeill device_printf(dev, "cannot enable PHY regulator\n"); 1296d3810ff9SJared McNeill goto fail; 1297d3810ff9SJared McNeill } 1298d3810ff9SJared McNeill } 1299d3810ff9SJared McNeill 1300d3810ff9SJared McNeill /* Determine MDC clock divide ratio based on AHB clock */ 1301d3810ff9SJared McNeill error = clk_get_freq(clk_ahb, &freq); 1302d3810ff9SJared McNeill if (error != 0) { 1303d3810ff9SJared McNeill device_printf(dev, "cannot get AHB clock frequency\n"); 1304d3810ff9SJared McNeill goto fail; 1305d3810ff9SJared McNeill } 1306d3810ff9SJared McNeill div = freq / MDIO_FREQ; 1307d3810ff9SJared McNeill if (div <= 16) 1308d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16; 1309d3810ff9SJared McNeill else if (div <= 32) 1310d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32; 1311d3810ff9SJared McNeill else if (div <= 64) 1312d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64; 1313d3810ff9SJared McNeill else if (div <= 128) 1314d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128; 1315d3810ff9SJared McNeill else { 1316d3810ff9SJared McNeill device_printf(dev, "cannot determine MDC clock divide ratio\n"); 1317d3810ff9SJared McNeill error = ENXIO; 1318d3810ff9SJared McNeill goto fail; 1319d3810ff9SJared McNeill } 1320d3810ff9SJared McNeill 1321d3810ff9SJared McNeill if (bootverbose) 132201a469b8SJared McNeill device_printf(dev, "AHB frequency %ju Hz, MDC div: 0x%x\n", 132301a469b8SJared McNeill (uintmax_t)freq, sc->mdc_div_ratio_m); 1324d3810ff9SJared McNeill 1325d3810ff9SJared McNeill return (0); 1326d3810ff9SJared McNeill 1327d3810ff9SJared McNeill fail: 1328d3810ff9SJared McNeill if (reg != NULL) 1329d3810ff9SJared McNeill regulator_release(reg); 133001a469b8SJared McNeill if (clk_ephy != NULL) 133101a469b8SJared McNeill clk_release(clk_ephy); 1332d3810ff9SJared McNeill if (clk_ahb != NULL) 1333d3810ff9SJared McNeill clk_release(clk_ahb); 133401a469b8SJared McNeill if (rst_ephy != NULL) 133501a469b8SJared McNeill hwreset_release(rst_ephy); 1336d3810ff9SJared McNeill if (rst_ahb != NULL) 1337d3810ff9SJared McNeill hwreset_release(rst_ahb); 1338d3810ff9SJared McNeill return (error); 1339d3810ff9SJared McNeill } 1340d3810ff9SJared McNeill 1341d3810ff9SJared McNeill static void 1342d3810ff9SJared McNeill awg_get_eaddr(device_t dev, uint8_t *eaddr) 1343d3810ff9SJared McNeill { 1344d3810ff9SJared McNeill struct awg_softc *sc; 1345d3810ff9SJared McNeill uint32_t maclo, machi, rnd; 13461403e695SJared McNeill u_char rootkey[16]; 1347d3810ff9SJared McNeill 1348d3810ff9SJared McNeill sc = device_get_softc(dev); 1349d3810ff9SJared McNeill 1350d3810ff9SJared McNeill machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff; 1351d3810ff9SJared McNeill maclo = RD4(sc, EMAC_ADDR_LOW(0)); 1352d3810ff9SJared McNeill 1353d3810ff9SJared McNeill if (maclo == 0xffffffff && machi == 0xffff) { 1354d3810ff9SJared McNeill /* MAC address in hardware is invalid, create one */ 13551403e695SJared McNeill if (aw_sid_get_rootkey(rootkey) == 0 && 13561403e695SJared McNeill (rootkey[3] | rootkey[12] | rootkey[13] | rootkey[14] | 13571403e695SJared McNeill rootkey[15]) != 0) { 13581403e695SJared McNeill /* MAC address is derived from the root key in SID */ 13591403e695SJared McNeill maclo = (rootkey[13] << 24) | (rootkey[12] << 16) | 13601403e695SJared McNeill (rootkey[3] << 8) | 0x02; 13611403e695SJared McNeill machi = (rootkey[15] << 8) | rootkey[14]; 13621403e695SJared McNeill } else { 13631403e695SJared McNeill /* Create one */ 1364d3810ff9SJared McNeill rnd = arc4random(); 1365d3810ff9SJared McNeill maclo = 0x00f2 | (rnd & 0xffff0000); 1366d3810ff9SJared McNeill machi = rnd & 0xffff; 1367d3810ff9SJared McNeill } 13681403e695SJared McNeill } 1369d3810ff9SJared McNeill 1370d3810ff9SJared McNeill eaddr[0] = maclo & 0xff; 1371d3810ff9SJared McNeill eaddr[1] = (maclo >> 8) & 0xff; 1372d3810ff9SJared McNeill eaddr[2] = (maclo >> 16) & 0xff; 1373d3810ff9SJared McNeill eaddr[3] = (maclo >> 24) & 0xff; 1374d3810ff9SJared McNeill eaddr[4] = machi & 0xff; 1375d3810ff9SJared McNeill eaddr[5] = (machi >> 8) & 0xff; 1376d3810ff9SJared McNeill } 1377d3810ff9SJared McNeill 1378d3810ff9SJared McNeill #ifdef AWG_DEBUG 1379d3810ff9SJared McNeill static void 1380d3810ff9SJared McNeill awg_dump_regs(device_t dev) 1381d3810ff9SJared McNeill { 1382d3810ff9SJared McNeill static const struct { 1383d3810ff9SJared McNeill const char *name; 1384d3810ff9SJared McNeill u_int reg; 1385d3810ff9SJared McNeill } regs[] = { 1386d3810ff9SJared McNeill { "BASIC_CTL_0", EMAC_BASIC_CTL_0 }, 1387d3810ff9SJared McNeill { "BASIC_CTL_1", EMAC_BASIC_CTL_1 }, 1388d3810ff9SJared McNeill { "INT_STA", EMAC_INT_STA }, 1389d3810ff9SJared McNeill { "INT_EN", EMAC_INT_EN }, 1390d3810ff9SJared McNeill { "TX_CTL_0", EMAC_TX_CTL_0 }, 1391d3810ff9SJared McNeill { "TX_CTL_1", EMAC_TX_CTL_1 }, 1392d3810ff9SJared McNeill { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL }, 1393d3810ff9SJared McNeill { "TX_DMA_LIST", EMAC_TX_DMA_LIST }, 1394d3810ff9SJared McNeill { "RX_CTL_0", EMAC_RX_CTL_0 }, 1395d3810ff9SJared McNeill { "RX_CTL_1", EMAC_RX_CTL_1 }, 1396d3810ff9SJared McNeill { "RX_DMA_LIST", EMAC_RX_DMA_LIST }, 1397d3810ff9SJared McNeill { "RX_FRM_FLT", EMAC_RX_FRM_FLT }, 1398d3810ff9SJared McNeill { "RX_HASH_0", EMAC_RX_HASH_0 }, 1399d3810ff9SJared McNeill { "RX_HASH_1", EMAC_RX_HASH_1 }, 1400d3810ff9SJared McNeill { "MII_CMD", EMAC_MII_CMD }, 1401d3810ff9SJared McNeill { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) }, 1402d3810ff9SJared McNeill { "ADDR_LOW0", EMAC_ADDR_LOW(0) }, 1403d3810ff9SJared McNeill { "TX_DMA_STA", EMAC_TX_DMA_STA }, 1404d3810ff9SJared McNeill { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC }, 1405d3810ff9SJared McNeill { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF }, 1406d3810ff9SJared McNeill { "RX_DMA_STA", EMAC_RX_DMA_STA }, 1407d3810ff9SJared McNeill { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC }, 1408d3810ff9SJared McNeill { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF }, 1409d3810ff9SJared McNeill { "RGMII_STA", EMAC_RGMII_STA }, 1410d3810ff9SJared McNeill }; 1411d3810ff9SJared McNeill struct awg_softc *sc; 1412d3810ff9SJared McNeill unsigned int n; 1413d3810ff9SJared McNeill 1414d3810ff9SJared McNeill sc = device_get_softc(dev); 1415d3810ff9SJared McNeill 1416d3810ff9SJared McNeill for (n = 0; n < nitems(regs); n++) 1417d3810ff9SJared McNeill device_printf(dev, " %-20s %08x\n", regs[n].name, 1418d3810ff9SJared McNeill RD4(sc, regs[n].reg)); 1419d3810ff9SJared McNeill } 1420d3810ff9SJared McNeill #endif 1421d3810ff9SJared McNeill 142201a469b8SJared McNeill #define GPIO_ACTIVE_LOW 1 142301a469b8SJared McNeill 142401a469b8SJared McNeill static int 142501a469b8SJared McNeill awg_phy_reset(device_t dev) 142601a469b8SJared McNeill { 142701a469b8SJared McNeill pcell_t gpio_prop[4], delay_prop[3]; 142801a469b8SJared McNeill phandle_t node, gpio_node; 142901a469b8SJared McNeill device_t gpio; 143001a469b8SJared McNeill uint32_t pin, flags; 143101a469b8SJared McNeill uint32_t pin_value; 143201a469b8SJared McNeill 143301a469b8SJared McNeill node = ofw_bus_get_node(dev); 143401a469b8SJared McNeill if (OF_getencprop(node, "allwinner,reset-gpio", gpio_prop, 143501a469b8SJared McNeill sizeof(gpio_prop)) <= 0) 143601a469b8SJared McNeill return (0); 143701a469b8SJared McNeill 143801a469b8SJared McNeill if (OF_getencprop(node, "allwinner,reset-delays-us", delay_prop, 143901a469b8SJared McNeill sizeof(delay_prop)) <= 0) 144001a469b8SJared McNeill return (ENXIO); 144101a469b8SJared McNeill 144201a469b8SJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 144301a469b8SJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) 144401a469b8SJared McNeill return (ENXIO); 144501a469b8SJared McNeill 144601a469b8SJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1, 144701a469b8SJared McNeill gpio_prop + 1, &pin, &flags) != 0) 144801a469b8SJared McNeill return (ENXIO); 144901a469b8SJared McNeill 145001a469b8SJared McNeill pin_value = GPIO_PIN_LOW; 145101a469b8SJared McNeill if (OF_hasprop(node, "allwinner,reset-active-low")) 145201a469b8SJared McNeill pin_value = GPIO_PIN_HIGH; 145301a469b8SJared McNeill 145401a469b8SJared McNeill if (flags & GPIO_ACTIVE_LOW) 145501a469b8SJared McNeill pin_value = !pin_value; 145601a469b8SJared McNeill 145701a469b8SJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 145801a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 145901a469b8SJared McNeill DELAY(delay_prop[0]); 146001a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 146101a469b8SJared McNeill DELAY(delay_prop[1]); 146201a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 146301a469b8SJared McNeill DELAY(delay_prop[2]); 146401a469b8SJared McNeill 146501a469b8SJared McNeill return (0); 146601a469b8SJared McNeill } 146701a469b8SJared McNeill 1468d3810ff9SJared McNeill static int 1469d3810ff9SJared McNeill awg_reset(device_t dev) 1470d3810ff9SJared McNeill { 1471d3810ff9SJared McNeill struct awg_softc *sc; 1472d3810ff9SJared McNeill int retry; 1473d3810ff9SJared McNeill 1474d3810ff9SJared McNeill sc = device_get_softc(dev); 1475d3810ff9SJared McNeill 147601a469b8SJared McNeill /* Reset PHY if necessary */ 147701a469b8SJared McNeill if (awg_phy_reset(dev) != 0) { 147801a469b8SJared McNeill device_printf(dev, "failed to reset PHY\n"); 147901a469b8SJared McNeill return (ENXIO); 148001a469b8SJared McNeill } 148101a469b8SJared McNeill 1482d3810ff9SJared McNeill /* Soft reset all registers and logic */ 1483d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); 1484d3810ff9SJared McNeill 1485d3810ff9SJared McNeill /* Wait for soft reset bit to self-clear */ 1486d3810ff9SJared McNeill for (retry = SOFT_RST_RETRY; retry > 0; retry--) { 1487d3810ff9SJared McNeill if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0) 1488d3810ff9SJared McNeill break; 1489d3810ff9SJared McNeill DELAY(10); 1490d3810ff9SJared McNeill } 1491d3810ff9SJared McNeill if (retry == 0) { 1492d3810ff9SJared McNeill device_printf(dev, "soft reset timed out\n"); 1493d3810ff9SJared McNeill #ifdef AWG_DEBUG 1494d3810ff9SJared McNeill awg_dump_regs(dev); 1495d3810ff9SJared McNeill #endif 1496d3810ff9SJared McNeill return (ETIMEDOUT); 1497d3810ff9SJared McNeill } 1498d3810ff9SJared McNeill 1499d3810ff9SJared McNeill return (0); 1500d3810ff9SJared McNeill } 1501d3810ff9SJared McNeill 1502d3810ff9SJared McNeill static void 1503d3810ff9SJared McNeill awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 1504d3810ff9SJared McNeill { 1505d3810ff9SJared McNeill if (error != 0) 1506d3810ff9SJared McNeill return; 1507d3810ff9SJared McNeill *(bus_addr_t *)arg = segs[0].ds_addr; 1508d3810ff9SJared McNeill } 1509d3810ff9SJared McNeill 1510d3810ff9SJared McNeill static int 1511d3810ff9SJared McNeill awg_setup_dma(device_t dev) 1512d3810ff9SJared McNeill { 1513d3810ff9SJared McNeill struct awg_softc *sc; 1514d3810ff9SJared McNeill int error, i; 1515d3810ff9SJared McNeill 1516d3810ff9SJared McNeill sc = device_get_softc(dev); 1517d3810ff9SJared McNeill 1518d3810ff9SJared McNeill /* Setup TX ring */ 1519d3810ff9SJared McNeill error = bus_dma_tag_create( 1520d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1521d3810ff9SJared McNeill DESC_ALIGN, 0, /* alignment, boundary */ 1522d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1523d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1524d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1525d3810ff9SJared McNeill TX_DESC_SIZE, 1, /* maxsize, nsegs */ 1526d3810ff9SJared McNeill TX_DESC_SIZE, /* maxsegsize */ 1527d3810ff9SJared McNeill 0, /* flags */ 1528d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1529d3810ff9SJared McNeill &sc->tx.desc_tag); 1530d3810ff9SJared McNeill if (error != 0) { 1531d3810ff9SJared McNeill device_printf(dev, "cannot create TX descriptor ring tag\n"); 1532d3810ff9SJared McNeill return (error); 1533d3810ff9SJared McNeill } 1534d3810ff9SJared McNeill 1535d3810ff9SJared McNeill error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring, 1536d3810ff9SJared McNeill BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map); 1537d3810ff9SJared McNeill if (error != 0) { 1538d3810ff9SJared McNeill device_printf(dev, "cannot allocate TX descriptor ring\n"); 1539d3810ff9SJared McNeill return (error); 1540d3810ff9SJared McNeill } 1541d3810ff9SJared McNeill 1542d3810ff9SJared McNeill error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map, 1543d3810ff9SJared McNeill sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb, 1544d3810ff9SJared McNeill &sc->tx.desc_ring_paddr, 0); 1545d3810ff9SJared McNeill if (error != 0) { 1546d3810ff9SJared McNeill device_printf(dev, "cannot load TX descriptor ring\n"); 1547d3810ff9SJared McNeill return (error); 1548d3810ff9SJared McNeill } 1549d3810ff9SJared McNeill 1550d3810ff9SJared McNeill for (i = 0; i < TX_DESC_COUNT; i++) 1551d3810ff9SJared McNeill sc->tx.desc_ring[i].next = 1552d3810ff9SJared McNeill htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i))); 1553d3810ff9SJared McNeill 1554d3810ff9SJared McNeill error = bus_dma_tag_create( 1555d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1556d3810ff9SJared McNeill 1, 0, /* alignment, boundary */ 1557d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1558d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1559d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1560d3810ff9SJared McNeill MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ 1561d3810ff9SJared McNeill MCLBYTES, /* maxsegsize */ 1562d3810ff9SJared McNeill 0, /* flags */ 1563d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1564d3810ff9SJared McNeill &sc->tx.buf_tag); 1565d3810ff9SJared McNeill if (error != 0) { 1566d3810ff9SJared McNeill device_printf(dev, "cannot create TX buffer tag\n"); 1567d3810ff9SJared McNeill return (error); 1568d3810ff9SJared McNeill } 1569d3810ff9SJared McNeill 1570c6110e75SEmmanuel Vadot sc->tx.queued = 0; 1571d3810ff9SJared McNeill for (i = 0; i < TX_DESC_COUNT; i++) { 1572d3810ff9SJared McNeill error = bus_dmamap_create(sc->tx.buf_tag, 0, 1573d3810ff9SJared McNeill &sc->tx.buf_map[i].map); 1574d3810ff9SJared McNeill if (error != 0) { 1575d3810ff9SJared McNeill device_printf(dev, "cannot create TX buffer map\n"); 1576d3810ff9SJared McNeill return (error); 1577d3810ff9SJared McNeill } 1578d3810ff9SJared McNeill } 1579d3810ff9SJared McNeill 1580d3810ff9SJared McNeill /* Setup RX ring */ 1581d3810ff9SJared McNeill error = bus_dma_tag_create( 1582d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1583d3810ff9SJared McNeill DESC_ALIGN, 0, /* alignment, boundary */ 1584d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1585d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1586d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1587d3810ff9SJared McNeill RX_DESC_SIZE, 1, /* maxsize, nsegs */ 1588d3810ff9SJared McNeill RX_DESC_SIZE, /* maxsegsize */ 1589d3810ff9SJared McNeill 0, /* flags */ 1590d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1591d3810ff9SJared McNeill &sc->rx.desc_tag); 1592d3810ff9SJared McNeill if (error != 0) { 1593d3810ff9SJared McNeill device_printf(dev, "cannot create RX descriptor ring tag\n"); 1594d3810ff9SJared McNeill return (error); 1595d3810ff9SJared McNeill } 1596d3810ff9SJared McNeill 1597d3810ff9SJared McNeill error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring, 1598d3810ff9SJared McNeill BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map); 1599d3810ff9SJared McNeill if (error != 0) { 1600d3810ff9SJared McNeill device_printf(dev, "cannot allocate RX descriptor ring\n"); 1601d3810ff9SJared McNeill return (error); 1602d3810ff9SJared McNeill } 1603d3810ff9SJared McNeill 1604d3810ff9SJared McNeill error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map, 1605d3810ff9SJared McNeill sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb, 1606d3810ff9SJared McNeill &sc->rx.desc_ring_paddr, 0); 1607d3810ff9SJared McNeill if (error != 0) { 1608d3810ff9SJared McNeill device_printf(dev, "cannot load RX descriptor ring\n"); 1609d3810ff9SJared McNeill return (error); 1610d3810ff9SJared McNeill } 1611d3810ff9SJared McNeill 1612d3810ff9SJared McNeill error = bus_dma_tag_create( 1613d3810ff9SJared McNeill bus_get_dma_tag(dev), /* Parent tag */ 1614d3810ff9SJared McNeill 1, 0, /* alignment, boundary */ 1615d3810ff9SJared McNeill BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 1616d3810ff9SJared McNeill BUS_SPACE_MAXADDR, /* highaddr */ 1617d3810ff9SJared McNeill NULL, NULL, /* filter, filterarg */ 1618d3810ff9SJared McNeill MCLBYTES, 1, /* maxsize, nsegs */ 1619d3810ff9SJared McNeill MCLBYTES, /* maxsegsize */ 1620d3810ff9SJared McNeill 0, /* flags */ 1621d3810ff9SJared McNeill NULL, NULL, /* lockfunc, lockarg */ 1622d3810ff9SJared McNeill &sc->rx.buf_tag); 1623d3810ff9SJared McNeill if (error != 0) { 1624d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer tag\n"); 1625d3810ff9SJared McNeill return (error); 1626d3810ff9SJared McNeill } 1627d3810ff9SJared McNeill 1628*bd906329SEmmanuel Vadot error = bus_dmamap_create(sc->rx.buf_tag, 0, &sc->rx.buf_spare_map); 1629*bd906329SEmmanuel Vadot if (error != 0) { 1630*bd906329SEmmanuel Vadot device_printf(dev, 1631*bd906329SEmmanuel Vadot "cannot create RX buffer spare map\n"); 1632*bd906329SEmmanuel Vadot return (error); 1633*bd906329SEmmanuel Vadot } 1634*bd906329SEmmanuel Vadot 1635d3810ff9SJared McNeill for (i = 0; i < RX_DESC_COUNT; i++) { 1636*bd906329SEmmanuel Vadot sc->rx.desc_ring[i].next = 1637*bd906329SEmmanuel Vadot htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(i))); 1638*bd906329SEmmanuel Vadot 1639d3810ff9SJared McNeill error = bus_dmamap_create(sc->rx.buf_tag, 0, 1640d3810ff9SJared McNeill &sc->rx.buf_map[i].map); 1641d3810ff9SJared McNeill if (error != 0) { 1642d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer map\n"); 1643d3810ff9SJared McNeill return (error); 1644d3810ff9SJared McNeill } 1645*bd906329SEmmanuel Vadot sc->rx.buf_map[i].mbuf = NULL; 1646*bd906329SEmmanuel Vadot error = awg_newbuf_rx(sc, i); 1647d3810ff9SJared McNeill if (error != 0) { 1648d3810ff9SJared McNeill device_printf(dev, "cannot create RX buffer\n"); 1649d3810ff9SJared McNeill return (error); 1650d3810ff9SJared McNeill } 1651d3810ff9SJared McNeill } 1652d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 1653d3810ff9SJared McNeill BUS_DMASYNC_PREWRITE); 1654d3810ff9SJared McNeill 1655d3810ff9SJared McNeill /* Write transmit and receive descriptor base address registers */ 1656d3810ff9SJared McNeill WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr); 1657d3810ff9SJared McNeill WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr); 1658d3810ff9SJared McNeill 1659d3810ff9SJared McNeill return (0); 1660d3810ff9SJared McNeill } 1661d3810ff9SJared McNeill 1662d3810ff9SJared McNeill static int 1663d3810ff9SJared McNeill awg_probe(device_t dev) 1664d3810ff9SJared McNeill { 1665d3810ff9SJared McNeill if (!ofw_bus_status_okay(dev)) 1666d3810ff9SJared McNeill return (ENXIO); 1667d3810ff9SJared McNeill 1668d3810ff9SJared McNeill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1669d3810ff9SJared McNeill return (ENXIO); 1670d3810ff9SJared McNeill 1671d3810ff9SJared McNeill device_set_desc(dev, "Allwinner Gigabit Ethernet"); 1672d3810ff9SJared McNeill return (BUS_PROBE_DEFAULT); 1673d3810ff9SJared McNeill } 1674d3810ff9SJared McNeill 1675d3810ff9SJared McNeill static int 1676d3810ff9SJared McNeill awg_attach(device_t dev) 1677d3810ff9SJared McNeill { 1678d3810ff9SJared McNeill uint8_t eaddr[ETHER_ADDR_LEN]; 1679d3810ff9SJared McNeill struct awg_softc *sc; 1680d3810ff9SJared McNeill phandle_t node; 1681d3810ff9SJared McNeill int error; 1682d3810ff9SJared McNeill 1683d3810ff9SJared McNeill sc = device_get_softc(dev); 1684031d5777SOleksandr Tymoshenko sc->dev = dev; 168501a469b8SJared McNeill sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 1686d3810ff9SJared McNeill node = ofw_bus_get_node(dev); 1687d3810ff9SJared McNeill 1688d3810ff9SJared McNeill if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { 1689d3810ff9SJared McNeill device_printf(dev, "cannot allocate resources for device\n"); 1690d3810ff9SJared McNeill return (ENXIO); 1691d3810ff9SJared McNeill } 1692d3810ff9SJared McNeill 1693d3810ff9SJared McNeill mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); 1694d3810ff9SJared McNeill callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); 1695d3810ff9SJared McNeill TASK_INIT(&sc->link_task, 0, awg_link_task, sc); 1696d3810ff9SJared McNeill 1697d3810ff9SJared McNeill /* Setup clocks and regulators */ 1698d3810ff9SJared McNeill error = awg_setup_extres(dev); 1699d3810ff9SJared McNeill if (error != 0) 1700d3810ff9SJared McNeill return (error); 1701d3810ff9SJared McNeill 1702d3810ff9SJared McNeill /* Read MAC address before resetting the chip */ 1703d3810ff9SJared McNeill awg_get_eaddr(dev, eaddr); 1704d3810ff9SJared McNeill 1705d3810ff9SJared McNeill /* Soft reset EMAC core */ 1706d3810ff9SJared McNeill error = awg_reset(dev); 1707d3810ff9SJared McNeill if (error != 0) 1708d3810ff9SJared McNeill return (error); 1709d3810ff9SJared McNeill 1710d3810ff9SJared McNeill /* Setup DMA descriptors */ 1711d3810ff9SJared McNeill error = awg_setup_dma(dev); 1712d3810ff9SJared McNeill if (error != 0) 1713d3810ff9SJared McNeill return (error); 1714d3810ff9SJared McNeill 1715d3810ff9SJared McNeill /* Install interrupt handler */ 171601a469b8SJared McNeill error = bus_setup_intr(dev, sc->res[_RES_IRQ], 171701a469b8SJared McNeill INTR_TYPE_NET | INTR_MPSAFE, NULL, awg_intr, sc, &sc->ih); 1718d3810ff9SJared McNeill if (error != 0) { 1719d3810ff9SJared McNeill device_printf(dev, "cannot setup interrupt handler\n"); 1720d3810ff9SJared McNeill return (error); 1721d3810ff9SJared McNeill } 1722d3810ff9SJared McNeill 1723d3810ff9SJared McNeill /* Setup ethernet interface */ 1724d3810ff9SJared McNeill sc->ifp = if_alloc(IFT_ETHER); 1725d3810ff9SJared McNeill if_setsoftc(sc->ifp, sc); 1726d3810ff9SJared McNeill if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); 1727d3810ff9SJared McNeill if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 1728d3810ff9SJared McNeill if_setstartfn(sc->ifp, awg_start); 1729d3810ff9SJared McNeill if_setioctlfn(sc->ifp, awg_ioctl); 1730d3810ff9SJared McNeill if_setinitfn(sc->ifp, awg_init); 1731d3810ff9SJared McNeill if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); 1732d3810ff9SJared McNeill if_setsendqready(sc->ifp); 1733d3810ff9SJared McNeill if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 1734d3810ff9SJared McNeill if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 1735d3810ff9SJared McNeill if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 173616928528SJared McNeill #ifdef DEVICE_POLLING 173716928528SJared McNeill if_setcapabilitiesbit(sc->ifp, IFCAP_POLLING, 0); 173816928528SJared McNeill #endif 1739d3810ff9SJared McNeill 1740d3810ff9SJared McNeill /* Attach MII driver */ 1741d3810ff9SJared McNeill error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change, 1742d3810ff9SJared McNeill awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 1743d3810ff9SJared McNeill MIIF_DOPAUSE); 1744d3810ff9SJared McNeill if (error != 0) { 1745d3810ff9SJared McNeill device_printf(dev, "cannot attach PHY\n"); 1746d3810ff9SJared McNeill return (error); 1747d3810ff9SJared McNeill } 1748d3810ff9SJared McNeill 1749d3810ff9SJared McNeill /* Attach ethernet interface */ 1750d3810ff9SJared McNeill ether_ifattach(sc->ifp, eaddr); 1751d3810ff9SJared McNeill 1752d3810ff9SJared McNeill return (0); 1753d3810ff9SJared McNeill } 1754d3810ff9SJared McNeill 1755d3810ff9SJared McNeill static device_method_t awg_methods[] = { 1756d3810ff9SJared McNeill /* Device interface */ 1757d3810ff9SJared McNeill DEVMETHOD(device_probe, awg_probe), 1758d3810ff9SJared McNeill DEVMETHOD(device_attach, awg_attach), 1759d3810ff9SJared McNeill 1760d3810ff9SJared McNeill /* MII interface */ 1761d3810ff9SJared McNeill DEVMETHOD(miibus_readreg, awg_miibus_readreg), 1762d3810ff9SJared McNeill DEVMETHOD(miibus_writereg, awg_miibus_writereg), 1763d3810ff9SJared McNeill DEVMETHOD(miibus_statchg, awg_miibus_statchg), 1764d3810ff9SJared McNeill 1765d3810ff9SJared McNeill DEVMETHOD_END 1766d3810ff9SJared McNeill }; 1767d3810ff9SJared McNeill 1768d3810ff9SJared McNeill static driver_t awg_driver = { 1769d3810ff9SJared McNeill "awg", 1770d3810ff9SJared McNeill awg_methods, 1771d3810ff9SJared McNeill sizeof(struct awg_softc), 1772d3810ff9SJared McNeill }; 1773d3810ff9SJared McNeill 1774d3810ff9SJared McNeill static devclass_t awg_devclass; 1775d3810ff9SJared McNeill 1776d3810ff9SJared McNeill DRIVER_MODULE(awg, simplebus, awg_driver, awg_devclass, 0, 0); 1777d3810ff9SJared McNeill DRIVER_MODULE(miibus, awg, miibus_driver, miibus_devclass, 0, 0); 1778d3810ff9SJared McNeill 1779d3810ff9SJared McNeill MODULE_DEPEND(awg, ether, 1, 1, 1); 1780d3810ff9SJared McNeill MODULE_DEPEND(awg, miibus, 1, 1, 1); 1781