1d3810ff9SJared McNeill /*- 2d3810ff9SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3d3810ff9SJared McNeill * 4d3810ff9SJared McNeill * Redistribution and use in source and binary forms, with or without 5d3810ff9SJared McNeill * modification, are permitted provided that the following conditions 6d3810ff9SJared McNeill * are met: 7d3810ff9SJared McNeill * 1. Redistributions of source code must retain the above copyright 8d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer. 9d3810ff9SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright 10d3810ff9SJared McNeill * notice, this list of conditions and the following disclaimer in the 11d3810ff9SJared McNeill * documentation and/or other materials provided with the distribution. 12d3810ff9SJared McNeill * 13d3810ff9SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14d3810ff9SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15d3810ff9SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16d3810ff9SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17d3810ff9SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18d3810ff9SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19d3810ff9SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20d3810ff9SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21d3810ff9SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22d3810ff9SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23d3810ff9SJared McNeill * SUCH DAMAGE. 24d3810ff9SJared McNeill * 25d3810ff9SJared McNeill * $FreeBSD$ 26d3810ff9SJared McNeill */ 27d3810ff9SJared McNeill 28d3810ff9SJared McNeill /* 29d3810ff9SJared McNeill * Allwinner Gigabit Ethernet MAC (EMAC) controller 30d3810ff9SJared McNeill */ 31d3810ff9SJared McNeill 3216928528SJared McNeill #include "opt_device_polling.h" 3316928528SJared McNeill 34d3810ff9SJared McNeill #include <sys/cdefs.h> 35d3810ff9SJared McNeill __FBSDID("$FreeBSD$"); 36d3810ff9SJared McNeill 37d3810ff9SJared McNeill #include <sys/param.h> 38d3810ff9SJared McNeill #include <sys/systm.h> 39d3810ff9SJared McNeill #include <sys/bus.h> 40d3810ff9SJared McNeill #include <sys/rman.h> 41d3810ff9SJared McNeill #include <sys/kernel.h> 42d3810ff9SJared McNeill #include <sys/endian.h> 43d3810ff9SJared McNeill #include <sys/mbuf.h> 44d3810ff9SJared McNeill #include <sys/socket.h> 45d3810ff9SJared McNeill #include <sys/sockio.h> 46d3810ff9SJared McNeill #include <sys/module.h> 4701a469b8SJared McNeill #include <sys/gpio.h> 48d3810ff9SJared McNeill 49d3810ff9SJared McNeill #include <net/bpf.h> 50d3810ff9SJared McNeill #include <net/if.h> 51d3810ff9SJared McNeill #include <net/ethernet.h> 52d3810ff9SJared McNeill #include <net/if_dl.h> 53d3810ff9SJared McNeill #include <net/if_media.h> 54d3810ff9SJared McNeill #include <net/if_types.h> 55d3810ff9SJared McNeill #include <net/if_var.h> 56d3810ff9SJared McNeill 57d3810ff9SJared McNeill #include <machine/bus.h> 58d3810ff9SJared McNeill 59d3810ff9SJared McNeill #include <dev/ofw/ofw_bus.h> 60d3810ff9SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 61d3810ff9SJared McNeill 62d3810ff9SJared McNeill #include <arm/allwinner/if_awgreg.h> 631403e695SJared McNeill #include <arm/allwinner/aw_sid.h> 64d3810ff9SJared McNeill #include <dev/mii/mii.h> 65d3810ff9SJared McNeill #include <dev/mii/miivar.h> 66d3810ff9SJared McNeill 67d3810ff9SJared McNeill #include <dev/extres/clk/clk.h> 68d3810ff9SJared McNeill #include <dev/extres/hwreset/hwreset.h> 69d3810ff9SJared McNeill #include <dev/extres/regulator/regulator.h> 702defb358SKyle Evans #include <dev/extres/syscon/syscon.h> 71d3810ff9SJared McNeill 722defb358SKyle Evans #include "syscon_if.h" 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 */ 1082defb358SKyle Evans #define EMAC_CLK_REG 0x30 10901a469b8SJared McNeill #define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */ 11001a469b8SJared McNeill #define EMAC_CLK_EPHY_ADDR_SHIFT 20 11101a469b8SJared McNeill #define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */ 11201a469b8SJared McNeill #define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */ 11301a469b8SJared McNeill #define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */ 11401a469b8SJared McNeill #define EMAC_CLK_RMII_EN (1 << 13) 11501a469b8SJared McNeill #define EMAC_CLK_ETXDC (0x7 << 10) 11601a469b8SJared McNeill #define EMAC_CLK_ETXDC_SHIFT 10 11701a469b8SJared McNeill #define EMAC_CLK_ERXDC (0x1f << 5) 11801a469b8SJared McNeill #define EMAC_CLK_ERXDC_SHIFT 5 11901a469b8SJared McNeill #define EMAC_CLK_PIT (0x1 << 2) 12001a469b8SJared McNeill #define EMAC_CLK_PIT_MII (0 << 2) 12101a469b8SJared McNeill #define EMAC_CLK_PIT_RGMII (1 << 2) 12201a469b8SJared McNeill #define EMAC_CLK_SRC (0x3 << 0) 12301a469b8SJared McNeill #define EMAC_CLK_SRC_MII (0 << 0) 12401a469b8SJared McNeill #define EMAC_CLK_SRC_EXT_RGMII (1 << 0) 12501a469b8SJared McNeill #define EMAC_CLK_SRC_RGMII (2 << 0) 12601a469b8SJared McNeill 127d3810ff9SJared McNeill /* Burst length of RX and TX DMA transfers */ 128d3810ff9SJared McNeill static int awg_burst_len = BURST_LEN_DEFAULT; 129d3810ff9SJared McNeill TUNABLE_INT("hw.awg.burst_len", &awg_burst_len); 130d3810ff9SJared McNeill 131d3810ff9SJared McNeill /* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */ 132d3810ff9SJared McNeill static int awg_rx_tx_pri = RX_TX_PRI_DEFAULT; 133d3810ff9SJared McNeill TUNABLE_INT("hw.awg.rx_tx_pri", &awg_rx_tx_pri); 134d3810ff9SJared McNeill 135d3810ff9SJared McNeill /* Pause time field in the transmitted control frame */ 136d3810ff9SJared McNeill static int awg_pause_time = PAUSE_TIME_DEFAULT; 137d3810ff9SJared McNeill TUNABLE_INT("hw.awg.pause_time", &awg_pause_time); 138d3810ff9SJared McNeill 139d3810ff9SJared McNeill /* Request a TX interrupt every <n> descriptors */ 140d3810ff9SJared McNeill static int awg_tx_interval = TX_INTERVAL_DEFAULT; 141d3810ff9SJared McNeill TUNABLE_INT("hw.awg.tx_interval", &awg_tx_interval); 142d3810ff9SJared McNeill 14316928528SJared McNeill /* Maximum number of mbufs to send to if_input */ 14416928528SJared McNeill static int awg_rx_batch = RX_BATCH_DEFAULT; 14516928528SJared McNeill TUNABLE_INT("hw.awg.rx_batch", &awg_rx_batch); 14616928528SJared McNeill 14701a469b8SJared McNeill enum awg_type { 14801a469b8SJared McNeill EMAC_A83T = 1, 14901a469b8SJared McNeill EMAC_H3, 15050bb2d50SEmmanuel Vadot EMAC_A64, 15101a469b8SJared McNeill }; 15201a469b8SJared McNeill 153d3810ff9SJared McNeill static struct ofw_compat_data compat_data[] = { 15401a469b8SJared McNeill { "allwinner,sun8i-a83t-emac", EMAC_A83T }, 15501a469b8SJared McNeill { "allwinner,sun8i-h3-emac", EMAC_H3 }, 15650bb2d50SEmmanuel Vadot { "allwinner,sun50i-a64-emac", EMAC_A64 }, 157d3810ff9SJared McNeill { NULL, 0 } 158d3810ff9SJared McNeill }; 159d3810ff9SJared McNeill 160d3810ff9SJared McNeill struct awg_bufmap { 161d3810ff9SJared McNeill bus_dmamap_t map; 162d3810ff9SJared McNeill struct mbuf *mbuf; 163d3810ff9SJared McNeill }; 164d3810ff9SJared McNeill 165d3810ff9SJared McNeill struct awg_txring { 166d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 167d3810ff9SJared McNeill bus_dmamap_t desc_map; 168d3810ff9SJared McNeill struct emac_desc *desc_ring; 169d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 170d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 171d3810ff9SJared McNeill struct awg_bufmap buf_map[TX_DESC_COUNT]; 172d3810ff9SJared McNeill u_int cur, next, queued; 1731ee5a3d3SEmmanuel Vadot u_int segs; 174d3810ff9SJared McNeill }; 175d3810ff9SJared McNeill 176d3810ff9SJared McNeill struct awg_rxring { 177d3810ff9SJared McNeill bus_dma_tag_t desc_tag; 178d3810ff9SJared McNeill bus_dmamap_t desc_map; 179d3810ff9SJared McNeill struct emac_desc *desc_ring; 180d3810ff9SJared McNeill bus_addr_t desc_ring_paddr; 181d3810ff9SJared McNeill bus_dma_tag_t buf_tag; 182d3810ff9SJared McNeill struct awg_bufmap buf_map[RX_DESC_COUNT]; 183bd906329SEmmanuel Vadot bus_dmamap_t buf_spare_map; 184d3810ff9SJared McNeill u_int cur; 185d3810ff9SJared McNeill }; 186d3810ff9SJared McNeill 18701a469b8SJared McNeill enum { 18801a469b8SJared McNeill _RES_EMAC, 18901a469b8SJared McNeill _RES_IRQ, 19001a469b8SJared McNeill _RES_SYSCON, 19101a469b8SJared McNeill _RES_NITEMS 19201a469b8SJared McNeill }; 19301a469b8SJared McNeill 194d3810ff9SJared McNeill struct awg_softc { 19501a469b8SJared McNeill struct resource *res[_RES_NITEMS]; 196d3810ff9SJared McNeill struct mtx mtx; 197d3810ff9SJared McNeill if_t ifp; 198031d5777SOleksandr Tymoshenko device_t dev; 199d3810ff9SJared McNeill device_t miibus; 200d3810ff9SJared McNeill struct callout stat_ch; 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; 2062defb358SKyle Evans struct syscon *syscon; 207d3810ff9SJared McNeill 208d3810ff9SJared McNeill struct awg_txring tx; 209d3810ff9SJared McNeill struct awg_rxring rx; 210d3810ff9SJared McNeill }; 211d3810ff9SJared McNeill 212d3810ff9SJared McNeill static struct resource_spec awg_spec[] = { 213d3810ff9SJared McNeill { SYS_RES_MEMORY, 0, RF_ACTIVE }, 214d3810ff9SJared McNeill { SYS_RES_IRQ, 0, RF_ACTIVE }, 21501a469b8SJared McNeill { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, 216d3810ff9SJared McNeill { -1, 0 } 217d3810ff9SJared McNeill }; 218d3810ff9SJared McNeill 2193f9ade06SEmmanuel Vadot static void awg_txeof(struct awg_softc *sc); 220*5fba9064SEmmanuel Vadot static void awg_start_locked(struct awg_softc *sc); 221*5fba9064SEmmanuel Vadot 222*5fba9064SEmmanuel Vadot static void awg_tick(void *softc); 2233f9ade06SEmmanuel Vadot 2249a77a643SKyle Evans static int awg_parse_delay(device_t dev, uint32_t *tx_delay, 2259a77a643SKyle Evans uint32_t *rx_delay); 2262defb358SKyle Evans static uint32_t syscon_read_emac_clk_reg(device_t dev); 2272defb358SKyle Evans static void syscon_write_emac_clk_reg(device_t dev, uint32_t val); 228767754e5SKyle Evans static phandle_t awg_get_phy_node(device_t dev); 229767754e5SKyle Evans static bool awg_has_internal_phy(device_t dev); 2302defb358SKyle Evans 231*5fba9064SEmmanuel Vadot /* 232*5fba9064SEmmanuel Vadot * MII functions 233*5fba9064SEmmanuel Vadot */ 234*5fba9064SEmmanuel Vadot 235d3810ff9SJared McNeill static int 236d3810ff9SJared McNeill awg_miibus_readreg(device_t dev, int phy, int reg) 237d3810ff9SJared McNeill { 238d3810ff9SJared McNeill struct awg_softc *sc; 239d3810ff9SJared McNeill int retry, val; 240d3810ff9SJared McNeill 241d3810ff9SJared McNeill sc = device_get_softc(dev); 242d3810ff9SJared McNeill val = 0; 243d3810ff9SJared McNeill 244d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 245d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 246d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 247d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 248d3810ff9SJared McNeill MII_BUSY); 249d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 250d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) { 251d3810ff9SJared McNeill val = RD4(sc, EMAC_MII_DATA); 252d3810ff9SJared McNeill break; 253d3810ff9SJared McNeill } 254d3810ff9SJared McNeill DELAY(10); 255d3810ff9SJared McNeill } 256d3810ff9SJared McNeill 257d3810ff9SJared McNeill if (retry == 0) 258d3810ff9SJared McNeill device_printf(dev, "phy read timeout, phy=%d reg=%d\n", 259d3810ff9SJared McNeill phy, reg); 260d3810ff9SJared McNeill 261d3810ff9SJared McNeill return (val); 262d3810ff9SJared McNeill } 263d3810ff9SJared McNeill 264d3810ff9SJared McNeill static int 265d3810ff9SJared McNeill awg_miibus_writereg(device_t dev, int phy, int reg, int val) 266d3810ff9SJared McNeill { 267d3810ff9SJared McNeill struct awg_softc *sc; 268d3810ff9SJared McNeill int retry; 269d3810ff9SJared McNeill 270d3810ff9SJared McNeill sc = device_get_softc(dev); 271d3810ff9SJared McNeill 272d3810ff9SJared McNeill WR4(sc, EMAC_MII_DATA, val); 273d3810ff9SJared McNeill WR4(sc, EMAC_MII_CMD, 274d3810ff9SJared McNeill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) | 275d3810ff9SJared McNeill (phy << PHY_ADDR_SHIFT) | 276d3810ff9SJared McNeill (reg << PHY_REG_ADDR_SHIFT) | 277d3810ff9SJared McNeill MII_WR | MII_BUSY); 278d3810ff9SJared McNeill for (retry = MII_BUSY_RETRY; retry > 0; retry--) { 279d3810ff9SJared McNeill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) 280d3810ff9SJared McNeill break; 281d3810ff9SJared McNeill DELAY(10); 282d3810ff9SJared McNeill } 283d3810ff9SJared McNeill 284d3810ff9SJared McNeill if (retry == 0) 285d3810ff9SJared McNeill device_printf(dev, "phy write timeout, phy=%d reg=%d\n", 286d3810ff9SJared McNeill phy, reg); 287d3810ff9SJared McNeill 288d3810ff9SJared McNeill return (0); 289d3810ff9SJared McNeill } 290d3810ff9SJared McNeill 291d3810ff9SJared McNeill static void 292e6579433SEmmanuel Vadot awg_miibus_statchg(device_t dev) 293d3810ff9SJared McNeill { 294e6579433SEmmanuel Vadot struct awg_softc *sc; 295d3810ff9SJared McNeill struct mii_data *mii; 296d3810ff9SJared McNeill uint32_t val; 297d3810ff9SJared McNeill 298e6579433SEmmanuel Vadot sc = device_get_softc(dev); 299e6579433SEmmanuel Vadot 300d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 301d3810ff9SJared McNeill 302d3810ff9SJared McNeill if ((if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) == 0) 303d3810ff9SJared McNeill return; 304d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 305d3810ff9SJared McNeill 306d3810ff9SJared McNeill if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 307d3810ff9SJared McNeill (IFM_ACTIVE | IFM_AVALID)) { 308d3810ff9SJared McNeill switch (IFM_SUBTYPE(mii->mii_media_active)) { 309d3810ff9SJared McNeill case IFM_1000_T: 310d3810ff9SJared McNeill case IFM_1000_SX: 311d3810ff9SJared McNeill case IFM_100_TX: 312d3810ff9SJared McNeill case IFM_10_T: 313d3810ff9SJared McNeill sc->link = 1; 314d3810ff9SJared McNeill break; 315d3810ff9SJared McNeill default: 316d3810ff9SJared McNeill sc->link = 0; 317d3810ff9SJared McNeill break; 318d3810ff9SJared McNeill } 319d3810ff9SJared McNeill } else 320d3810ff9SJared McNeill sc->link = 0; 321d3810ff9SJared McNeill 322d3810ff9SJared McNeill if (sc->link == 0) 323d3810ff9SJared McNeill return; 324d3810ff9SJared McNeill 325d3810ff9SJared McNeill val = RD4(sc, EMAC_BASIC_CTL_0); 326d3810ff9SJared McNeill val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX); 327d3810ff9SJared McNeill 328d3810ff9SJared McNeill if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || 329d3810ff9SJared McNeill IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) 330d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT; 331d3810ff9SJared McNeill else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 332d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT; 333d3810ff9SJared McNeill else 334d3810ff9SJared McNeill val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT; 335d3810ff9SJared McNeill 336d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 337d3810ff9SJared McNeill val |= BASIC_CTL_DUPLEX; 338d3810ff9SJared McNeill 339d3810ff9SJared McNeill WR4(sc, EMAC_BASIC_CTL_0, val); 340d3810ff9SJared McNeill 341d3810ff9SJared McNeill val = RD4(sc, EMAC_RX_CTL_0); 342d3810ff9SJared McNeill val &= ~RX_FLOW_CTL_EN; 343d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 344d3810ff9SJared McNeill val |= RX_FLOW_CTL_EN; 345d3810ff9SJared McNeill WR4(sc, EMAC_RX_CTL_0, val); 346d3810ff9SJared McNeill 347d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_FLOW_CTL); 348d3810ff9SJared McNeill val &= ~(PAUSE_TIME|TX_FLOW_CTL_EN); 349d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 350d3810ff9SJared McNeill val |= TX_FLOW_CTL_EN; 351d3810ff9SJared McNeill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 352d3810ff9SJared McNeill val |= awg_pause_time << PAUSE_TIME_SHIFT; 353d3810ff9SJared McNeill WR4(sc, EMAC_TX_FLOW_CTL, val); 354d3810ff9SJared McNeill } 355d3810ff9SJared McNeill 356*5fba9064SEmmanuel Vadot /* 357*5fba9064SEmmanuel Vadot * Media functions 358*5fba9064SEmmanuel Vadot */ 359*5fba9064SEmmanuel Vadot 360d3810ff9SJared McNeill static void 361d3810ff9SJared McNeill awg_media_status(if_t ifp, struct ifmediareq *ifmr) 362d3810ff9SJared McNeill { 363d3810ff9SJared McNeill struct awg_softc *sc; 364d3810ff9SJared McNeill struct mii_data *mii; 365d3810ff9SJared McNeill 366d3810ff9SJared McNeill sc = if_getsoftc(ifp); 367d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 368d3810ff9SJared McNeill 369d3810ff9SJared McNeill AWG_LOCK(sc); 370d3810ff9SJared McNeill mii_pollstat(mii); 371d3810ff9SJared McNeill ifmr->ifm_active = mii->mii_media_active; 372d3810ff9SJared McNeill ifmr->ifm_status = mii->mii_media_status; 373d3810ff9SJared McNeill AWG_UNLOCK(sc); 374d3810ff9SJared McNeill } 375d3810ff9SJared McNeill 376d3810ff9SJared McNeill static int 377d3810ff9SJared McNeill awg_media_change(if_t ifp) 378d3810ff9SJared McNeill { 379d3810ff9SJared McNeill struct awg_softc *sc; 380d3810ff9SJared McNeill struct mii_data *mii; 381d3810ff9SJared McNeill int error; 382d3810ff9SJared McNeill 383d3810ff9SJared McNeill sc = if_getsoftc(ifp); 384d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 385d3810ff9SJared McNeill 386d3810ff9SJared McNeill AWG_LOCK(sc); 387d3810ff9SJared McNeill error = mii_mediachg(mii); 388d3810ff9SJared McNeill AWG_UNLOCK(sc); 389d3810ff9SJared McNeill 390d3810ff9SJared McNeill return (error); 391d3810ff9SJared McNeill } 392d3810ff9SJared McNeill 393*5fba9064SEmmanuel Vadot /* 394*5fba9064SEmmanuel Vadot * Core functions 395*5fba9064SEmmanuel Vadot */ 396*5fba9064SEmmanuel Vadot 397*5fba9064SEmmanuel Vadot /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */ 398*5fba9064SEmmanuel Vadot static uint32_t 399*5fba9064SEmmanuel Vadot bitrev32(uint32_t x) 400*5fba9064SEmmanuel Vadot { 401*5fba9064SEmmanuel Vadot x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); 402*5fba9064SEmmanuel Vadot x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); 403*5fba9064SEmmanuel Vadot x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); 404*5fba9064SEmmanuel Vadot x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); 405*5fba9064SEmmanuel Vadot 406*5fba9064SEmmanuel Vadot return (x >> 16) | (x << 16); 407*5fba9064SEmmanuel Vadot } 408*5fba9064SEmmanuel Vadot 409*5fba9064SEmmanuel Vadot static u_int 410*5fba9064SEmmanuel Vadot awg_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 411*5fba9064SEmmanuel Vadot { 412*5fba9064SEmmanuel Vadot uint32_t crc, hashreg, hashbit, *hash = arg; 413*5fba9064SEmmanuel Vadot 414*5fba9064SEmmanuel Vadot crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN) & 0x7f; 415*5fba9064SEmmanuel Vadot crc = bitrev32(~crc) >> 26; 416*5fba9064SEmmanuel Vadot hashreg = (crc >> 5); 417*5fba9064SEmmanuel Vadot hashbit = (crc & 0x1f); 418*5fba9064SEmmanuel Vadot hash[hashreg] |= (1 << hashbit); 419*5fba9064SEmmanuel Vadot 420*5fba9064SEmmanuel Vadot return (1); 421*5fba9064SEmmanuel Vadot } 422*5fba9064SEmmanuel Vadot 423*5fba9064SEmmanuel Vadot static void 424*5fba9064SEmmanuel Vadot awg_setup_rxfilter(struct awg_softc *sc) 425*5fba9064SEmmanuel Vadot { 426*5fba9064SEmmanuel Vadot uint32_t val, hash[2], machi, maclo; 427*5fba9064SEmmanuel Vadot uint8_t *eaddr; 428*5fba9064SEmmanuel Vadot if_t ifp; 429*5fba9064SEmmanuel Vadot 430*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 431*5fba9064SEmmanuel Vadot 432*5fba9064SEmmanuel Vadot ifp = sc->ifp; 433*5fba9064SEmmanuel Vadot val = 0; 434*5fba9064SEmmanuel Vadot hash[0] = hash[1] = 0; 435*5fba9064SEmmanuel Vadot 436*5fba9064SEmmanuel Vadot if (if_getflags(ifp) & IFF_PROMISC) 437*5fba9064SEmmanuel Vadot val |= DIS_ADDR_FILTER; 438*5fba9064SEmmanuel Vadot else if (if_getflags(ifp) & IFF_ALLMULTI) { 439*5fba9064SEmmanuel Vadot val |= RX_ALL_MULTICAST; 440*5fba9064SEmmanuel Vadot hash[0] = hash[1] = ~0; 441*5fba9064SEmmanuel Vadot } else if (if_foreach_llmaddr(ifp, awg_hash_maddr, hash) > 0) 442*5fba9064SEmmanuel Vadot val |= HASH_MULTICAST; 443*5fba9064SEmmanuel Vadot 444*5fba9064SEmmanuel Vadot /* Write our unicast address */ 445*5fba9064SEmmanuel Vadot eaddr = IF_LLADDR(ifp); 446*5fba9064SEmmanuel Vadot machi = (eaddr[5] << 8) | eaddr[4]; 447*5fba9064SEmmanuel Vadot maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | 448*5fba9064SEmmanuel Vadot (eaddr[0] << 0); 449*5fba9064SEmmanuel Vadot WR4(sc, EMAC_ADDR_HIGH(0), machi); 450*5fba9064SEmmanuel Vadot WR4(sc, EMAC_ADDR_LOW(0), maclo); 451*5fba9064SEmmanuel Vadot 452*5fba9064SEmmanuel Vadot /* Multicast hash filters */ 453*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_HASH_0, hash[1]); 454*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_HASH_1, hash[0]); 455*5fba9064SEmmanuel Vadot 456*5fba9064SEmmanuel Vadot /* RX frame filter config */ 457*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_FRM_FLT, val); 458*5fba9064SEmmanuel Vadot } 459*5fba9064SEmmanuel Vadot 460*5fba9064SEmmanuel Vadot static void 461*5fba9064SEmmanuel Vadot awg_setup_core(struct awg_softc *sc) 462*5fba9064SEmmanuel Vadot { 463*5fba9064SEmmanuel Vadot uint32_t val; 464*5fba9064SEmmanuel Vadot 465*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 466*5fba9064SEmmanuel Vadot /* Configure DMA burst length and priorities */ 467*5fba9064SEmmanuel Vadot val = awg_burst_len << BASIC_CTL_BURST_LEN_SHIFT; 468*5fba9064SEmmanuel Vadot if (awg_rx_tx_pri) 469*5fba9064SEmmanuel Vadot val |= BASIC_CTL_RX_TX_PRI; 470*5fba9064SEmmanuel Vadot WR4(sc, EMAC_BASIC_CTL_1, val); 471*5fba9064SEmmanuel Vadot 472*5fba9064SEmmanuel Vadot } 473*5fba9064SEmmanuel Vadot 474*5fba9064SEmmanuel Vadot static void 475*5fba9064SEmmanuel Vadot awg_enable_mac(struct awg_softc *sc, bool enable) 476*5fba9064SEmmanuel Vadot { 477*5fba9064SEmmanuel Vadot uint32_t tx, rx; 478*5fba9064SEmmanuel Vadot 479*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 480*5fba9064SEmmanuel Vadot 481*5fba9064SEmmanuel Vadot tx = RD4(sc, EMAC_TX_CTL_0); 482*5fba9064SEmmanuel Vadot rx = RD4(sc, EMAC_RX_CTL_0); 483*5fba9064SEmmanuel Vadot if (enable) { 484*5fba9064SEmmanuel Vadot tx |= TX_EN; 485*5fba9064SEmmanuel Vadot rx |= RX_EN | CHECK_CRC; 486*5fba9064SEmmanuel Vadot } else { 487*5fba9064SEmmanuel Vadot tx &= ~TX_EN; 488*5fba9064SEmmanuel Vadot rx &= ~(RX_EN | CHECK_CRC); 489*5fba9064SEmmanuel Vadot } 490*5fba9064SEmmanuel Vadot 491*5fba9064SEmmanuel Vadot WR4(sc, EMAC_TX_CTL_0, tx); 492*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_CTL_0, rx); 493*5fba9064SEmmanuel Vadot } 494*5fba9064SEmmanuel Vadot 495*5fba9064SEmmanuel Vadot static void 496*5fba9064SEmmanuel Vadot awg_get_eaddr(device_t dev, uint8_t *eaddr) 497*5fba9064SEmmanuel Vadot { 498*5fba9064SEmmanuel Vadot struct awg_softc *sc; 499*5fba9064SEmmanuel Vadot uint32_t maclo, machi, rnd; 500*5fba9064SEmmanuel Vadot u_char rootkey[16]; 501*5fba9064SEmmanuel Vadot uint32_t rootkey_size; 502*5fba9064SEmmanuel Vadot 503*5fba9064SEmmanuel Vadot sc = device_get_softc(dev); 504*5fba9064SEmmanuel Vadot 505*5fba9064SEmmanuel Vadot machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff; 506*5fba9064SEmmanuel Vadot maclo = RD4(sc, EMAC_ADDR_LOW(0)); 507*5fba9064SEmmanuel Vadot 508*5fba9064SEmmanuel Vadot rootkey_size = sizeof(rootkey); 509*5fba9064SEmmanuel Vadot if (maclo == 0xffffffff && machi == 0xffff) { 510*5fba9064SEmmanuel Vadot /* MAC address in hardware is invalid, create one */ 511*5fba9064SEmmanuel Vadot if (aw_sid_get_fuse(AW_SID_FUSE_ROOTKEY, rootkey, 512*5fba9064SEmmanuel Vadot &rootkey_size) == 0 && 513*5fba9064SEmmanuel Vadot (rootkey[3] | rootkey[12] | rootkey[13] | rootkey[14] | 514*5fba9064SEmmanuel Vadot rootkey[15]) != 0) { 515*5fba9064SEmmanuel Vadot /* MAC address is derived from the root key in SID */ 516*5fba9064SEmmanuel Vadot maclo = (rootkey[13] << 24) | (rootkey[12] << 16) | 517*5fba9064SEmmanuel Vadot (rootkey[3] << 8) | 0x02; 518*5fba9064SEmmanuel Vadot machi = (rootkey[15] << 8) | rootkey[14]; 519*5fba9064SEmmanuel Vadot } else { 520*5fba9064SEmmanuel Vadot /* Create one */ 521*5fba9064SEmmanuel Vadot rnd = arc4random(); 522*5fba9064SEmmanuel Vadot maclo = 0x00f2 | (rnd & 0xffff0000); 523*5fba9064SEmmanuel Vadot machi = rnd & 0xffff; 524*5fba9064SEmmanuel Vadot } 525*5fba9064SEmmanuel Vadot } 526*5fba9064SEmmanuel Vadot 527*5fba9064SEmmanuel Vadot eaddr[0] = maclo & 0xff; 528*5fba9064SEmmanuel Vadot eaddr[1] = (maclo >> 8) & 0xff; 529*5fba9064SEmmanuel Vadot eaddr[2] = (maclo >> 16) & 0xff; 530*5fba9064SEmmanuel Vadot eaddr[3] = (maclo >> 24) & 0xff; 531*5fba9064SEmmanuel Vadot eaddr[4] = machi & 0xff; 532*5fba9064SEmmanuel Vadot eaddr[5] = (machi >> 8) & 0xff; 533*5fba9064SEmmanuel Vadot } 534*5fba9064SEmmanuel Vadot 535*5fba9064SEmmanuel Vadot /* 536*5fba9064SEmmanuel Vadot * DMA functions 537*5fba9064SEmmanuel Vadot */ 538*5fba9064SEmmanuel Vadot 539*5fba9064SEmmanuel Vadot static void 540*5fba9064SEmmanuel Vadot awg_enable_dma_intr(struct awg_softc *sc) 541*5fba9064SEmmanuel Vadot { 542*5fba9064SEmmanuel Vadot /* Enable interrupts */ 543*5fba9064SEmmanuel Vadot WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN); 544*5fba9064SEmmanuel Vadot } 545*5fba9064SEmmanuel Vadot 546*5fba9064SEmmanuel Vadot static void 547*5fba9064SEmmanuel Vadot awg_disable_dma_intr(struct awg_softc *sc) 548*5fba9064SEmmanuel Vadot { 549*5fba9064SEmmanuel Vadot /* Disable interrupts */ 550*5fba9064SEmmanuel Vadot WR4(sc, EMAC_INT_EN, 0); 551*5fba9064SEmmanuel Vadot } 552*5fba9064SEmmanuel Vadot 553*5fba9064SEmmanuel Vadot static void 554*5fba9064SEmmanuel Vadot awg_init_dma(struct awg_softc *sc) 555*5fba9064SEmmanuel Vadot { 556*5fba9064SEmmanuel Vadot uint32_t val; 557*5fba9064SEmmanuel Vadot 558*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 559*5fba9064SEmmanuel Vadot 560*5fba9064SEmmanuel Vadot /* Enable interrupts */ 561*5fba9064SEmmanuel Vadot #ifdef DEVICE_POLLING 562*5fba9064SEmmanuel Vadot if ((if_getcapenable(sc->ifp) & IFCAP_POLLING) == 0) 563*5fba9064SEmmanuel Vadot awg_enable_dma_intr(sc); 564*5fba9064SEmmanuel Vadot else 565*5fba9064SEmmanuel Vadot awg_disable_dma_intr(sc); 566*5fba9064SEmmanuel Vadot #else 567*5fba9064SEmmanuel Vadot awg_enable_dma_intr(sc); 568*5fba9064SEmmanuel Vadot #endif 569*5fba9064SEmmanuel Vadot 570*5fba9064SEmmanuel Vadot /* Enable transmit DMA */ 571*5fba9064SEmmanuel Vadot val = RD4(sc, EMAC_TX_CTL_1); 572*5fba9064SEmmanuel Vadot WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD | TX_NEXT_FRAME); 573*5fba9064SEmmanuel Vadot 574*5fba9064SEmmanuel Vadot /* Enable receive DMA */ 575*5fba9064SEmmanuel Vadot val = RD4(sc, EMAC_RX_CTL_1); 576*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD); 577*5fba9064SEmmanuel Vadot } 578*5fba9064SEmmanuel Vadot 579*5fba9064SEmmanuel Vadot static void 580*5fba9064SEmmanuel Vadot awg_stop_dma(struct awg_softc *sc) 581*5fba9064SEmmanuel Vadot { 582*5fba9064SEmmanuel Vadot uint32_t val; 583*5fba9064SEmmanuel Vadot 584*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 585*5fba9064SEmmanuel Vadot 586*5fba9064SEmmanuel Vadot /* Stop transmit DMA and flush data in the TX FIFO */ 587*5fba9064SEmmanuel Vadot val = RD4(sc, EMAC_TX_CTL_1); 588*5fba9064SEmmanuel Vadot val &= ~TX_DMA_EN; 589*5fba9064SEmmanuel Vadot val |= FLUSH_TX_FIFO; 590*5fba9064SEmmanuel Vadot WR4(sc, EMAC_TX_CTL_1, val); 591*5fba9064SEmmanuel Vadot 592*5fba9064SEmmanuel Vadot /* Disable interrupts */ 593*5fba9064SEmmanuel Vadot awg_disable_dma_intr(sc); 594*5fba9064SEmmanuel Vadot 595*5fba9064SEmmanuel Vadot /* Disable transmit DMA */ 596*5fba9064SEmmanuel Vadot val = RD4(sc, EMAC_TX_CTL_1); 597*5fba9064SEmmanuel Vadot WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN); 598*5fba9064SEmmanuel Vadot 599*5fba9064SEmmanuel Vadot /* Disable receive DMA */ 600*5fba9064SEmmanuel Vadot val = RD4(sc, EMAC_RX_CTL_1); 601*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN); 602*5fba9064SEmmanuel Vadot } 603*5fba9064SEmmanuel Vadot 604d3810ff9SJared McNeill static int 605337c6940SEmmanuel Vadot awg_encap(struct awg_softc *sc, struct mbuf **mp) 606d3810ff9SJared McNeill { 607fce9d29fSEmmanuel Vadot bus_dmamap_t map; 608d3810ff9SJared McNeill bus_dma_segment_t segs[TX_MAX_SEGS]; 609fce9d29fSEmmanuel Vadot int error, nsegs, cur, first, last, i; 610d3810ff9SJared McNeill u_int csum_flags; 611c6110e75SEmmanuel Vadot uint32_t flags, status; 612d3810ff9SJared McNeill struct mbuf *m; 613d3810ff9SJared McNeill 614337c6940SEmmanuel Vadot cur = first = sc->tx.cur; 615fce9d29fSEmmanuel Vadot map = sc->tx.buf_map[first].map; 616c6110e75SEmmanuel Vadot 617d3810ff9SJared McNeill m = *mp; 618fce9d29fSEmmanuel Vadot error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, segs, 619fce9d29fSEmmanuel Vadot &nsegs, BUS_DMA_NOWAIT); 620d3810ff9SJared McNeill if (error == EFBIG) { 621d3810ff9SJared McNeill m = m_collapse(m, M_NOWAIT, TX_MAX_SEGS); 622031d5777SOleksandr Tymoshenko if (m == NULL) { 623337c6940SEmmanuel Vadot device_printf(sc->dev, "awg_encap: m_collapse failed\n"); 624337c6940SEmmanuel Vadot m_freem(*mp); 625337c6940SEmmanuel Vadot *mp = NULL; 626337c6940SEmmanuel Vadot return (ENOMEM); 627031d5777SOleksandr Tymoshenko } 628d3810ff9SJared McNeill *mp = m; 629fce9d29fSEmmanuel Vadot error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag, map, m, 630fce9d29fSEmmanuel Vadot segs, &nsegs, BUS_DMA_NOWAIT); 631337c6940SEmmanuel Vadot if (error != 0) { 632337c6940SEmmanuel Vadot m_freem(*mp); 633337c6940SEmmanuel Vadot *mp = NULL; 634337c6940SEmmanuel Vadot } 635d3810ff9SJared McNeill } 636031d5777SOleksandr Tymoshenko if (error != 0) { 637337c6940SEmmanuel Vadot device_printf(sc->dev, "awg_encap: bus_dmamap_load_mbuf_sg failed\n"); 638337c6940SEmmanuel Vadot return (error); 639337c6940SEmmanuel Vadot } 640337c6940SEmmanuel Vadot if (nsegs == 0) { 641337c6940SEmmanuel Vadot m_freem(*mp); 642337c6940SEmmanuel Vadot *mp = NULL; 643337c6940SEmmanuel Vadot return (EIO); 644337c6940SEmmanuel Vadot } 645337c6940SEmmanuel Vadot 646337c6940SEmmanuel Vadot if (sc->tx.queued + nsegs > TX_DESC_COUNT) { 647337c6940SEmmanuel Vadot bus_dmamap_unload(sc->tx.buf_tag, map); 648337c6940SEmmanuel Vadot return (ENOBUFS); 649031d5777SOleksandr Tymoshenko } 650d3810ff9SJared McNeill 651fce9d29fSEmmanuel Vadot bus_dmamap_sync(sc->tx.buf_tag, map, BUS_DMASYNC_PREWRITE); 652d3810ff9SJared McNeill 653d3810ff9SJared McNeill flags = TX_FIR_DESC; 654c6110e75SEmmanuel Vadot status = 0; 655d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { 656d3810ff9SJared McNeill if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) 657d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_FULL; 658d3810ff9SJared McNeill else 659d3810ff9SJared McNeill csum_flags = TX_CHECKSUM_CTL_IP; 660d3810ff9SJared McNeill flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT); 661d3810ff9SJared McNeill } 662d3810ff9SJared McNeill 663c6110e75SEmmanuel Vadot for (i = 0; i < nsegs; i++) { 6641ee5a3d3SEmmanuel Vadot sc->tx.segs++; 6651ee5a3d3SEmmanuel Vadot if (i == nsegs - 1) { 666d3810ff9SJared McNeill flags |= TX_LAST_DESC; 6671ee5a3d3SEmmanuel Vadot /* 6681ee5a3d3SEmmanuel Vadot * Can only request TX completion 6691ee5a3d3SEmmanuel Vadot * interrupt on last descriptor. 6701ee5a3d3SEmmanuel Vadot */ 6711ee5a3d3SEmmanuel Vadot if (sc->tx.segs >= awg_tx_interval) { 6721ee5a3d3SEmmanuel Vadot sc->tx.segs = 0; 6731ee5a3d3SEmmanuel Vadot flags |= TX_INT_CTL; 6741ee5a3d3SEmmanuel Vadot } 6751ee5a3d3SEmmanuel Vadot } 676c6110e75SEmmanuel Vadot 677c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].addr = htole32((uint32_t)segs[i].ds_addr); 678c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].size = htole32(flags | segs[i].ds_len); 679c6110e75SEmmanuel Vadot sc->tx.desc_ring[cur].status = htole32(status); 680c6110e75SEmmanuel Vadot 681d3810ff9SJared McNeill flags &= ~TX_FIR_DESC; 682c6110e75SEmmanuel Vadot /* 683c6110e75SEmmanuel Vadot * Setting of the valid bit in the first descriptor is 684c6110e75SEmmanuel Vadot * deferred until the whole chain is fully set up. 685c6110e75SEmmanuel Vadot */ 686c6110e75SEmmanuel Vadot status = TX_DESC_CTL; 687c6110e75SEmmanuel Vadot 688c6110e75SEmmanuel Vadot ++sc->tx.queued; 689d3810ff9SJared McNeill cur = TX_NEXT(cur); 690d3810ff9SJared McNeill } 691d3810ff9SJared McNeill 692337c6940SEmmanuel Vadot sc->tx.cur = cur; 693337c6940SEmmanuel Vadot 694fce9d29fSEmmanuel Vadot /* Store mapping and mbuf in the last segment */ 695fce9d29fSEmmanuel Vadot last = TX_SKIP(cur, TX_DESC_COUNT - 1); 696fce9d29fSEmmanuel Vadot sc->tx.buf_map[first].map = sc->tx.buf_map[last].map; 697fce9d29fSEmmanuel Vadot sc->tx.buf_map[last].map = map; 698fce9d29fSEmmanuel Vadot sc->tx.buf_map[last].mbuf = m; 699c6110e75SEmmanuel Vadot 700c6110e75SEmmanuel Vadot /* 701c6110e75SEmmanuel Vadot * The whole mbuf chain has been DMA mapped, 702c6110e75SEmmanuel Vadot * fix the first descriptor. 703c6110e75SEmmanuel Vadot */ 704c6110e75SEmmanuel Vadot sc->tx.desc_ring[first].status = htole32(TX_DESC_CTL); 705c6110e75SEmmanuel Vadot 706337c6940SEmmanuel Vadot return (0); 707d3810ff9SJared McNeill } 708d3810ff9SJared McNeill 709d3810ff9SJared McNeill static void 710c6110e75SEmmanuel Vadot awg_clean_txbuf(struct awg_softc *sc, int index) 711c6110e75SEmmanuel Vadot { 712c6110e75SEmmanuel Vadot struct awg_bufmap *bmap; 713c6110e75SEmmanuel Vadot 714c6110e75SEmmanuel Vadot --sc->tx.queued; 715c6110e75SEmmanuel Vadot 716c6110e75SEmmanuel Vadot bmap = &sc->tx.buf_map[index]; 717c6110e75SEmmanuel Vadot if (bmap->mbuf != NULL) { 718c6110e75SEmmanuel Vadot bus_dmamap_sync(sc->tx.buf_tag, bmap->map, 719c6110e75SEmmanuel Vadot BUS_DMASYNC_POSTWRITE); 720c6110e75SEmmanuel Vadot bus_dmamap_unload(sc->tx.buf_tag, bmap->map); 721c6110e75SEmmanuel Vadot m_freem(bmap->mbuf); 722c6110e75SEmmanuel Vadot bmap->mbuf = NULL; 723c6110e75SEmmanuel Vadot } 724c6110e75SEmmanuel Vadot } 725c6110e75SEmmanuel Vadot 726c6110e75SEmmanuel Vadot static void 727d3810ff9SJared McNeill awg_setup_rxdesc(struct awg_softc *sc, int index, bus_addr_t paddr) 728d3810ff9SJared McNeill { 729d3810ff9SJared McNeill uint32_t status, size; 730d3810ff9SJared McNeill 731d3810ff9SJared McNeill status = RX_DESC_CTL; 732d3810ff9SJared McNeill size = MCLBYTES - 1; 733d3810ff9SJared McNeill 734d3810ff9SJared McNeill sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr); 735d3810ff9SJared McNeill sc->rx.desc_ring[index].size = htole32(size); 736d3810ff9SJared McNeill sc->rx.desc_ring[index].status = htole32(status); 737d3810ff9SJared McNeill } 738d3810ff9SJared McNeill 739bd906329SEmmanuel Vadot static void 740bd906329SEmmanuel Vadot awg_reuse_rxdesc(struct awg_softc *sc, int index) 741d3810ff9SJared McNeill { 742d3810ff9SJared McNeill 743bd906329SEmmanuel Vadot sc->rx.desc_ring[index].status = htole32(RX_DESC_CTL); 744bd906329SEmmanuel Vadot } 745bd906329SEmmanuel Vadot 746bd906329SEmmanuel Vadot static int 747bd906329SEmmanuel Vadot awg_newbuf_rx(struct awg_softc *sc, int index) 748bd906329SEmmanuel Vadot { 749bd906329SEmmanuel Vadot struct mbuf *m; 750bd906329SEmmanuel Vadot bus_dma_segment_t seg; 751bd906329SEmmanuel Vadot bus_dmamap_t map; 752bd906329SEmmanuel Vadot int nsegs; 753bd906329SEmmanuel Vadot 754bd906329SEmmanuel Vadot m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 755bd906329SEmmanuel Vadot if (m == NULL) 756bd906329SEmmanuel Vadot return (ENOBUFS); 757bd906329SEmmanuel Vadot 758bd906329SEmmanuel Vadot m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 759d3810ff9SJared McNeill m_adj(m, ETHER_ALIGN); 760d3810ff9SJared McNeill 761bd906329SEmmanuel Vadot if (bus_dmamap_load_mbuf_sg(sc->rx.buf_tag, sc->rx.buf_spare_map, 762bd906329SEmmanuel Vadot m, &seg, &nsegs, BUS_DMA_NOWAIT) != 0) { 763bd906329SEmmanuel Vadot m_freem(m); 764bd906329SEmmanuel Vadot return (ENOBUFS); 765bd906329SEmmanuel Vadot } 766d3810ff9SJared McNeill 767bd906329SEmmanuel Vadot if (sc->rx.buf_map[index].mbuf != NULL) { 768bd906329SEmmanuel Vadot bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 769bd906329SEmmanuel Vadot BUS_DMASYNC_POSTREAD); 770bd906329SEmmanuel Vadot bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map); 771bd906329SEmmanuel Vadot } 772bd906329SEmmanuel Vadot map = sc->rx.buf_map[index].map; 773bd906329SEmmanuel Vadot sc->rx.buf_map[index].map = sc->rx.buf_spare_map; 774bd906329SEmmanuel Vadot sc->rx.buf_spare_map = map; 775d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map, 776d3810ff9SJared McNeill BUS_DMASYNC_PREREAD); 777d3810ff9SJared McNeill 778d3810ff9SJared McNeill sc->rx.buf_map[index].mbuf = m; 779d3810ff9SJared McNeill awg_setup_rxdesc(sc, index, seg.ds_addr); 780d3810ff9SJared McNeill 781d3810ff9SJared McNeill return (0); 782d3810ff9SJared McNeill } 783d3810ff9SJared McNeill 784d3810ff9SJared McNeill static void 785*5fba9064SEmmanuel Vadot awg_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 786*5fba9064SEmmanuel Vadot { 787*5fba9064SEmmanuel Vadot if (error != 0) 788*5fba9064SEmmanuel Vadot return; 789*5fba9064SEmmanuel Vadot *(bus_addr_t *)arg = segs[0].ds_addr; 790*5fba9064SEmmanuel Vadot } 791*5fba9064SEmmanuel Vadot 792*5fba9064SEmmanuel Vadot static int 793*5fba9064SEmmanuel Vadot awg_setup_dma(device_t dev) 794*5fba9064SEmmanuel Vadot { 795*5fba9064SEmmanuel Vadot struct awg_softc *sc; 796*5fba9064SEmmanuel Vadot int error, i; 797*5fba9064SEmmanuel Vadot 798*5fba9064SEmmanuel Vadot sc = device_get_softc(dev); 799*5fba9064SEmmanuel Vadot 800*5fba9064SEmmanuel Vadot /* Setup TX ring */ 801*5fba9064SEmmanuel Vadot error = bus_dma_tag_create( 802*5fba9064SEmmanuel Vadot bus_get_dma_tag(dev), /* Parent tag */ 803*5fba9064SEmmanuel Vadot DESC_ALIGN, 0, /* alignment, boundary */ 804*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 805*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR, /* highaddr */ 806*5fba9064SEmmanuel Vadot NULL, NULL, /* filter, filterarg */ 807*5fba9064SEmmanuel Vadot TX_DESC_SIZE, 1, /* maxsize, nsegs */ 808*5fba9064SEmmanuel Vadot TX_DESC_SIZE, /* maxsegsize */ 809*5fba9064SEmmanuel Vadot 0, /* flags */ 810*5fba9064SEmmanuel Vadot NULL, NULL, /* lockfunc, lockarg */ 811*5fba9064SEmmanuel Vadot &sc->tx.desc_tag); 812*5fba9064SEmmanuel Vadot if (error != 0) { 813*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create TX descriptor ring tag\n"); 814*5fba9064SEmmanuel Vadot return (error); 815*5fba9064SEmmanuel Vadot } 816*5fba9064SEmmanuel Vadot 817*5fba9064SEmmanuel Vadot error = bus_dmamem_alloc(sc->tx.desc_tag, (void **)&sc->tx.desc_ring, 818*5fba9064SEmmanuel Vadot BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->tx.desc_map); 819*5fba9064SEmmanuel Vadot if (error != 0) { 820*5fba9064SEmmanuel Vadot device_printf(dev, "cannot allocate TX descriptor ring\n"); 821*5fba9064SEmmanuel Vadot return (error); 822*5fba9064SEmmanuel Vadot } 823*5fba9064SEmmanuel Vadot 824*5fba9064SEmmanuel Vadot error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map, 825*5fba9064SEmmanuel Vadot sc->tx.desc_ring, TX_DESC_SIZE, awg_dmamap_cb, 826*5fba9064SEmmanuel Vadot &sc->tx.desc_ring_paddr, 0); 827*5fba9064SEmmanuel Vadot if (error != 0) { 828*5fba9064SEmmanuel Vadot device_printf(dev, "cannot load TX descriptor ring\n"); 829*5fba9064SEmmanuel Vadot return (error); 830*5fba9064SEmmanuel Vadot } 831*5fba9064SEmmanuel Vadot 832*5fba9064SEmmanuel Vadot for (i = 0; i < TX_DESC_COUNT; i++) 833*5fba9064SEmmanuel Vadot sc->tx.desc_ring[i].next = 834*5fba9064SEmmanuel Vadot htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i))); 835*5fba9064SEmmanuel Vadot 836*5fba9064SEmmanuel Vadot error = bus_dma_tag_create( 837*5fba9064SEmmanuel Vadot bus_get_dma_tag(dev), /* Parent tag */ 838*5fba9064SEmmanuel Vadot 1, 0, /* alignment, boundary */ 839*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 840*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR, /* highaddr */ 841*5fba9064SEmmanuel Vadot NULL, NULL, /* filter, filterarg */ 842*5fba9064SEmmanuel Vadot MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ 843*5fba9064SEmmanuel Vadot MCLBYTES, /* maxsegsize */ 844*5fba9064SEmmanuel Vadot 0, /* flags */ 845*5fba9064SEmmanuel Vadot NULL, NULL, /* lockfunc, lockarg */ 846*5fba9064SEmmanuel Vadot &sc->tx.buf_tag); 847*5fba9064SEmmanuel Vadot if (error != 0) { 848*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create TX buffer tag\n"); 849*5fba9064SEmmanuel Vadot return (error); 850*5fba9064SEmmanuel Vadot } 851*5fba9064SEmmanuel Vadot 852*5fba9064SEmmanuel Vadot sc->tx.queued = 0; 853*5fba9064SEmmanuel Vadot for (i = 0; i < TX_DESC_COUNT; i++) { 854*5fba9064SEmmanuel Vadot error = bus_dmamap_create(sc->tx.buf_tag, 0, 855*5fba9064SEmmanuel Vadot &sc->tx.buf_map[i].map); 856*5fba9064SEmmanuel Vadot if (error != 0) { 857*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create TX buffer map\n"); 858*5fba9064SEmmanuel Vadot return (error); 859*5fba9064SEmmanuel Vadot } 860*5fba9064SEmmanuel Vadot } 861*5fba9064SEmmanuel Vadot 862*5fba9064SEmmanuel Vadot /* Setup RX ring */ 863*5fba9064SEmmanuel Vadot error = bus_dma_tag_create( 864*5fba9064SEmmanuel Vadot bus_get_dma_tag(dev), /* Parent tag */ 865*5fba9064SEmmanuel Vadot DESC_ALIGN, 0, /* alignment, boundary */ 866*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 867*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR, /* highaddr */ 868*5fba9064SEmmanuel Vadot NULL, NULL, /* filter, filterarg */ 869*5fba9064SEmmanuel Vadot RX_DESC_SIZE, 1, /* maxsize, nsegs */ 870*5fba9064SEmmanuel Vadot RX_DESC_SIZE, /* maxsegsize */ 871*5fba9064SEmmanuel Vadot 0, /* flags */ 872*5fba9064SEmmanuel Vadot NULL, NULL, /* lockfunc, lockarg */ 873*5fba9064SEmmanuel Vadot &sc->rx.desc_tag); 874*5fba9064SEmmanuel Vadot if (error != 0) { 875*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create RX descriptor ring tag\n"); 876*5fba9064SEmmanuel Vadot return (error); 877*5fba9064SEmmanuel Vadot } 878*5fba9064SEmmanuel Vadot 879*5fba9064SEmmanuel Vadot error = bus_dmamem_alloc(sc->rx.desc_tag, (void **)&sc->rx.desc_ring, 880*5fba9064SEmmanuel Vadot BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rx.desc_map); 881*5fba9064SEmmanuel Vadot if (error != 0) { 882*5fba9064SEmmanuel Vadot device_printf(dev, "cannot allocate RX descriptor ring\n"); 883*5fba9064SEmmanuel Vadot return (error); 884*5fba9064SEmmanuel Vadot } 885*5fba9064SEmmanuel Vadot 886*5fba9064SEmmanuel Vadot error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map, 887*5fba9064SEmmanuel Vadot sc->rx.desc_ring, RX_DESC_SIZE, awg_dmamap_cb, 888*5fba9064SEmmanuel Vadot &sc->rx.desc_ring_paddr, 0); 889*5fba9064SEmmanuel Vadot if (error != 0) { 890*5fba9064SEmmanuel Vadot device_printf(dev, "cannot load RX descriptor ring\n"); 891*5fba9064SEmmanuel Vadot return (error); 892*5fba9064SEmmanuel Vadot } 893*5fba9064SEmmanuel Vadot 894*5fba9064SEmmanuel Vadot error = bus_dma_tag_create( 895*5fba9064SEmmanuel Vadot bus_get_dma_tag(dev), /* Parent tag */ 896*5fba9064SEmmanuel Vadot 1, 0, /* alignment, boundary */ 897*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 898*5fba9064SEmmanuel Vadot BUS_SPACE_MAXADDR, /* highaddr */ 899*5fba9064SEmmanuel Vadot NULL, NULL, /* filter, filterarg */ 900*5fba9064SEmmanuel Vadot MCLBYTES, 1, /* maxsize, nsegs */ 901*5fba9064SEmmanuel Vadot MCLBYTES, /* maxsegsize */ 902*5fba9064SEmmanuel Vadot 0, /* flags */ 903*5fba9064SEmmanuel Vadot NULL, NULL, /* lockfunc, lockarg */ 904*5fba9064SEmmanuel Vadot &sc->rx.buf_tag); 905*5fba9064SEmmanuel Vadot if (error != 0) { 906*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create RX buffer tag\n"); 907*5fba9064SEmmanuel Vadot return (error); 908*5fba9064SEmmanuel Vadot } 909*5fba9064SEmmanuel Vadot 910*5fba9064SEmmanuel Vadot error = bus_dmamap_create(sc->rx.buf_tag, 0, &sc->rx.buf_spare_map); 911*5fba9064SEmmanuel Vadot if (error != 0) { 912*5fba9064SEmmanuel Vadot device_printf(dev, 913*5fba9064SEmmanuel Vadot "cannot create RX buffer spare map\n"); 914*5fba9064SEmmanuel Vadot return (error); 915*5fba9064SEmmanuel Vadot } 916*5fba9064SEmmanuel Vadot 917*5fba9064SEmmanuel Vadot for (i = 0; i < RX_DESC_COUNT; i++) { 918*5fba9064SEmmanuel Vadot sc->rx.desc_ring[i].next = 919*5fba9064SEmmanuel Vadot htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(i))); 920*5fba9064SEmmanuel Vadot 921*5fba9064SEmmanuel Vadot error = bus_dmamap_create(sc->rx.buf_tag, 0, 922*5fba9064SEmmanuel Vadot &sc->rx.buf_map[i].map); 923*5fba9064SEmmanuel Vadot if (error != 0) { 924*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create RX buffer map\n"); 925*5fba9064SEmmanuel Vadot return (error); 926*5fba9064SEmmanuel Vadot } 927*5fba9064SEmmanuel Vadot sc->rx.buf_map[i].mbuf = NULL; 928*5fba9064SEmmanuel Vadot error = awg_newbuf_rx(sc, i); 929*5fba9064SEmmanuel Vadot if (error != 0) { 930*5fba9064SEmmanuel Vadot device_printf(dev, "cannot create RX buffer\n"); 931*5fba9064SEmmanuel Vadot return (error); 932*5fba9064SEmmanuel Vadot } 933*5fba9064SEmmanuel Vadot } 934*5fba9064SEmmanuel Vadot bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 935*5fba9064SEmmanuel Vadot BUS_DMASYNC_PREWRITE); 936*5fba9064SEmmanuel Vadot 937*5fba9064SEmmanuel Vadot /* Write transmit and receive descriptor base address registers */ 938*5fba9064SEmmanuel Vadot WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr); 939*5fba9064SEmmanuel Vadot WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr); 940*5fba9064SEmmanuel Vadot 941*5fba9064SEmmanuel Vadot return (0); 942*5fba9064SEmmanuel Vadot } 943*5fba9064SEmmanuel Vadot 944*5fba9064SEmmanuel Vadot /* 945*5fba9064SEmmanuel Vadot * if_ functions 946*5fba9064SEmmanuel Vadot */ 947*5fba9064SEmmanuel Vadot 948*5fba9064SEmmanuel Vadot static void 949d3810ff9SJared McNeill awg_start_locked(struct awg_softc *sc) 950d3810ff9SJared McNeill { 951d3810ff9SJared McNeill struct mbuf *m; 952d3810ff9SJared McNeill uint32_t val; 953d3810ff9SJared McNeill if_t ifp; 954337c6940SEmmanuel Vadot int cnt, err; 955d3810ff9SJared McNeill 956d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 957d3810ff9SJared McNeill 958d3810ff9SJared McNeill if (!sc->link) 959d3810ff9SJared McNeill return; 960d3810ff9SJared McNeill 961d3810ff9SJared McNeill ifp = sc->ifp; 962d3810ff9SJared McNeill 963d3810ff9SJared McNeill if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != 964d3810ff9SJared McNeill IFF_DRV_RUNNING) 965d3810ff9SJared McNeill return; 966d3810ff9SJared McNeill 967d3810ff9SJared McNeill for (cnt = 0; ; cnt++) { 968d3810ff9SJared McNeill m = if_dequeue(ifp); 969d3810ff9SJared McNeill if (m == NULL) 970d3810ff9SJared McNeill break; 971d3810ff9SJared McNeill 972337c6940SEmmanuel Vadot err = awg_encap(sc, &m); 973337c6940SEmmanuel Vadot if (err != 0) { 974337c6940SEmmanuel Vadot if (err == ENOBUFS) 975337c6940SEmmanuel Vadot if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 976337c6940SEmmanuel Vadot if (m != NULL) 977d3810ff9SJared McNeill if_sendq_prepend(ifp, m); 978d3810ff9SJared McNeill break; 979d3810ff9SJared McNeill } 980d3810ff9SJared McNeill if_bpfmtap(ifp, m); 981d3810ff9SJared McNeill } 982d3810ff9SJared McNeill 983d3810ff9SJared McNeill if (cnt != 0) { 984d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 985d3810ff9SJared McNeill BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 986d3810ff9SJared McNeill 987d3810ff9SJared McNeill /* Start and run TX DMA */ 988d3810ff9SJared McNeill val = RD4(sc, EMAC_TX_CTL_1); 989d3810ff9SJared McNeill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START); 990d3810ff9SJared McNeill } 991d3810ff9SJared McNeill } 992d3810ff9SJared McNeill 993d3810ff9SJared McNeill static void 994d3810ff9SJared McNeill awg_start(if_t ifp) 995d3810ff9SJared McNeill { 996d3810ff9SJared McNeill struct awg_softc *sc; 997d3810ff9SJared McNeill 998d3810ff9SJared McNeill sc = if_getsoftc(ifp); 999d3810ff9SJared McNeill 1000d3810ff9SJared McNeill AWG_LOCK(sc); 1001d3810ff9SJared McNeill awg_start_locked(sc); 1002d3810ff9SJared McNeill AWG_UNLOCK(sc); 1003d3810ff9SJared McNeill } 1004d3810ff9SJared McNeill 1005d3810ff9SJared McNeill static void 1006d3810ff9SJared McNeill awg_init_locked(struct awg_softc *sc) 1007d3810ff9SJared McNeill { 1008d3810ff9SJared McNeill struct mii_data *mii; 1009d3810ff9SJared McNeill if_t ifp; 1010d3810ff9SJared McNeill 1011d3810ff9SJared McNeill mii = device_get_softc(sc->miibus); 1012d3810ff9SJared McNeill ifp = sc->ifp; 1013d3810ff9SJared McNeill 1014d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 1015d3810ff9SJared McNeill 1016d3810ff9SJared McNeill if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 1017d3810ff9SJared McNeill return; 1018d3810ff9SJared McNeill 1019d3810ff9SJared McNeill awg_setup_rxfilter(sc); 1020612a1b8dSEmmanuel Vadot awg_setup_core(sc); 102116790d8fSEmmanuel Vadot awg_enable_mac(sc, true); 1022612a1b8dSEmmanuel Vadot awg_init_dma(sc); 1023d3810ff9SJared McNeill 1024d3810ff9SJared McNeill if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); 1025d3810ff9SJared McNeill 1026d3810ff9SJared McNeill mii_mediachg(mii); 1027d3810ff9SJared McNeill callout_reset(&sc->stat_ch, hz, awg_tick, sc); 1028d3810ff9SJared McNeill } 1029d3810ff9SJared McNeill 1030d3810ff9SJared McNeill static void 1031d3810ff9SJared McNeill awg_init(void *softc) 1032d3810ff9SJared McNeill { 1033d3810ff9SJared McNeill struct awg_softc *sc; 1034d3810ff9SJared McNeill 1035d3810ff9SJared McNeill sc = softc; 1036d3810ff9SJared McNeill 1037d3810ff9SJared McNeill AWG_LOCK(sc); 1038d3810ff9SJared McNeill awg_init_locked(sc); 1039d3810ff9SJared McNeill AWG_UNLOCK(sc); 1040d3810ff9SJared McNeill } 1041d3810ff9SJared McNeill 1042d3810ff9SJared McNeill static void 1043d3810ff9SJared McNeill awg_stop(struct awg_softc *sc) 1044d3810ff9SJared McNeill { 1045d3810ff9SJared McNeill if_t ifp; 1046d3810ff9SJared McNeill uint32_t val; 10473f9ade06SEmmanuel Vadot int i; 1048d3810ff9SJared McNeill 1049d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 1050d3810ff9SJared McNeill 1051d3810ff9SJared McNeill ifp = sc->ifp; 1052d3810ff9SJared McNeill 1053d3810ff9SJared McNeill callout_stop(&sc->stat_ch); 1054d3810ff9SJared McNeill 1055a19071ceSEmmanuel Vadot awg_stop_dma(sc); 105616790d8fSEmmanuel Vadot awg_enable_mac(sc, false); 1057d3810ff9SJared McNeill 1058d3810ff9SJared McNeill sc->link = 0; 1059d3810ff9SJared McNeill 10603f9ade06SEmmanuel Vadot /* Finish handling transmitted buffers */ 10613f9ade06SEmmanuel Vadot awg_txeof(sc); 10623f9ade06SEmmanuel Vadot 10633f9ade06SEmmanuel Vadot /* Release any untransmitted buffers. */ 10643f9ade06SEmmanuel Vadot for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { 10653f9ade06SEmmanuel Vadot val = le32toh(sc->tx.desc_ring[i].status); 10663f9ade06SEmmanuel Vadot if ((val & TX_DESC_CTL) != 0) 10673f9ade06SEmmanuel Vadot break; 10683f9ade06SEmmanuel Vadot awg_clean_txbuf(sc, i); 10693f9ade06SEmmanuel Vadot } 10703f9ade06SEmmanuel Vadot sc->tx.next = i; 10713f9ade06SEmmanuel Vadot for (; sc->tx.queued > 0; i = TX_NEXT(i)) { 10723f9ade06SEmmanuel Vadot sc->tx.desc_ring[i].status = 0; 10733f9ade06SEmmanuel Vadot awg_clean_txbuf(sc, i); 10743f9ade06SEmmanuel Vadot } 10753f9ade06SEmmanuel Vadot sc->tx.cur = sc->tx.next; 10763f9ade06SEmmanuel Vadot bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 10773f9ade06SEmmanuel Vadot BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 10783f9ade06SEmmanuel Vadot 10793f9ade06SEmmanuel Vadot /* Setup RX buffers for reuse */ 10803f9ade06SEmmanuel Vadot bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 10813f9ade06SEmmanuel Vadot BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 10823f9ade06SEmmanuel Vadot 10833f9ade06SEmmanuel Vadot for (i = sc->rx.cur; ; i = RX_NEXT(i)) { 10843f9ade06SEmmanuel Vadot val = le32toh(sc->rx.desc_ring[i].status); 10853f9ade06SEmmanuel Vadot if ((val & RX_DESC_CTL) != 0) 10863f9ade06SEmmanuel Vadot break; 10873f9ade06SEmmanuel Vadot awg_reuse_rxdesc(sc, i); 10883f9ade06SEmmanuel Vadot } 10893f9ade06SEmmanuel Vadot sc->rx.cur = i; 10903f9ade06SEmmanuel Vadot bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 10913f9ade06SEmmanuel Vadot BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 10923f9ade06SEmmanuel Vadot 1093d3810ff9SJared McNeill if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 1094d3810ff9SJared McNeill } 1095d3810ff9SJared McNeill 109616928528SJared McNeill static int 1097*5fba9064SEmmanuel Vadot awg_ioctl(if_t ifp, u_long cmd, caddr_t data) 1098*5fba9064SEmmanuel Vadot { 1099*5fba9064SEmmanuel Vadot struct awg_softc *sc; 1100*5fba9064SEmmanuel Vadot struct mii_data *mii; 1101*5fba9064SEmmanuel Vadot struct ifreq *ifr; 1102*5fba9064SEmmanuel Vadot int flags, mask, error; 1103*5fba9064SEmmanuel Vadot 1104*5fba9064SEmmanuel Vadot sc = if_getsoftc(ifp); 1105*5fba9064SEmmanuel Vadot mii = device_get_softc(sc->miibus); 1106*5fba9064SEmmanuel Vadot ifr = (struct ifreq *)data; 1107*5fba9064SEmmanuel Vadot error = 0; 1108*5fba9064SEmmanuel Vadot 1109*5fba9064SEmmanuel Vadot switch (cmd) { 1110*5fba9064SEmmanuel Vadot case SIOCSIFFLAGS: 1111*5fba9064SEmmanuel Vadot AWG_LOCK(sc); 1112*5fba9064SEmmanuel Vadot if (if_getflags(ifp) & IFF_UP) { 1113*5fba9064SEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 1114*5fba9064SEmmanuel Vadot flags = if_getflags(ifp) ^ sc->if_flags; 1115*5fba9064SEmmanuel Vadot if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) 1116*5fba9064SEmmanuel Vadot awg_setup_rxfilter(sc); 1117*5fba9064SEmmanuel Vadot } else 1118*5fba9064SEmmanuel Vadot awg_init_locked(sc); 1119*5fba9064SEmmanuel Vadot } else { 1120*5fba9064SEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) 1121*5fba9064SEmmanuel Vadot awg_stop(sc); 1122*5fba9064SEmmanuel Vadot } 1123*5fba9064SEmmanuel Vadot sc->if_flags = if_getflags(ifp); 1124*5fba9064SEmmanuel Vadot AWG_UNLOCK(sc); 1125*5fba9064SEmmanuel Vadot break; 1126*5fba9064SEmmanuel Vadot case SIOCADDMULTI: 1127*5fba9064SEmmanuel Vadot case SIOCDELMULTI: 1128*5fba9064SEmmanuel Vadot if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { 1129*5fba9064SEmmanuel Vadot AWG_LOCK(sc); 1130*5fba9064SEmmanuel Vadot awg_setup_rxfilter(sc); 1131*5fba9064SEmmanuel Vadot AWG_UNLOCK(sc); 1132*5fba9064SEmmanuel Vadot } 1133*5fba9064SEmmanuel Vadot break; 1134*5fba9064SEmmanuel Vadot case SIOCSIFMEDIA: 1135*5fba9064SEmmanuel Vadot case SIOCGIFMEDIA: 1136*5fba9064SEmmanuel Vadot error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 1137*5fba9064SEmmanuel Vadot break; 1138*5fba9064SEmmanuel Vadot case SIOCSIFCAP: 1139*5fba9064SEmmanuel Vadot mask = ifr->ifr_reqcap ^ if_getcapenable(ifp); 1140*5fba9064SEmmanuel Vadot #ifdef DEVICE_POLLING 1141*5fba9064SEmmanuel Vadot if (mask & IFCAP_POLLING) { 1142*5fba9064SEmmanuel Vadot if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) { 1143*5fba9064SEmmanuel Vadot error = ether_poll_register(awg_poll, ifp); 1144*5fba9064SEmmanuel Vadot if (error != 0) 1145*5fba9064SEmmanuel Vadot break; 1146*5fba9064SEmmanuel Vadot AWG_LOCK(sc); 1147*5fba9064SEmmanuel Vadot awg_disable_dma_intr(sc); 1148*5fba9064SEmmanuel Vadot if_setcapenablebit(ifp, IFCAP_POLLING, 0); 1149*5fba9064SEmmanuel Vadot AWG_UNLOCK(sc); 1150*5fba9064SEmmanuel Vadot } else { 1151*5fba9064SEmmanuel Vadot error = ether_poll_deregister(ifp); 1152*5fba9064SEmmanuel Vadot AWG_LOCK(sc); 1153*5fba9064SEmmanuel Vadot awg_enable_dma_intr(sc); 1154*5fba9064SEmmanuel Vadot if_setcapenablebit(ifp, 0, IFCAP_POLLING); 1155*5fba9064SEmmanuel Vadot AWG_UNLOCK(sc); 1156*5fba9064SEmmanuel Vadot } 1157*5fba9064SEmmanuel Vadot } 1158*5fba9064SEmmanuel Vadot #endif 1159*5fba9064SEmmanuel Vadot if (mask & IFCAP_VLAN_MTU) 1160*5fba9064SEmmanuel Vadot if_togglecapenable(ifp, IFCAP_VLAN_MTU); 1161*5fba9064SEmmanuel Vadot if (mask & IFCAP_RXCSUM) 1162*5fba9064SEmmanuel Vadot if_togglecapenable(ifp, IFCAP_RXCSUM); 1163*5fba9064SEmmanuel Vadot if (mask & IFCAP_TXCSUM) 1164*5fba9064SEmmanuel Vadot if_togglecapenable(ifp, IFCAP_TXCSUM); 1165*5fba9064SEmmanuel Vadot if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0) 1166*5fba9064SEmmanuel Vadot if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0); 1167*5fba9064SEmmanuel Vadot else 1168*5fba9064SEmmanuel Vadot if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP); 1169*5fba9064SEmmanuel Vadot break; 1170*5fba9064SEmmanuel Vadot default: 1171*5fba9064SEmmanuel Vadot error = ether_ioctl(ifp, cmd, data); 1172*5fba9064SEmmanuel Vadot break; 1173*5fba9064SEmmanuel Vadot } 1174*5fba9064SEmmanuel Vadot 1175*5fba9064SEmmanuel Vadot return (error); 1176*5fba9064SEmmanuel Vadot } 1177*5fba9064SEmmanuel Vadot 1178*5fba9064SEmmanuel Vadot /* 1179*5fba9064SEmmanuel Vadot * Interrupts functions 1180*5fba9064SEmmanuel Vadot */ 1181*5fba9064SEmmanuel Vadot 1182*5fba9064SEmmanuel Vadot static int 1183d3810ff9SJared McNeill awg_rxintr(struct awg_softc *sc) 1184d3810ff9SJared McNeill { 1185d3810ff9SJared McNeill if_t ifp; 1186bd906329SEmmanuel Vadot struct mbuf *m, *mh, *mt; 118716928528SJared McNeill int error, index, len, cnt, npkt; 1188d3810ff9SJared McNeill uint32_t status; 1189d3810ff9SJared McNeill 1190d3810ff9SJared McNeill ifp = sc->ifp; 119116928528SJared McNeill mh = mt = NULL; 119216928528SJared McNeill cnt = 0; 119316928528SJared McNeill npkt = 0; 1194d3810ff9SJared McNeill 1195d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 1196d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1197d3810ff9SJared McNeill 1198d3810ff9SJared McNeill for (index = sc->rx.cur; ; index = RX_NEXT(index)) { 1199d3810ff9SJared McNeill status = le32toh(sc->rx.desc_ring[index].status); 1200d3810ff9SJared McNeill if ((status & RX_DESC_CTL) != 0) 1201d3810ff9SJared McNeill break; 1202d3810ff9SJared McNeill 1203d3810ff9SJared McNeill len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT; 1204bd906329SEmmanuel Vadot 1205bd906329SEmmanuel Vadot if (len == 0) { 1206bd906329SEmmanuel Vadot if ((status & (RX_NO_ENOUGH_BUF_ERR | RX_OVERFLOW_ERR)) != 0) 1207bd906329SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 1208bd906329SEmmanuel Vadot awg_reuse_rxdesc(sc, index); 1209bd906329SEmmanuel Vadot continue; 1210bd906329SEmmanuel Vadot } 1211bd906329SEmmanuel Vadot 1212d3810ff9SJared McNeill m = sc->rx.buf_map[index].mbuf; 1213bd906329SEmmanuel Vadot 1214bd906329SEmmanuel Vadot error = awg_newbuf_rx(sc, index); 1215bd906329SEmmanuel Vadot if (error != 0) { 1216bd906329SEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 1217bd906329SEmmanuel Vadot awg_reuse_rxdesc(sc, index); 1218bd906329SEmmanuel Vadot continue; 1219bd906329SEmmanuel Vadot } 1220bd906329SEmmanuel Vadot 1221d3810ff9SJared McNeill m->m_pkthdr.rcvif = ifp; 1222d3810ff9SJared McNeill m->m_pkthdr.len = len; 1223d3810ff9SJared McNeill m->m_len = len; 1224d3810ff9SJared McNeill if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 1225d3810ff9SJared McNeill 1226d3810ff9SJared McNeill if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0 && 1227d3810ff9SJared McNeill (status & RX_FRM_TYPE) != 0) { 1228d3810ff9SJared McNeill m->m_pkthdr.csum_flags = CSUM_IP_CHECKED; 1229d3810ff9SJared McNeill if ((status & RX_HEADER_ERR) == 0) 1230d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= CSUM_IP_VALID; 1231d3810ff9SJared McNeill if ((status & RX_PAYLOAD_ERR) == 0) { 1232d3810ff9SJared McNeill m->m_pkthdr.csum_flags |= 1233d3810ff9SJared McNeill CSUM_DATA_VALID | CSUM_PSEUDO_HDR; 1234d3810ff9SJared McNeill m->m_pkthdr.csum_data = 0xffff; 1235d3810ff9SJared McNeill } 1236d3810ff9SJared McNeill } 1237d3810ff9SJared McNeill 123816928528SJared McNeill m->m_nextpkt = NULL; 123916928528SJared McNeill if (mh == NULL) 124016928528SJared McNeill mh = m; 124116928528SJared McNeill else 124216928528SJared McNeill mt->m_nextpkt = m; 124316928528SJared McNeill mt = m; 124416928528SJared McNeill ++cnt; 124516928528SJared McNeill ++npkt; 124616928528SJared McNeill 124716928528SJared McNeill if (cnt == awg_rx_batch) { 1248d3810ff9SJared McNeill AWG_UNLOCK(sc); 124916928528SJared McNeill if_input(ifp, mh); 1250d3810ff9SJared McNeill AWG_LOCK(sc); 125116928528SJared McNeill mh = mt = NULL; 125216928528SJared McNeill cnt = 0; 125316928528SJared McNeill } 1254d3810ff9SJared McNeill } 1255d3810ff9SJared McNeill 1256d3810ff9SJared McNeill if (index != sc->rx.cur) { 1257d3810ff9SJared McNeill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, 1258bd906329SEmmanuel Vadot BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 1259d3810ff9SJared McNeill } 1260d3810ff9SJared McNeill 126116928528SJared McNeill if (mh != NULL) { 126216928528SJared McNeill AWG_UNLOCK(sc); 126316928528SJared McNeill if_input(ifp, mh); 126416928528SJared McNeill AWG_LOCK(sc); 126516928528SJared McNeill } 126616928528SJared McNeill 1267d3810ff9SJared McNeill sc->rx.cur = index; 126816928528SJared McNeill 126916928528SJared McNeill return (npkt); 1270d3810ff9SJared McNeill } 1271d3810ff9SJared McNeill 1272d3810ff9SJared McNeill static void 1273337c6940SEmmanuel Vadot awg_txeof(struct awg_softc *sc) 1274d3810ff9SJared McNeill { 1275d3810ff9SJared McNeill struct emac_desc *desc; 127609e2285cSEmmanuel Vadot uint32_t status, size; 1277d3810ff9SJared McNeill if_t ifp; 1278f179ed05SEmmanuel Vadot int i, prog; 1279d3810ff9SJared McNeill 1280d3810ff9SJared McNeill AWG_ASSERT_LOCKED(sc); 1281d3810ff9SJared McNeill 1282d3810ff9SJared McNeill bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, 1283d3810ff9SJared McNeill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 1284d3810ff9SJared McNeill 1285d3810ff9SJared McNeill ifp = sc->ifp; 1286f179ed05SEmmanuel Vadot 1287f179ed05SEmmanuel Vadot prog = 0; 1288d3810ff9SJared McNeill for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) { 1289d3810ff9SJared McNeill desc = &sc->tx.desc_ring[i]; 1290d3810ff9SJared McNeill status = le32toh(desc->status); 1291d3810ff9SJared McNeill if ((status & TX_DESC_CTL) != 0) 1292d3810ff9SJared McNeill break; 129309e2285cSEmmanuel Vadot size = le32toh(desc->size); 129409e2285cSEmmanuel Vadot if (size & TX_LAST_DESC) { 129509e2285cSEmmanuel Vadot if ((status & (TX_HEADER_ERR | TX_PAYLOAD_ERR)) != 0) 129609e2285cSEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 129709e2285cSEmmanuel Vadot else 129809e2285cSEmmanuel Vadot if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 129909e2285cSEmmanuel Vadot } 1300f179ed05SEmmanuel Vadot prog++; 1301c6110e75SEmmanuel Vadot awg_clean_txbuf(sc, i); 1302d3810ff9SJared McNeill } 1303d3810ff9SJared McNeill 1304f179ed05SEmmanuel Vadot if (prog > 0) { 1305d3810ff9SJared McNeill sc->tx.next = i; 1306f179ed05SEmmanuel Vadot if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 1307f179ed05SEmmanuel Vadot } 1308d3810ff9SJared McNeill } 1309d3810ff9SJared McNeill 1310d3810ff9SJared McNeill static void 1311d3810ff9SJared McNeill awg_intr(void *arg) 1312d3810ff9SJared McNeill { 1313d3810ff9SJared McNeill struct awg_softc *sc; 1314d3810ff9SJared McNeill uint32_t val; 1315d3810ff9SJared McNeill 1316d3810ff9SJared McNeill sc = arg; 1317d3810ff9SJared McNeill 1318d3810ff9SJared McNeill AWG_LOCK(sc); 1319d3810ff9SJared McNeill val = RD4(sc, EMAC_INT_STA); 1320d3810ff9SJared McNeill WR4(sc, EMAC_INT_STA, val); 1321d3810ff9SJared McNeill 1322d3810ff9SJared McNeill if (val & RX_INT) 1323d3810ff9SJared McNeill awg_rxintr(sc); 1324d3810ff9SJared McNeill 13250d2abe1eSEmmanuel Vadot if (val & TX_INT) 1326337c6940SEmmanuel Vadot awg_txeof(sc); 13270d2abe1eSEmmanuel Vadot 13280d2abe1eSEmmanuel Vadot if (val & (TX_INT | TX_BUF_UA_INT)) { 1329d3810ff9SJared McNeill if (!if_sendq_empty(sc->ifp)) 1330d3810ff9SJared McNeill awg_start_locked(sc); 1331d3810ff9SJared McNeill } 1332d3810ff9SJared McNeill 1333d3810ff9SJared McNeill AWG_UNLOCK(sc); 1334d3810ff9SJared McNeill } 1335d3810ff9SJared McNeill 133616928528SJared McNeill #ifdef DEVICE_POLLING 133716928528SJared McNeill static int 133816928528SJared McNeill awg_poll(if_t ifp, enum poll_cmd cmd, int count) 133916928528SJared McNeill { 134016928528SJared McNeill struct awg_softc *sc; 134116928528SJared McNeill uint32_t val; 134216928528SJared McNeill int rx_npkts; 134316928528SJared McNeill 134416928528SJared McNeill sc = if_getsoftc(ifp); 134516928528SJared McNeill rx_npkts = 0; 134616928528SJared McNeill 134716928528SJared McNeill AWG_LOCK(sc); 134816928528SJared McNeill 134916928528SJared McNeill if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) { 135016928528SJared McNeill AWG_UNLOCK(sc); 135116928528SJared McNeill return (0); 135216928528SJared McNeill } 135316928528SJared McNeill 135416928528SJared McNeill rx_npkts = awg_rxintr(sc); 1355337c6940SEmmanuel Vadot awg_txeof(sc); 135616928528SJared McNeill if (!if_sendq_empty(ifp)) 135716928528SJared McNeill awg_start_locked(sc); 135816928528SJared McNeill 135916928528SJared McNeill if (cmd == POLL_AND_CHECK_STATUS) { 136016928528SJared McNeill val = RD4(sc, EMAC_INT_STA); 136116928528SJared McNeill if (val != 0) 136216928528SJared McNeill WR4(sc, EMAC_INT_STA, val); 136316928528SJared McNeill } 136416928528SJared McNeill 136516928528SJared McNeill AWG_UNLOCK(sc); 136616928528SJared McNeill 136716928528SJared McNeill return (rx_npkts); 136816928528SJared McNeill } 136916928528SJared McNeill #endif 137016928528SJared McNeill 1371*5fba9064SEmmanuel Vadot /* 1372*5fba9064SEmmanuel Vadot * syscon functions 1373*5fba9064SEmmanuel Vadot */ 13742defb358SKyle Evans static uint32_t 13752defb358SKyle Evans syscon_read_emac_clk_reg(device_t dev) 13762defb358SKyle Evans { 13772defb358SKyle Evans struct awg_softc *sc; 13782defb358SKyle Evans 13792defb358SKyle Evans sc = device_get_softc(dev); 13802defb358SKyle Evans if (sc->syscon != NULL) 13812defb358SKyle Evans return (SYSCON_READ_4(sc->syscon, EMAC_CLK_REG)); 13822defb358SKyle Evans else if (sc->res[_RES_SYSCON] != NULL) 13832defb358SKyle Evans return (bus_read_4(sc->res[_RES_SYSCON], 0)); 13842defb358SKyle Evans 13852defb358SKyle Evans return (0); 13862defb358SKyle Evans } 13872defb358SKyle Evans 13882defb358SKyle Evans static void 13892defb358SKyle Evans syscon_write_emac_clk_reg(device_t dev, uint32_t val) 13902defb358SKyle Evans { 13912defb358SKyle Evans struct awg_softc *sc; 13922defb358SKyle Evans 13932defb358SKyle Evans sc = device_get_softc(dev); 13942defb358SKyle Evans if (sc->syscon != NULL) 13952defb358SKyle Evans SYSCON_WRITE_4(sc->syscon, EMAC_CLK_REG, val); 13962defb358SKyle Evans else if (sc->res[_RES_SYSCON] != NULL) 13972defb358SKyle Evans bus_write_4(sc->res[_RES_SYSCON], 0, val); 13982defb358SKyle Evans } 13992defb358SKyle Evans 1400*5fba9064SEmmanuel Vadot /* 1401*5fba9064SEmmanuel Vadot * PHY functions 1402*5fba9064SEmmanuel Vadot */ 1403*5fba9064SEmmanuel Vadot 1404767754e5SKyle Evans static phandle_t 1405767754e5SKyle Evans awg_get_phy_node(device_t dev) 1406767754e5SKyle Evans { 1407767754e5SKyle Evans phandle_t node; 1408767754e5SKyle Evans pcell_t phy_handle; 1409767754e5SKyle Evans 1410767754e5SKyle Evans node = ofw_bus_get_node(dev); 1411767754e5SKyle Evans if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, 1412767754e5SKyle Evans sizeof(phy_handle)) <= 0) 1413767754e5SKyle Evans return (0); 1414767754e5SKyle Evans 1415767754e5SKyle Evans return (OF_node_from_xref(phy_handle)); 1416767754e5SKyle Evans } 1417767754e5SKyle Evans 1418767754e5SKyle Evans static bool 1419767754e5SKyle Evans awg_has_internal_phy(device_t dev) 1420767754e5SKyle Evans { 1421767754e5SKyle Evans phandle_t node, phy_node; 1422767754e5SKyle Evans 1423767754e5SKyle Evans node = ofw_bus_get_node(dev); 1424767754e5SKyle Evans /* Legacy binding */ 1425767754e5SKyle Evans if (OF_hasprop(node, "allwinner,use-internal-phy")) 1426767754e5SKyle Evans return (true); 1427767754e5SKyle Evans 1428767754e5SKyle Evans phy_node = awg_get_phy_node(dev); 1429767754e5SKyle Evans return (phy_node != 0 && ofw_bus_node_is_compatible(OF_parent(phy_node), 1430767754e5SKyle Evans "allwinner,sun8i-h3-mdio-internal") != 0); 1431767754e5SKyle Evans } 1432767754e5SKyle Evans 1433d3810ff9SJared McNeill static int 14349a77a643SKyle Evans awg_parse_delay(device_t dev, uint32_t *tx_delay, uint32_t *rx_delay) 14359a77a643SKyle Evans { 14369a77a643SKyle Evans phandle_t node; 14379a77a643SKyle Evans uint32_t delay; 14389a77a643SKyle Evans 14399a77a643SKyle Evans if (tx_delay == NULL || rx_delay == NULL) 14409a77a643SKyle Evans return (EINVAL); 14419a77a643SKyle Evans *tx_delay = *rx_delay = 0; 14429a77a643SKyle Evans node = ofw_bus_get_node(dev); 14439a77a643SKyle Evans 14449a77a643SKyle Evans if (OF_getencprop(node, "tx-delay", &delay, sizeof(delay)) >= 0) 14459a77a643SKyle Evans *tx_delay = delay; 14469a77a643SKyle Evans else if (OF_getencprop(node, "allwinner,tx-delay-ps", &delay, 14479a77a643SKyle Evans sizeof(delay)) >= 0) { 14489a77a643SKyle Evans if ((delay % 100) != 0) { 14499a77a643SKyle Evans device_printf(dev, "tx-delay-ps is not a multiple of 100\n"); 14509a77a643SKyle Evans return (EDOM); 14519a77a643SKyle Evans } 14529a77a643SKyle Evans *tx_delay = delay / 100; 14539a77a643SKyle Evans } 14549a77a643SKyle Evans if (*tx_delay > 7) { 14559a77a643SKyle Evans device_printf(dev, "tx-delay out of range\n"); 14569a77a643SKyle Evans return (ERANGE); 14579a77a643SKyle Evans } 14589a77a643SKyle Evans 14599a77a643SKyle Evans if (OF_getencprop(node, "rx-delay", &delay, sizeof(delay)) >= 0) 14609a77a643SKyle Evans *rx_delay = delay; 14619a77a643SKyle Evans else if (OF_getencprop(node, "allwinner,rx-delay-ps", &delay, 14629a77a643SKyle Evans sizeof(delay)) >= 0) { 14639a77a643SKyle Evans if ((delay % 100) != 0) { 14649a77a643SKyle Evans device_printf(dev, "rx-delay-ps is not within documented domain\n"); 14659a77a643SKyle Evans return (EDOM); 14669a77a643SKyle Evans } 14679a77a643SKyle Evans *rx_delay = delay / 100; 14689a77a643SKyle Evans } 14699a77a643SKyle Evans if (*rx_delay > 31) { 14709a77a643SKyle Evans device_printf(dev, "rx-delay out of range\n"); 14719a77a643SKyle Evans return (ERANGE); 14729a77a643SKyle Evans } 14739a77a643SKyle Evans 14749a77a643SKyle Evans return (0); 14759a77a643SKyle Evans } 14769a77a643SKyle Evans 14779a77a643SKyle Evans static int 147801a469b8SJared McNeill awg_setup_phy(device_t dev) 1479d3810ff9SJared McNeill { 1480d3810ff9SJared McNeill struct awg_softc *sc; 148101a469b8SJared McNeill clk_t clk_tx, clk_tx_parent; 1482d3810ff9SJared McNeill const char *tx_parent_name; 1483d3810ff9SJared McNeill char *phy_type; 1484d3810ff9SJared McNeill phandle_t node; 148501a469b8SJared McNeill uint32_t reg, tx_delay, rx_delay; 148601a469b8SJared McNeill int error; 14872defb358SKyle Evans bool use_syscon; 1488d3810ff9SJared McNeill 1489d3810ff9SJared McNeill sc = device_get_softc(dev); 1490d3810ff9SJared McNeill node = ofw_bus_get_node(dev); 14912defb358SKyle Evans use_syscon = false; 1492d3810ff9SJared McNeill 1493217d17bcSOleksandr Tymoshenko if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_type) == 0) 149401a469b8SJared McNeill return (0); 1495d3810ff9SJared McNeill 14962defb358SKyle Evans if (sc->syscon != NULL || sc->res[_RES_SYSCON] != NULL) 14972defb358SKyle Evans use_syscon = true; 14982defb358SKyle Evans 1499d3810ff9SJared McNeill if (bootverbose) 150001a469b8SJared McNeill device_printf(dev, "PHY type: %s, conf mode: %s\n", phy_type, 15012defb358SKyle Evans use_syscon ? "reg" : "clk"); 1502d3810ff9SJared McNeill 15032defb358SKyle Evans if (use_syscon) { 15042defb358SKyle Evans /* 15052defb358SKyle Evans * Abstract away writing to syscon for devices like the pine64. 15062defb358SKyle Evans * For the pine64, we get dtb from U-Boot and it still uses the 15072defb358SKyle Evans * legacy setup of specifying syscon register in emac node 15082defb358SKyle Evans * rather than as its own node and using an xref in emac. 15092defb358SKyle Evans * These abstractions can go away once U-Boot dts is up-to-date. 15102defb358SKyle Evans */ 15112defb358SKyle Evans reg = syscon_read_emac_clk_reg(dev); 151201a469b8SJared McNeill reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN); 1513858f2466SKyle Evans if (strncmp(phy_type, "rgmii", 5) == 0) 151401a469b8SJared McNeill reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII; 151501a469b8SJared McNeill else if (strcmp(phy_type, "rmii") == 0) 151601a469b8SJared McNeill reg |= EMAC_CLK_RMII_EN; 151701a469b8SJared McNeill else 151801a469b8SJared McNeill reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII; 151901a469b8SJared McNeill 15209a77a643SKyle Evans /* 15219a77a643SKyle Evans * Fail attach if we fail to parse either of the delay 15229a77a643SKyle Evans * parameters. If we don't have the proper delay to write to 15239a77a643SKyle Evans * syscon, then awg likely won't function properly anyways. 15249a77a643SKyle Evans * Lack of delay is not an error! 15259a77a643SKyle Evans */ 15269a77a643SKyle Evans error = awg_parse_delay(dev, &tx_delay, &rx_delay); 15279a77a643SKyle Evans if (error != 0) 15289a77a643SKyle Evans goto fail; 15299a77a643SKyle Evans 15309a77a643SKyle Evans /* Default to 0 and we'll increase it if we need to. */ 15319a77a643SKyle Evans reg &= ~(EMAC_CLK_ETXDC | EMAC_CLK_ERXDC); 15329a77a643SKyle Evans if (tx_delay > 0) 153301a469b8SJared McNeill reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT); 15349a77a643SKyle Evans if (rx_delay > 0) 153501a469b8SJared McNeill reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT); 153601a469b8SJared McNeill 153701a469b8SJared McNeill if (sc->type == EMAC_H3) { 1538767754e5SKyle Evans if (awg_has_internal_phy(dev)) { 153901a469b8SJared McNeill reg |= EMAC_CLK_EPHY_SELECT; 154001a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_SHUTDOWN; 154101a469b8SJared McNeill if (OF_hasprop(node, 154201a469b8SJared McNeill "allwinner,leds-active-low")) 154301a469b8SJared McNeill reg |= EMAC_CLK_EPHY_LED_POL; 154401a469b8SJared McNeill else 154501a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_LED_POL; 154601a469b8SJared McNeill 154701a469b8SJared McNeill /* Set internal PHY addr to 1 */ 154801a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_ADDR; 154901a469b8SJared McNeill reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT); 155001a469b8SJared McNeill } else { 155101a469b8SJared McNeill reg &= ~EMAC_CLK_EPHY_SELECT; 155201a469b8SJared McNeill } 155301a469b8SJared McNeill } 155401a469b8SJared McNeill 155501a469b8SJared McNeill if (bootverbose) 155601a469b8SJared McNeill device_printf(dev, "EMAC clock: 0x%08x\n", reg); 15572defb358SKyle Evans syscon_write_emac_clk_reg(dev, reg); 155801a469b8SJared McNeill } else { 1559858f2466SKyle Evans if (strncmp(phy_type, "rgmii", 5) == 0) 1560d3810ff9SJared McNeill tx_parent_name = "emac_int_tx"; 1561d3810ff9SJared McNeill else 1562d3810ff9SJared McNeill tx_parent_name = "mii_phy_tx"; 1563d3810ff9SJared McNeill 1564d3810ff9SJared McNeill /* Get the TX clock */ 1565dac93553SMichal Meloun error = clk_get_by_ofw_name(dev, 0, "tx", &clk_tx); 1566d3810ff9SJared McNeill if (error != 0) { 1567d3810ff9SJared McNeill device_printf(dev, "cannot get tx clock\n"); 1568d3810ff9SJared McNeill goto fail; 1569d3810ff9SJared McNeill } 1570d3810ff9SJared McNeill 1571d3810ff9SJared McNeill /* Find the desired parent clock based on phy-mode property */ 1572d3810ff9SJared McNeill error = clk_get_by_name(dev, tx_parent_name, &clk_tx_parent); 1573d3810ff9SJared McNeill if (error != 0) { 1574d3810ff9SJared McNeill device_printf(dev, "cannot get clock '%s'\n", 1575d3810ff9SJared McNeill tx_parent_name); 1576d3810ff9SJared McNeill goto fail; 1577d3810ff9SJared McNeill } 1578d3810ff9SJared McNeill 1579d3810ff9SJared McNeill /* Set TX clock parent */ 1580d3810ff9SJared McNeill error = clk_set_parent_by_clk(clk_tx, clk_tx_parent); 1581d3810ff9SJared McNeill if (error != 0) { 1582d3810ff9SJared McNeill device_printf(dev, "cannot set tx clock parent\n"); 1583d3810ff9SJared McNeill goto fail; 1584d3810ff9SJared McNeill } 1585d3810ff9SJared McNeill 1586d3810ff9SJared McNeill /* Enable TX clock */ 1587d3810ff9SJared McNeill error = clk_enable(clk_tx); 1588d3810ff9SJared McNeill if (error != 0) { 1589d3810ff9SJared McNeill device_printf(dev, "cannot enable tx clock\n"); 1590d3810ff9SJared McNeill goto fail; 1591d3810ff9SJared McNeill } 1592d3810ff9SJared McNeill } 1593d3810ff9SJared McNeill 159401a469b8SJared McNeill error = 0; 159501a469b8SJared McNeill 159601a469b8SJared McNeill fail: 159701a469b8SJared McNeill OF_prop_free(phy_type); 159801a469b8SJared McNeill return (error); 159901a469b8SJared McNeill } 160001a469b8SJared McNeill 160101a469b8SJared McNeill static int 160201a469b8SJared McNeill awg_setup_extres(device_t dev) 160301a469b8SJared McNeill { 160401a469b8SJared McNeill struct awg_softc *sc; 1605767754e5SKyle Evans phandle_t node, phy_node; 160601a469b8SJared McNeill hwreset_t rst_ahb, rst_ephy; 160701a469b8SJared McNeill clk_t clk_ahb, clk_ephy; 160801a469b8SJared McNeill regulator_t reg; 160901a469b8SJared McNeill uint64_t freq; 161001a469b8SJared McNeill int error, div; 161101a469b8SJared McNeill 161201a469b8SJared McNeill sc = device_get_softc(dev); 161301a469b8SJared McNeill rst_ahb = rst_ephy = NULL; 161401a469b8SJared McNeill clk_ahb = clk_ephy = NULL; 161501a469b8SJared McNeill reg = NULL; 16162defb358SKyle Evans node = ofw_bus_get_node(dev); 1617767754e5SKyle Evans phy_node = awg_get_phy_node(dev); 1618767754e5SKyle Evans 1619767754e5SKyle Evans if (phy_node == 0 && OF_hasprop(node, "phy-handle")) { 1620767754e5SKyle Evans error = ENXIO; 1621767754e5SKyle Evans device_printf(dev, "cannot get phy handle\n"); 1622767754e5SKyle Evans goto fail; 1623767754e5SKyle Evans } 162401a469b8SJared McNeill 162501a469b8SJared McNeill /* Get AHB clock and reset resources */ 1626767754e5SKyle Evans error = hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst_ahb); 1627767754e5SKyle Evans if (error != 0) 162801a469b8SJared McNeill error = hwreset_get_by_ofw_name(dev, 0, "ahb", &rst_ahb); 162901a469b8SJared McNeill if (error != 0) { 163001a469b8SJared McNeill device_printf(dev, "cannot get ahb reset\n"); 163101a469b8SJared McNeill goto fail; 163201a469b8SJared McNeill } 163301a469b8SJared McNeill if (hwreset_get_by_ofw_name(dev, 0, "ephy", &rst_ephy) != 0) 1634767754e5SKyle Evans if (phy_node == 0 || hwreset_get_by_ofw_idx(dev, phy_node, 0, 1635767754e5SKyle Evans &rst_ephy) != 0) 163601a469b8SJared McNeill rst_ephy = NULL; 1637767754e5SKyle Evans error = clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk_ahb); 1638767754e5SKyle Evans if (error != 0) 163901a469b8SJared McNeill error = clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb); 164001a469b8SJared McNeill if (error != 0) { 164101a469b8SJared McNeill device_printf(dev, "cannot get ahb clock\n"); 164201a469b8SJared McNeill goto fail; 164301a469b8SJared McNeill } 164401a469b8SJared McNeill if (clk_get_by_ofw_name(dev, 0, "ephy", &clk_ephy) != 0) 1645767754e5SKyle Evans if (phy_node == 0 || clk_get_by_ofw_index(dev, phy_node, 0, 1646767754e5SKyle Evans &clk_ephy) != 0) 164701a469b8SJared McNeill clk_ephy = NULL; 164801a469b8SJared McNeill 16492defb358SKyle Evans if (OF_hasprop(node, "syscon") && syscon_get_by_ofw_property(dev, node, 16502defb358SKyle Evans "syscon", &sc->syscon) != 0) { 16512defb358SKyle Evans device_printf(dev, "cannot get syscon driver handle\n"); 16522defb358SKyle Evans goto fail; 16532defb358SKyle Evans } 16542defb358SKyle Evans 165501a469b8SJared McNeill /* Configure PHY for MII or RGMII mode */ 165601a469b8SJared McNeill if (awg_setup_phy(dev) != 0) 165701a469b8SJared McNeill goto fail; 165801a469b8SJared McNeill 165901a469b8SJared McNeill /* Enable clocks */ 1660d3810ff9SJared McNeill error = clk_enable(clk_ahb); 1661d3810ff9SJared McNeill if (error != 0) { 1662d3810ff9SJared McNeill device_printf(dev, "cannot enable ahb clock\n"); 1663d3810ff9SJared McNeill goto fail; 1664d3810ff9SJared McNeill } 166501a469b8SJared McNeill if (clk_ephy != NULL) { 166601a469b8SJared McNeill error = clk_enable(clk_ephy); 166701a469b8SJared McNeill if (error != 0) { 166801a469b8SJared McNeill device_printf(dev, "cannot enable ephy clock\n"); 166901a469b8SJared McNeill goto fail; 167001a469b8SJared McNeill } 167101a469b8SJared McNeill } 1672d3810ff9SJared McNeill 1673d3810ff9SJared McNeill /* De-assert reset */ 1674d3810ff9SJared McNeill error = hwreset_deassert(rst_ahb); 1675d3810ff9SJared McNeill if (error != 0) { 1676d3810ff9SJared McNeill device_printf(dev, "cannot de-assert ahb reset\n"); 1677d3810ff9SJared McNeill goto fail; 1678d3810ff9SJared McNeill } 167901a469b8SJared McNeill if (rst_ephy != NULL) { 1680649a5cd5SKyle Evans /* 1681649a5cd5SKyle Evans * The ephy reset is left de-asserted by U-Boot. Assert it 1682649a5cd5SKyle Evans * here to make sure that we're in a known good state going 1683649a5cd5SKyle Evans * into the PHY reset. 1684649a5cd5SKyle Evans */ 1685649a5cd5SKyle Evans hwreset_assert(rst_ephy); 168601a469b8SJared McNeill error = hwreset_deassert(rst_ephy); 168701a469b8SJared McNeill if (error != 0) { 168801a469b8SJared McNeill device_printf(dev, "cannot de-assert ephy reset\n"); 168901a469b8SJared McNeill goto fail; 169001a469b8SJared McNeill } 169101a469b8SJared McNeill } 1692d3810ff9SJared McNeill 1693d3810ff9SJared McNeill /* Enable PHY regulator if applicable */ 1694dac93553SMichal Meloun if (regulator_get_by_ofw_property(dev, 0, "phy-supply", ®) == 0) { 1695d3810ff9SJared McNeill error = regulator_enable(reg); 1696d3810ff9SJared McNeill if (error != 0) { 1697d3810ff9SJared McNeill device_printf(dev, "cannot enable PHY regulator\n"); 1698d3810ff9SJared McNeill goto fail; 1699d3810ff9SJared McNeill } 1700d3810ff9SJared McNeill } 1701d3810ff9SJared McNeill 1702d3810ff9SJared McNeill /* Determine MDC clock divide ratio based on AHB clock */ 1703d3810ff9SJared McNeill error = clk_get_freq(clk_ahb, &freq); 1704d3810ff9SJared McNeill if (error != 0) { 1705d3810ff9SJared McNeill device_printf(dev, "cannot get AHB clock frequency\n"); 1706d3810ff9SJared McNeill goto fail; 1707d3810ff9SJared McNeill } 1708d3810ff9SJared McNeill div = freq / MDIO_FREQ; 1709d3810ff9SJared McNeill if (div <= 16) 1710d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16; 1711d3810ff9SJared McNeill else if (div <= 32) 1712d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32; 1713d3810ff9SJared McNeill else if (div <= 64) 1714d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64; 1715d3810ff9SJared McNeill else if (div <= 128) 1716d3810ff9SJared McNeill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128; 1717d3810ff9SJared McNeill else { 1718d3810ff9SJared McNeill device_printf(dev, "cannot determine MDC clock divide ratio\n"); 1719d3810ff9SJared McNeill error = ENXIO; 1720d3810ff9SJared McNeill goto fail; 1721d3810ff9SJared McNeill } 1722d3810ff9SJared McNeill 1723d3810ff9SJared McNeill if (bootverbose) 172401a469b8SJared McNeill device_printf(dev, "AHB frequency %ju Hz, MDC div: 0x%x\n", 172501a469b8SJared McNeill (uintmax_t)freq, sc->mdc_div_ratio_m); 1726d3810ff9SJared McNeill 1727d3810ff9SJared McNeill return (0); 1728d3810ff9SJared McNeill 1729d3810ff9SJared McNeill fail: 1730d3810ff9SJared McNeill if (reg != NULL) 1731d3810ff9SJared McNeill regulator_release(reg); 173201a469b8SJared McNeill if (clk_ephy != NULL) 173301a469b8SJared McNeill clk_release(clk_ephy); 1734d3810ff9SJared McNeill if (clk_ahb != NULL) 1735d3810ff9SJared McNeill clk_release(clk_ahb); 173601a469b8SJared McNeill if (rst_ephy != NULL) 173701a469b8SJared McNeill hwreset_release(rst_ephy); 1738d3810ff9SJared McNeill if (rst_ahb != NULL) 1739d3810ff9SJared McNeill hwreset_release(rst_ahb); 1740d3810ff9SJared McNeill return (error); 1741d3810ff9SJared McNeill } 1742d3810ff9SJared McNeill 1743d3810ff9SJared McNeill #ifdef AWG_DEBUG 1744d3810ff9SJared McNeill static void 1745d3810ff9SJared McNeill awg_dump_regs(device_t dev) 1746d3810ff9SJared McNeill { 1747d3810ff9SJared McNeill static const struct { 1748d3810ff9SJared McNeill const char *name; 1749d3810ff9SJared McNeill u_int reg; 1750d3810ff9SJared McNeill } regs[] = { 1751d3810ff9SJared McNeill { "BASIC_CTL_0", EMAC_BASIC_CTL_0 }, 1752d3810ff9SJared McNeill { "BASIC_CTL_1", EMAC_BASIC_CTL_1 }, 1753d3810ff9SJared McNeill { "INT_STA", EMAC_INT_STA }, 1754d3810ff9SJared McNeill { "INT_EN", EMAC_INT_EN }, 1755d3810ff9SJared McNeill { "TX_CTL_0", EMAC_TX_CTL_0 }, 1756d3810ff9SJared McNeill { "TX_CTL_1", EMAC_TX_CTL_1 }, 1757d3810ff9SJared McNeill { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL }, 1758d3810ff9SJared McNeill { "TX_DMA_LIST", EMAC_TX_DMA_LIST }, 1759d3810ff9SJared McNeill { "RX_CTL_0", EMAC_RX_CTL_0 }, 1760d3810ff9SJared McNeill { "RX_CTL_1", EMAC_RX_CTL_1 }, 1761d3810ff9SJared McNeill { "RX_DMA_LIST", EMAC_RX_DMA_LIST }, 1762d3810ff9SJared McNeill { "RX_FRM_FLT", EMAC_RX_FRM_FLT }, 1763d3810ff9SJared McNeill { "RX_HASH_0", EMAC_RX_HASH_0 }, 1764d3810ff9SJared McNeill { "RX_HASH_1", EMAC_RX_HASH_1 }, 1765d3810ff9SJared McNeill { "MII_CMD", EMAC_MII_CMD }, 1766d3810ff9SJared McNeill { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) }, 1767d3810ff9SJared McNeill { "ADDR_LOW0", EMAC_ADDR_LOW(0) }, 1768d3810ff9SJared McNeill { "TX_DMA_STA", EMAC_TX_DMA_STA }, 1769d3810ff9SJared McNeill { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC }, 1770d3810ff9SJared McNeill { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF }, 1771d3810ff9SJared McNeill { "RX_DMA_STA", EMAC_RX_DMA_STA }, 1772d3810ff9SJared McNeill { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC }, 1773d3810ff9SJared McNeill { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF }, 1774d3810ff9SJared McNeill { "RGMII_STA", EMAC_RGMII_STA }, 1775d3810ff9SJared McNeill }; 1776d3810ff9SJared McNeill struct awg_softc *sc; 1777d3810ff9SJared McNeill unsigned int n; 1778d3810ff9SJared McNeill 1779d3810ff9SJared McNeill sc = device_get_softc(dev); 1780d3810ff9SJared McNeill 1781d3810ff9SJared McNeill for (n = 0; n < nitems(regs); n++) 1782d3810ff9SJared McNeill device_printf(dev, " %-20s %08x\n", regs[n].name, 1783d3810ff9SJared McNeill RD4(sc, regs[n].reg)); 1784d3810ff9SJared McNeill } 1785d3810ff9SJared McNeill #endif 1786d3810ff9SJared McNeill 178701a469b8SJared McNeill #define GPIO_ACTIVE_LOW 1 178801a469b8SJared McNeill 178901a469b8SJared McNeill static int 179001a469b8SJared McNeill awg_phy_reset(device_t dev) 179101a469b8SJared McNeill { 179201a469b8SJared McNeill pcell_t gpio_prop[4], delay_prop[3]; 179301a469b8SJared McNeill phandle_t node, gpio_node; 179401a469b8SJared McNeill device_t gpio; 179501a469b8SJared McNeill uint32_t pin, flags; 179601a469b8SJared McNeill uint32_t pin_value; 179701a469b8SJared McNeill 179801a469b8SJared McNeill node = ofw_bus_get_node(dev); 179901a469b8SJared McNeill if (OF_getencprop(node, "allwinner,reset-gpio", gpio_prop, 180001a469b8SJared McNeill sizeof(gpio_prop)) <= 0) 180101a469b8SJared McNeill return (0); 180201a469b8SJared McNeill 180301a469b8SJared McNeill if (OF_getencprop(node, "allwinner,reset-delays-us", delay_prop, 180401a469b8SJared McNeill sizeof(delay_prop)) <= 0) 180501a469b8SJared McNeill return (ENXIO); 180601a469b8SJared McNeill 180701a469b8SJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 180801a469b8SJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) 180901a469b8SJared McNeill return (ENXIO); 181001a469b8SJared McNeill 181101a469b8SJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, nitems(gpio_prop) - 1, 181201a469b8SJared McNeill gpio_prop + 1, &pin, &flags) != 0) 181301a469b8SJared McNeill return (ENXIO); 181401a469b8SJared McNeill 181501a469b8SJared McNeill pin_value = GPIO_PIN_LOW; 181601a469b8SJared McNeill if (OF_hasprop(node, "allwinner,reset-active-low")) 181701a469b8SJared McNeill pin_value = GPIO_PIN_HIGH; 181801a469b8SJared McNeill 181901a469b8SJared McNeill if (flags & GPIO_ACTIVE_LOW) 182001a469b8SJared McNeill pin_value = !pin_value; 182101a469b8SJared McNeill 182201a469b8SJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 182301a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 182401a469b8SJared McNeill DELAY(delay_prop[0]); 182501a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 182601a469b8SJared McNeill DELAY(delay_prop[1]); 182701a469b8SJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 182801a469b8SJared McNeill DELAY(delay_prop[2]); 182901a469b8SJared McNeill 183001a469b8SJared McNeill return (0); 183101a469b8SJared McNeill } 183201a469b8SJared McNeill 1833a3a7d2a4SKyle Evans static int 1834a3a7d2a4SKyle Evans awg_reset(device_t dev) 1835a3a7d2a4SKyle Evans { 1836a3a7d2a4SKyle Evans struct awg_softc *sc; 1837a3a7d2a4SKyle Evans int retry; 1838a3a7d2a4SKyle Evans 1839a3a7d2a4SKyle Evans sc = device_get_softc(dev); 1840a3a7d2a4SKyle Evans 1841a3a7d2a4SKyle Evans /* Reset PHY if necessary */ 1842a3a7d2a4SKyle Evans if (awg_phy_reset(dev) != 0) { 1843a3a7d2a4SKyle Evans device_printf(dev, "failed to reset PHY\n"); 1844a3a7d2a4SKyle Evans return (ENXIO); 1845a3a7d2a4SKyle Evans } 1846a3a7d2a4SKyle Evans 1847a3a7d2a4SKyle Evans /* Soft reset all registers and logic */ 1848a3a7d2a4SKyle Evans WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST); 1849a3a7d2a4SKyle Evans 1850a3a7d2a4SKyle Evans /* Wait for soft reset bit to self-clear */ 1851a3a7d2a4SKyle Evans for (retry = SOFT_RST_RETRY; retry > 0; retry--) { 1852a3a7d2a4SKyle Evans if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0) 1853a3a7d2a4SKyle Evans break; 1854a3a7d2a4SKyle Evans DELAY(10); 1855a3a7d2a4SKyle Evans } 1856a3a7d2a4SKyle Evans if (retry == 0) { 1857a3a7d2a4SKyle Evans device_printf(dev, "soft reset timed out\n"); 1858a3a7d2a4SKyle Evans #ifdef AWG_DEBUG 1859a3a7d2a4SKyle Evans awg_dump_regs(dev); 1860a3a7d2a4SKyle Evans #endif 1861a3a7d2a4SKyle Evans return (ETIMEDOUT); 1862a3a7d2a4SKyle Evans } 1863a3a7d2a4SKyle Evans 1864a3a7d2a4SKyle Evans return (0); 1865a3a7d2a4SKyle Evans } 1866a3a7d2a4SKyle Evans 1867*5fba9064SEmmanuel Vadot /* 1868*5fba9064SEmmanuel Vadot * Stats 1869*5fba9064SEmmanuel Vadot */ 1870d3810ff9SJared McNeill 1871*5fba9064SEmmanuel Vadot static void 1872*5fba9064SEmmanuel Vadot awg_tick(void *softc) 1873d3810ff9SJared McNeill { 1874d3810ff9SJared McNeill struct awg_softc *sc; 1875*5fba9064SEmmanuel Vadot struct mii_data *mii; 1876*5fba9064SEmmanuel Vadot if_t ifp; 1877*5fba9064SEmmanuel Vadot int link; 1878d3810ff9SJared McNeill 1879*5fba9064SEmmanuel Vadot sc = softc; 1880*5fba9064SEmmanuel Vadot ifp = sc->ifp; 1881*5fba9064SEmmanuel Vadot mii = device_get_softc(sc->miibus); 1882d3810ff9SJared McNeill 1883*5fba9064SEmmanuel Vadot AWG_ASSERT_LOCKED(sc); 1884*5fba9064SEmmanuel Vadot 1885*5fba9064SEmmanuel Vadot if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 1886*5fba9064SEmmanuel Vadot return; 1887*5fba9064SEmmanuel Vadot 1888*5fba9064SEmmanuel Vadot link = sc->link; 1889*5fba9064SEmmanuel Vadot mii_tick(mii); 1890*5fba9064SEmmanuel Vadot if (sc->link && !link) 1891*5fba9064SEmmanuel Vadot awg_start_locked(sc); 1892*5fba9064SEmmanuel Vadot 1893*5fba9064SEmmanuel Vadot callout_reset(&sc->stat_ch, hz, awg_tick, sc); 1894d3810ff9SJared McNeill } 1895d3810ff9SJared McNeill 1896*5fba9064SEmmanuel Vadot /* 1897*5fba9064SEmmanuel Vadot * Probe/attach functions 1898*5fba9064SEmmanuel Vadot */ 1899d3810ff9SJared McNeill 1900d3810ff9SJared McNeill static int 1901d3810ff9SJared McNeill awg_probe(device_t dev) 1902d3810ff9SJared McNeill { 1903d3810ff9SJared McNeill if (!ofw_bus_status_okay(dev)) 1904d3810ff9SJared McNeill return (ENXIO); 1905d3810ff9SJared McNeill 1906d3810ff9SJared McNeill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1907d3810ff9SJared McNeill return (ENXIO); 1908d3810ff9SJared McNeill 1909d3810ff9SJared McNeill device_set_desc(dev, "Allwinner Gigabit Ethernet"); 1910d3810ff9SJared McNeill return (BUS_PROBE_DEFAULT); 1911d3810ff9SJared McNeill } 1912d3810ff9SJared McNeill 1913d3810ff9SJared McNeill static int 1914d3810ff9SJared McNeill awg_attach(device_t dev) 1915d3810ff9SJared McNeill { 1916d3810ff9SJared McNeill uint8_t eaddr[ETHER_ADDR_LEN]; 1917d3810ff9SJared McNeill struct awg_softc *sc; 1918d3810ff9SJared McNeill int error; 1919d3810ff9SJared McNeill 1920d3810ff9SJared McNeill sc = device_get_softc(dev); 1921031d5777SOleksandr Tymoshenko sc->dev = dev; 192201a469b8SJared McNeill sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 1923d3810ff9SJared McNeill 1924d3810ff9SJared McNeill if (bus_alloc_resources(dev, awg_spec, sc->res) != 0) { 1925d3810ff9SJared McNeill device_printf(dev, "cannot allocate resources for device\n"); 1926d3810ff9SJared McNeill return (ENXIO); 1927d3810ff9SJared McNeill } 1928d3810ff9SJared McNeill 1929d3810ff9SJared McNeill mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); 1930d3810ff9SJared McNeill callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); 1931d3810ff9SJared McNeill 1932d3810ff9SJared McNeill /* Setup clocks and regulators */ 1933d3810ff9SJared McNeill error = awg_setup_extres(dev); 1934d3810ff9SJared McNeill if (error != 0) 1935d3810ff9SJared McNeill return (error); 1936d3810ff9SJared McNeill 1937d3810ff9SJared McNeill /* Read MAC address before resetting the chip */ 1938d3810ff9SJared McNeill awg_get_eaddr(dev, eaddr); 1939d3810ff9SJared McNeill 1940a3a7d2a4SKyle Evans /* Soft reset EMAC core */ 1941a3a7d2a4SKyle Evans error = awg_reset(dev); 1942a3a7d2a4SKyle Evans if (error != 0) 1943d3810ff9SJared McNeill return (error); 1944d3810ff9SJared McNeill 1945d3810ff9SJared McNeill /* Setup DMA descriptors */ 1946d3810ff9SJared McNeill error = awg_setup_dma(dev); 1947d3810ff9SJared McNeill if (error != 0) 1948d3810ff9SJared McNeill return (error); 1949d3810ff9SJared McNeill 1950d3810ff9SJared McNeill /* Install interrupt handler */ 195101a469b8SJared McNeill error = bus_setup_intr(dev, sc->res[_RES_IRQ], 195201a469b8SJared McNeill INTR_TYPE_NET | INTR_MPSAFE, NULL, awg_intr, sc, &sc->ih); 1953d3810ff9SJared McNeill if (error != 0) { 1954d3810ff9SJared McNeill device_printf(dev, "cannot setup interrupt handler\n"); 1955d3810ff9SJared McNeill return (error); 1956d3810ff9SJared McNeill } 1957d3810ff9SJared McNeill 1958d3810ff9SJared McNeill /* Setup ethernet interface */ 1959d3810ff9SJared McNeill sc->ifp = if_alloc(IFT_ETHER); 1960d3810ff9SJared McNeill if_setsoftc(sc->ifp, sc); 1961d3810ff9SJared McNeill if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); 1962d3810ff9SJared McNeill if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 1963d3810ff9SJared McNeill if_setstartfn(sc->ifp, awg_start); 1964d3810ff9SJared McNeill if_setioctlfn(sc->ifp, awg_ioctl); 1965d3810ff9SJared McNeill if_setinitfn(sc->ifp, awg_init); 1966d3810ff9SJared McNeill if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); 1967d3810ff9SJared McNeill if_setsendqready(sc->ifp); 1968d3810ff9SJared McNeill if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); 1969d3810ff9SJared McNeill if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); 1970d3810ff9SJared McNeill if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); 197116928528SJared McNeill #ifdef DEVICE_POLLING 197216928528SJared McNeill if_setcapabilitiesbit(sc->ifp, IFCAP_POLLING, 0); 197316928528SJared McNeill #endif 1974d3810ff9SJared McNeill 1975d3810ff9SJared McNeill /* Attach MII driver */ 1976d3810ff9SJared McNeill error = mii_attach(dev, &sc->miibus, sc->ifp, awg_media_change, 1977d3810ff9SJared McNeill awg_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 1978d3810ff9SJared McNeill MIIF_DOPAUSE); 1979d3810ff9SJared McNeill if (error != 0) { 1980d3810ff9SJared McNeill device_printf(dev, "cannot attach PHY\n"); 1981d3810ff9SJared McNeill return (error); 1982d3810ff9SJared McNeill } 1983d3810ff9SJared McNeill 1984d3810ff9SJared McNeill /* Attach ethernet interface */ 1985d3810ff9SJared McNeill ether_ifattach(sc->ifp, eaddr); 1986d3810ff9SJared McNeill 1987d3810ff9SJared McNeill return (0); 1988d3810ff9SJared McNeill } 1989d3810ff9SJared McNeill 1990d3810ff9SJared McNeill static device_method_t awg_methods[] = { 1991d3810ff9SJared McNeill /* Device interface */ 1992d3810ff9SJared McNeill DEVMETHOD(device_probe, awg_probe), 1993d3810ff9SJared McNeill DEVMETHOD(device_attach, awg_attach), 1994d3810ff9SJared McNeill 1995d3810ff9SJared McNeill /* MII interface */ 1996d3810ff9SJared McNeill DEVMETHOD(miibus_readreg, awg_miibus_readreg), 1997d3810ff9SJared McNeill DEVMETHOD(miibus_writereg, awg_miibus_writereg), 1998d3810ff9SJared McNeill DEVMETHOD(miibus_statchg, awg_miibus_statchg), 1999d3810ff9SJared McNeill 2000d3810ff9SJared McNeill DEVMETHOD_END 2001d3810ff9SJared McNeill }; 2002d3810ff9SJared McNeill 2003d3810ff9SJared McNeill static driver_t awg_driver = { 2004d3810ff9SJared McNeill "awg", 2005d3810ff9SJared McNeill awg_methods, 2006d3810ff9SJared McNeill sizeof(struct awg_softc), 2007d3810ff9SJared McNeill }; 2008d3810ff9SJared McNeill 2009d3810ff9SJared McNeill static devclass_t awg_devclass; 2010d3810ff9SJared McNeill 2011d3810ff9SJared McNeill DRIVER_MODULE(awg, simplebus, awg_driver, awg_devclass, 0, 0); 2012d3810ff9SJared McNeill DRIVER_MODULE(miibus, awg, miibus_driver, miibus_devclass, 0, 0); 2013d3810ff9SJared McNeill MODULE_DEPEND(awg, ether, 1, 1, 1); 2014d3810ff9SJared McNeill MODULE_DEPEND(awg, miibus, 1, 1, 1); 201556c37d89SEmmanuel Vadot MODULE_DEPEND(awg, aw_sid, 1, 1, 1); 201656c37d89SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data); 2017