1*62e8bc8bSthorpej /* $NetBSD: sunxi_emac.c,v 1.37 2022/09/18 15:44:29 thorpej Exp $ */
29432a99aSjmcneill
39432a99aSjmcneill /*-
49432a99aSjmcneill * Copyright (c) 2016-2017 Jared McNeill <jmcneill@invisible.ca>
59432a99aSjmcneill * All rights reserved.
69432a99aSjmcneill *
79432a99aSjmcneill * Redistribution and use in source and binary forms, with or without
89432a99aSjmcneill * modification, are permitted provided that the following conditions
99432a99aSjmcneill * are met:
109432a99aSjmcneill * 1. Redistributions of source code must retain the above copyright
119432a99aSjmcneill * notice, this list of conditions and the following disclaimer.
129432a99aSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
139432a99aSjmcneill * notice, this list of conditions and the following disclaimer in the
149432a99aSjmcneill * documentation and/or other materials provided with the distribution.
159432a99aSjmcneill *
169432a99aSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
179432a99aSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
189432a99aSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
199432a99aSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
209432a99aSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
219432a99aSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
229432a99aSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
239432a99aSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
249432a99aSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259432a99aSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269432a99aSjmcneill * SUCH DAMAGE.
279432a99aSjmcneill */
289432a99aSjmcneill
299432a99aSjmcneill /*
309432a99aSjmcneill * Allwinner Gigabit Ethernet MAC (EMAC) controller
319432a99aSjmcneill */
329432a99aSjmcneill
339432a99aSjmcneill #include "opt_net_mpsafe.h"
349432a99aSjmcneill
359432a99aSjmcneill #include <sys/cdefs.h>
36*62e8bc8bSthorpej __KERNEL_RCSID(0, "$NetBSD: sunxi_emac.c,v 1.37 2022/09/18 15:44:29 thorpej Exp $");
379432a99aSjmcneill
389432a99aSjmcneill #include <sys/param.h>
399432a99aSjmcneill #include <sys/bus.h>
409432a99aSjmcneill #include <sys/device.h>
419432a99aSjmcneill #include <sys/intr.h>
429432a99aSjmcneill #include <sys/systm.h>
439432a99aSjmcneill #include <sys/kernel.h>
449432a99aSjmcneill #include <sys/mutex.h>
459432a99aSjmcneill #include <sys/callout.h>
469432a99aSjmcneill #include <sys/gpio.h>
479432a99aSjmcneill #include <sys/cprng.h>
489432a99aSjmcneill
499432a99aSjmcneill #include <net/if.h>
509432a99aSjmcneill #include <net/if_dl.h>
519432a99aSjmcneill #include <net/if_ether.h>
529432a99aSjmcneill #include <net/if_media.h>
539432a99aSjmcneill #include <net/bpf.h>
549432a99aSjmcneill
559432a99aSjmcneill #include <dev/mii/miivar.h>
569432a99aSjmcneill
579432a99aSjmcneill #include <dev/fdt/fdtvar.h>
5887578108Sjmcneill #include <dev/fdt/syscon.h>
599432a99aSjmcneill
609432a99aSjmcneill #include <arm/sunxi/sunxi_emac.h>
619432a99aSjmcneill
629432a99aSjmcneill #ifdef NET_MPSAFE
639432a99aSjmcneill #define EMAC_MPSAFE 1
649432a99aSjmcneill #define CALLOUT_FLAGS CALLOUT_MPSAFE
659432a99aSjmcneill #define FDT_INTR_FLAGS FDT_INTR_MPSAFE
669432a99aSjmcneill #else
679432a99aSjmcneill #define CALLOUT_FLAGS 0
689432a99aSjmcneill #define FDT_INTR_FLAGS 0
699432a99aSjmcneill #endif
709432a99aSjmcneill
719432a99aSjmcneill #define EMAC_IFNAME "emac%d"
729432a99aSjmcneill
739432a99aSjmcneill #define EMAC_LOCK(sc) mutex_enter(&(sc)->mtx)
749432a99aSjmcneill #define EMAC_UNLOCK(sc) mutex_exit(&(sc)->mtx)
759432a99aSjmcneill #define EMAC_ASSERT_LOCKED(sc) KASSERT(mutex_owned(&(sc)->mtx))
769432a99aSjmcneill
77450959deSjmcneill #define DESC_ALIGN sizeof(struct sunxi_emac_desc)
789432a99aSjmcneill #define TX_DESC_COUNT 1024
799432a99aSjmcneill #define TX_DESC_SIZE (sizeof(struct sunxi_emac_desc) * TX_DESC_COUNT)
809432a99aSjmcneill #define RX_DESC_COUNT 256
819432a99aSjmcneill #define RX_DESC_SIZE (sizeof(struct sunxi_emac_desc) * RX_DESC_COUNT)
829432a99aSjmcneill
839432a99aSjmcneill #define DESC_OFF(n) ((n) * sizeof(struct sunxi_emac_desc))
849432a99aSjmcneill #define TX_NEXT(n) (((n) + 1) & (TX_DESC_COUNT - 1))
859432a99aSjmcneill #define TX_SKIP(n, o) (((n) + (o)) & (TX_DESC_COUNT - 1))
869432a99aSjmcneill #define RX_NEXT(n) (((n) + 1) & (RX_DESC_COUNT - 1))
879432a99aSjmcneill
889432a99aSjmcneill #define TX_MAX_SEGS 128
899432a99aSjmcneill
909432a99aSjmcneill #define SOFT_RST_RETRY 1000
919432a99aSjmcneill #define MII_BUSY_RETRY 1000
929432a99aSjmcneill #define MDIO_FREQ 2500000
939432a99aSjmcneill
949432a99aSjmcneill #define BURST_LEN_DEFAULT 8
959432a99aSjmcneill #define RX_TX_PRI_DEFAULT 0
969432a99aSjmcneill #define PAUSE_TIME_DEFAULT 0x400
979432a99aSjmcneill
989432a99aSjmcneill /* syscon EMAC clock register */
9933381db0Sjmcneill #define EMAC_CLK_REG 0x30
1009432a99aSjmcneill #define EMAC_CLK_EPHY_ADDR (0x1f << 20) /* H3 */
1019432a99aSjmcneill #define EMAC_CLK_EPHY_ADDR_SHIFT 20
1029432a99aSjmcneill #define EMAC_CLK_EPHY_LED_POL (1 << 17) /* H3 */
1039432a99aSjmcneill #define EMAC_CLK_EPHY_SHUTDOWN (1 << 16) /* H3 */
1049432a99aSjmcneill #define EMAC_CLK_EPHY_SELECT (1 << 15) /* H3 */
1059432a99aSjmcneill #define EMAC_CLK_RMII_EN (1 << 13)
1069432a99aSjmcneill #define EMAC_CLK_ETXDC (0x7 << 10)
1079432a99aSjmcneill #define EMAC_CLK_ETXDC_SHIFT 10
1089432a99aSjmcneill #define EMAC_CLK_ERXDC (0x1f << 5)
1099432a99aSjmcneill #define EMAC_CLK_ERXDC_SHIFT 5
1109432a99aSjmcneill #define EMAC_CLK_PIT (0x1 << 2)
1119432a99aSjmcneill #define EMAC_CLK_PIT_MII (0 << 2)
1129432a99aSjmcneill #define EMAC_CLK_PIT_RGMII (1 << 2)
1139432a99aSjmcneill #define EMAC_CLK_SRC (0x3 << 0)
1149432a99aSjmcneill #define EMAC_CLK_SRC_MII (0 << 0)
1159432a99aSjmcneill #define EMAC_CLK_SRC_EXT_RGMII (1 << 0)
1169432a99aSjmcneill #define EMAC_CLK_SRC_RGMII (2 << 0)
1179432a99aSjmcneill
1189432a99aSjmcneill /* Burst length of RX and TX DMA transfers */
1199432a99aSjmcneill static int sunxi_emac_burst_len = BURST_LEN_DEFAULT;
1209432a99aSjmcneill
1219432a99aSjmcneill /* RX / TX DMA priority. If 1, RX DMA has priority over TX DMA. */
1229432a99aSjmcneill static int sunxi_emac_rx_tx_pri = RX_TX_PRI_DEFAULT;
1239432a99aSjmcneill
1249432a99aSjmcneill /* Pause time field in the transmitted control frame */
1259432a99aSjmcneill static int sunxi_emac_pause_time = PAUSE_TIME_DEFAULT;
1269432a99aSjmcneill
1279432a99aSjmcneill enum sunxi_emac_type {
1284156dcc5Sjmcneill EMAC_A64 = 1,
1294156dcc5Sjmcneill EMAC_A83T,
1309432a99aSjmcneill EMAC_H3,
1314156dcc5Sjmcneill EMAC_H6,
1329432a99aSjmcneill };
1339432a99aSjmcneill
134646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
135646c0f59Sthorpej { .compat = "allwinner,sun8i-a83t-emac", .value = EMAC_A83T },
136646c0f59Sthorpej { .compat = "allwinner,sun8i-h3-emac", .value = EMAC_H3 },
1375639be33Sskrll { .compat = "allwinner,sun8i-v3s-emac", .value = EMAC_H3 },
138646c0f59Sthorpej { .compat = "allwinner,sun50i-a64-emac", .value = EMAC_A64 },
139646c0f59Sthorpej { .compat = "allwinner,sun50i-h6-emac", .value = EMAC_H6 },
140ec189949Sthorpej DEVICE_COMPAT_EOL
1419432a99aSjmcneill };
1429432a99aSjmcneill
1439432a99aSjmcneill struct sunxi_emac_bufmap {
1449432a99aSjmcneill bus_dmamap_t map;
1459432a99aSjmcneill struct mbuf *mbuf;
1469432a99aSjmcneill };
1479432a99aSjmcneill
1489432a99aSjmcneill struct sunxi_emac_txring {
1499432a99aSjmcneill bus_dma_tag_t desc_tag;
1509432a99aSjmcneill bus_dmamap_t desc_map;
1519432a99aSjmcneill bus_dma_segment_t desc_dmaseg;
1529432a99aSjmcneill struct sunxi_emac_desc *desc_ring;
1539432a99aSjmcneill bus_addr_t desc_ring_paddr;
1549432a99aSjmcneill bus_dma_tag_t buf_tag;
1559432a99aSjmcneill struct sunxi_emac_bufmap buf_map[TX_DESC_COUNT];
1569432a99aSjmcneill u_int cur, next, queued;
1579432a99aSjmcneill };
1589432a99aSjmcneill
1599432a99aSjmcneill struct sunxi_emac_rxring {
1609432a99aSjmcneill bus_dma_tag_t desc_tag;
1619432a99aSjmcneill bus_dmamap_t desc_map;
1629432a99aSjmcneill bus_dma_segment_t desc_dmaseg;
1639432a99aSjmcneill struct sunxi_emac_desc *desc_ring;
1649432a99aSjmcneill bus_addr_t desc_ring_paddr;
1659432a99aSjmcneill bus_dma_tag_t buf_tag;
1669432a99aSjmcneill struct sunxi_emac_bufmap buf_map[RX_DESC_COUNT];
1679432a99aSjmcneill u_int cur;
1689432a99aSjmcneill };
1699432a99aSjmcneill
1709432a99aSjmcneill struct sunxi_emac_softc {
1719432a99aSjmcneill device_t dev;
1729432a99aSjmcneill int phandle;
1739432a99aSjmcneill enum sunxi_emac_type type;
1749432a99aSjmcneill bus_space_tag_t bst;
1759432a99aSjmcneill bus_dma_tag_t dmat;
1769432a99aSjmcneill
17787578108Sjmcneill bus_space_handle_t bsh;
1789432a99aSjmcneill struct clk *clk_ahb;
1799432a99aSjmcneill struct clk *clk_ephy;
1809432a99aSjmcneill struct fdtbus_reset *rst_ahb;
1819432a99aSjmcneill struct fdtbus_reset *rst_ephy;
1829432a99aSjmcneill struct fdtbus_regulator *reg_phy;
1839432a99aSjmcneill struct fdtbus_gpio_pin *pin_reset;
1849432a99aSjmcneill
18587578108Sjmcneill struct syscon *syscon;
18687578108Sjmcneill
18797456612Sjmcneill int phy_id;
18897456612Sjmcneill
1899432a99aSjmcneill kmutex_t mtx;
1909432a99aSjmcneill struct ethercom ec;
1919432a99aSjmcneill struct mii_data mii;
1929432a99aSjmcneill callout_t stat_ch;
1939432a99aSjmcneill void *ih;
1949432a99aSjmcneill u_int mdc_div_ratio_m;
1959432a99aSjmcneill
1969432a99aSjmcneill struct sunxi_emac_txring tx;
1979432a99aSjmcneill struct sunxi_emac_rxring rx;
1989432a99aSjmcneill };
1999432a99aSjmcneill
2009432a99aSjmcneill #define RD4(sc, reg) \
20187578108Sjmcneill bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
2029432a99aSjmcneill #define WR4(sc, reg, val) \
20387578108Sjmcneill bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
2049432a99aSjmcneill
2059432a99aSjmcneill static int
sunxi_emac_mii_readreg(device_t dev,int phy,int reg,uint16_t * val)206a5cdd4b4Smsaitoh sunxi_emac_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
2079432a99aSjmcneill {
2089432a99aSjmcneill struct sunxi_emac_softc *sc = device_private(dev);
209a5cdd4b4Smsaitoh int retry;
2109432a99aSjmcneill
2119432a99aSjmcneill WR4(sc, EMAC_MII_CMD,
2129432a99aSjmcneill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) |
2139432a99aSjmcneill (phy << PHY_ADDR_SHIFT) |
2149432a99aSjmcneill (reg << PHY_REG_ADDR_SHIFT) |
2159432a99aSjmcneill MII_BUSY);
2169432a99aSjmcneill for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
2179432a99aSjmcneill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0) {
218a5cdd4b4Smsaitoh *val = RD4(sc, EMAC_MII_DATA) & 0xffff;
2199432a99aSjmcneill break;
2209432a99aSjmcneill }
2219432a99aSjmcneill delay(10);
2229432a99aSjmcneill }
2239432a99aSjmcneill
224a5cdd4b4Smsaitoh if (retry == 0) {
2259432a99aSjmcneill device_printf(dev, "phy read timeout, phy=%d reg=%d\n",
2269432a99aSjmcneill phy, reg);
227a5cdd4b4Smsaitoh return ETIMEDOUT;
2289432a99aSjmcneill }
2299432a99aSjmcneill
230a5cdd4b4Smsaitoh return 0;
231a5cdd4b4Smsaitoh }
232a5cdd4b4Smsaitoh
233a5cdd4b4Smsaitoh static int
sunxi_emac_mii_writereg(device_t dev,int phy,int reg,uint16_t val)234a5cdd4b4Smsaitoh sunxi_emac_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
2359432a99aSjmcneill {
2369432a99aSjmcneill struct sunxi_emac_softc *sc = device_private(dev);
2379432a99aSjmcneill int retry;
2389432a99aSjmcneill
2399432a99aSjmcneill WR4(sc, EMAC_MII_DATA, val);
2409432a99aSjmcneill WR4(sc, EMAC_MII_CMD,
2419432a99aSjmcneill (sc->mdc_div_ratio_m << MDC_DIV_RATIO_M_SHIFT) |
2429432a99aSjmcneill (phy << PHY_ADDR_SHIFT) |
2439432a99aSjmcneill (reg << PHY_REG_ADDR_SHIFT) |
2449432a99aSjmcneill MII_WR | MII_BUSY);
2459432a99aSjmcneill for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
2469432a99aSjmcneill if ((RD4(sc, EMAC_MII_CMD) & MII_BUSY) == 0)
2479432a99aSjmcneill break;
2489432a99aSjmcneill delay(10);
2499432a99aSjmcneill }
2509432a99aSjmcneill
251a5cdd4b4Smsaitoh if (retry == 0) {
2529432a99aSjmcneill device_printf(dev, "phy write timeout, phy=%d reg=%d\n",
2539432a99aSjmcneill phy, reg);
254a5cdd4b4Smsaitoh return ETIMEDOUT;
255a5cdd4b4Smsaitoh }
256a5cdd4b4Smsaitoh
257a5cdd4b4Smsaitoh return 0;
2589432a99aSjmcneill }
2599432a99aSjmcneill
2609432a99aSjmcneill static void
sunxi_emac_update_link(struct sunxi_emac_softc * sc)2619432a99aSjmcneill sunxi_emac_update_link(struct sunxi_emac_softc *sc)
2629432a99aSjmcneill {
2639432a99aSjmcneill struct mii_data *mii = &sc->mii;
2649432a99aSjmcneill uint32_t val;
2659432a99aSjmcneill
2669432a99aSjmcneill val = RD4(sc, EMAC_BASIC_CTL_0);
2679432a99aSjmcneill val &= ~(BASIC_CTL_SPEED | BASIC_CTL_DUPLEX);
2689432a99aSjmcneill
2699432a99aSjmcneill if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
2709432a99aSjmcneill IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)
2719432a99aSjmcneill val |= BASIC_CTL_SPEED_1000 << BASIC_CTL_SPEED_SHIFT;
2729432a99aSjmcneill else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
2739432a99aSjmcneill val |= BASIC_CTL_SPEED_100 << BASIC_CTL_SPEED_SHIFT;
2749432a99aSjmcneill else
2759432a99aSjmcneill val |= BASIC_CTL_SPEED_10 << BASIC_CTL_SPEED_SHIFT;
2769432a99aSjmcneill
2779432a99aSjmcneill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
2789432a99aSjmcneill val |= BASIC_CTL_DUPLEX;
2799432a99aSjmcneill
2809432a99aSjmcneill WR4(sc, EMAC_BASIC_CTL_0, val);
2819432a99aSjmcneill
2829432a99aSjmcneill val = RD4(sc, EMAC_RX_CTL_0);
2839432a99aSjmcneill val &= ~RX_FLOW_CTL_EN;
2849432a99aSjmcneill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
2859432a99aSjmcneill val |= RX_FLOW_CTL_EN;
2869432a99aSjmcneill WR4(sc, EMAC_RX_CTL_0, val);
2879432a99aSjmcneill
2889432a99aSjmcneill val = RD4(sc, EMAC_TX_FLOW_CTL);
2899432a99aSjmcneill val &= ~(PAUSE_TIME | TX_FLOW_CTL_EN);
2909432a99aSjmcneill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
2919432a99aSjmcneill val |= TX_FLOW_CTL_EN;
2929432a99aSjmcneill if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
2939432a99aSjmcneill val |= sunxi_emac_pause_time << PAUSE_TIME_SHIFT;
2949432a99aSjmcneill WR4(sc, EMAC_TX_FLOW_CTL, val);
2959432a99aSjmcneill }
2969432a99aSjmcneill
2979432a99aSjmcneill static void
sunxi_emac_mii_statchg(struct ifnet * ifp)2989432a99aSjmcneill sunxi_emac_mii_statchg(struct ifnet *ifp)
2999432a99aSjmcneill {
3009432a99aSjmcneill struct sunxi_emac_softc * const sc = ifp->if_softc;
3019432a99aSjmcneill
3029432a99aSjmcneill sunxi_emac_update_link(sc);
3039432a99aSjmcneill }
3049432a99aSjmcneill
3059432a99aSjmcneill static void
sunxi_emac_dma_sync(struct sunxi_emac_softc * sc,bus_dma_tag_t dmat,bus_dmamap_t map,int start,int end,int total,int flags)3069432a99aSjmcneill sunxi_emac_dma_sync(struct sunxi_emac_softc *sc, bus_dma_tag_t dmat,
3079432a99aSjmcneill bus_dmamap_t map, int start, int end, int total, int flags)
3089432a99aSjmcneill {
3099432a99aSjmcneill if (end > start) {
3109432a99aSjmcneill bus_dmamap_sync(dmat, map, DESC_OFF(start),
3119432a99aSjmcneill DESC_OFF(end) - DESC_OFF(start), flags);
3129432a99aSjmcneill } else {
3139432a99aSjmcneill bus_dmamap_sync(dmat, map, DESC_OFF(start),
3149432a99aSjmcneill DESC_OFF(total) - DESC_OFF(start), flags);
315450959deSjmcneill if (DESC_OFF(end) - DESC_OFF(0) > 0)
3169432a99aSjmcneill bus_dmamap_sync(dmat, map, DESC_OFF(0),
3179432a99aSjmcneill DESC_OFF(end) - DESC_OFF(0), flags);
3189432a99aSjmcneill }
3199432a99aSjmcneill }
3209432a99aSjmcneill
3219432a99aSjmcneill static void
sunxi_emac_setup_txdesc(struct sunxi_emac_softc * sc,int index,int flags,bus_addr_t paddr,u_int len)3229432a99aSjmcneill sunxi_emac_setup_txdesc(struct sunxi_emac_softc *sc, int index, int flags,
3239432a99aSjmcneill bus_addr_t paddr, u_int len)
3249432a99aSjmcneill {
3259432a99aSjmcneill uint32_t status, size;
3269432a99aSjmcneill
3279432a99aSjmcneill if (paddr == 0 || len == 0) {
3289432a99aSjmcneill status = 0;
3299432a99aSjmcneill size = 0;
3309432a99aSjmcneill --sc->tx.queued;
3319432a99aSjmcneill } else {
3329432a99aSjmcneill status = TX_DESC_CTL;
3339432a99aSjmcneill size = flags | len;
3349432a99aSjmcneill ++sc->tx.queued;
3359432a99aSjmcneill }
3369432a99aSjmcneill
3379432a99aSjmcneill sc->tx.desc_ring[index].addr = htole32((uint32_t)paddr);
3389432a99aSjmcneill sc->tx.desc_ring[index].size = htole32(size);
3399432a99aSjmcneill sc->tx.desc_ring[index].status = htole32(status);
3409432a99aSjmcneill }
3419432a99aSjmcneill
3429432a99aSjmcneill static int
sunxi_emac_setup_txbuf(struct sunxi_emac_softc * sc,int index,struct mbuf * m)3439432a99aSjmcneill sunxi_emac_setup_txbuf(struct sunxi_emac_softc *sc, int index, struct mbuf *m)
3449432a99aSjmcneill {
3459432a99aSjmcneill bus_dma_segment_t *segs;
3469432a99aSjmcneill int error, nsegs, cur, i, flags;
3479432a99aSjmcneill u_int csum_flags;
3489432a99aSjmcneill
3499432a99aSjmcneill error = bus_dmamap_load_mbuf(sc->tx.buf_tag,
3509432a99aSjmcneill sc->tx.buf_map[index].map, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
3519432a99aSjmcneill if (error == EFBIG) {
3529432a99aSjmcneill device_printf(sc->dev,
3539432a99aSjmcneill "TX packet needs too many DMA segments, dropping...\n");
354*62e8bc8bSthorpej /* Caller will dequeue and free packet. */
355*62e8bc8bSthorpej return -1;
3569432a99aSjmcneill }
3579432a99aSjmcneill if (error != 0)
3589432a99aSjmcneill return 0;
3599432a99aSjmcneill
3609432a99aSjmcneill segs = sc->tx.buf_map[index].map->dm_segs;
3619432a99aSjmcneill nsegs = sc->tx.buf_map[index].map->dm_nsegs;
3629432a99aSjmcneill
3639432a99aSjmcneill flags = TX_FIR_DESC;
3649432a99aSjmcneill if ((m->m_pkthdr.csum_flags & M_CSUM_IPv4) != 0) {
3659432a99aSjmcneill if ((m->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) != 0)
3669432a99aSjmcneill csum_flags = TX_CHECKSUM_CTL_FULL;
3679432a99aSjmcneill else
3689432a99aSjmcneill csum_flags = TX_CHECKSUM_CTL_IP;
3699432a99aSjmcneill flags |= (csum_flags << TX_CHECKSUM_CTL_SHIFT);
3709432a99aSjmcneill }
3719432a99aSjmcneill
3729432a99aSjmcneill for (cur = index, i = 0; i < nsegs; i++) {
3739432a99aSjmcneill sc->tx.buf_map[cur].mbuf = (i == 0 ? m : NULL);
3749432a99aSjmcneill if (i == nsegs - 1)
375d1b80ee1Sjmcneill flags |= TX_LAST_DESC | TX_INT_CTL;
3769432a99aSjmcneill
3779432a99aSjmcneill sunxi_emac_setup_txdesc(sc, cur, flags, segs[i].ds_addr,
3789432a99aSjmcneill segs[i].ds_len);
3799432a99aSjmcneill flags &= ~TX_FIR_DESC;
3809432a99aSjmcneill cur = TX_NEXT(cur);
3819432a99aSjmcneill }
3829432a99aSjmcneill
383450959deSjmcneill bus_dmamap_sync(sc->tx.buf_tag, sc->tx.buf_map[index].map,
384450959deSjmcneill 0, sc->tx.buf_map[index].map->dm_mapsize, BUS_DMASYNC_PREWRITE);
385450959deSjmcneill
3869432a99aSjmcneill return nsegs;
3879432a99aSjmcneill }
3889432a99aSjmcneill
3899432a99aSjmcneill static void
sunxi_emac_setup_rxdesc(struct sunxi_emac_softc * sc,int index,bus_addr_t paddr)3909432a99aSjmcneill sunxi_emac_setup_rxdesc(struct sunxi_emac_softc *sc, int index,
3919432a99aSjmcneill bus_addr_t paddr)
3929432a99aSjmcneill {
3939432a99aSjmcneill uint32_t status, size;
3949432a99aSjmcneill
3959432a99aSjmcneill status = RX_DESC_CTL;
3969432a99aSjmcneill size = MCLBYTES - 1;
3979432a99aSjmcneill
3989432a99aSjmcneill sc->rx.desc_ring[index].addr = htole32((uint32_t)paddr);
3999432a99aSjmcneill sc->rx.desc_ring[index].size = htole32(size);
4009432a99aSjmcneill sc->rx.desc_ring[index].next =
4019432a99aSjmcneill htole32(sc->rx.desc_ring_paddr + DESC_OFF(RX_NEXT(index)));
4029432a99aSjmcneill sc->rx.desc_ring[index].status = htole32(status);
4039432a99aSjmcneill }
4049432a99aSjmcneill
4059432a99aSjmcneill static int
sunxi_emac_setup_rxbuf(struct sunxi_emac_softc * sc,int index,struct mbuf * m)4069432a99aSjmcneill sunxi_emac_setup_rxbuf(struct sunxi_emac_softc *sc, int index, struct mbuf *m)
4079432a99aSjmcneill {
4089432a99aSjmcneill int error;
4099432a99aSjmcneill
4109432a99aSjmcneill m_adj(m, ETHER_ALIGN);
4119432a99aSjmcneill
4129432a99aSjmcneill error = bus_dmamap_load_mbuf(sc->rx.buf_tag,
4139432a99aSjmcneill sc->rx.buf_map[index].map, m, BUS_DMA_READ | BUS_DMA_NOWAIT);
4149432a99aSjmcneill if (error != 0)
4159432a99aSjmcneill return error;
4169432a99aSjmcneill
4179432a99aSjmcneill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map,
4189432a99aSjmcneill 0, sc->rx.buf_map[index].map->dm_mapsize,
4199432a99aSjmcneill BUS_DMASYNC_PREREAD);
4209432a99aSjmcneill
4219432a99aSjmcneill sc->rx.buf_map[index].mbuf = m;
4229432a99aSjmcneill sunxi_emac_setup_rxdesc(sc, index,
4239432a99aSjmcneill sc->rx.buf_map[index].map->dm_segs[0].ds_addr);
4249432a99aSjmcneill
4259432a99aSjmcneill return 0;
4269432a99aSjmcneill }
4279432a99aSjmcneill
4289432a99aSjmcneill static struct mbuf *
sunxi_emac_alloc_mbufcl(struct sunxi_emac_softc * sc)4299432a99aSjmcneill sunxi_emac_alloc_mbufcl(struct sunxi_emac_softc *sc)
4309432a99aSjmcneill {
4319432a99aSjmcneill struct mbuf *m;
4329432a99aSjmcneill
4339432a99aSjmcneill m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
4349432a99aSjmcneill if (m != NULL)
4359432a99aSjmcneill m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
4369432a99aSjmcneill
4379432a99aSjmcneill return m;
4389432a99aSjmcneill }
4399432a99aSjmcneill
4409432a99aSjmcneill static void
sunxi_emac_start_locked(struct sunxi_emac_softc * sc)4419432a99aSjmcneill sunxi_emac_start_locked(struct sunxi_emac_softc *sc)
4429432a99aSjmcneill {
4439432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
4449432a99aSjmcneill struct mbuf *m;
4459432a99aSjmcneill uint32_t val;
4469432a99aSjmcneill int cnt, nsegs, start;
4479432a99aSjmcneill
4489432a99aSjmcneill EMAC_ASSERT_LOCKED(sc);
4499432a99aSjmcneill
450*62e8bc8bSthorpej if ((ifp->if_flags & IFF_RUNNING) == 0)
4519432a99aSjmcneill return;
4529432a99aSjmcneill
4539432a99aSjmcneill for (cnt = 0, start = sc->tx.cur; ; cnt++) {
4549432a99aSjmcneill if (sc->tx.queued >= TX_DESC_COUNT - TX_MAX_SEGS) {
4559432a99aSjmcneill break;
4569432a99aSjmcneill }
4579432a99aSjmcneill
4589432a99aSjmcneill IFQ_POLL(&ifp->if_snd, m);
4599432a99aSjmcneill if (m == NULL)
4609432a99aSjmcneill break;
4619432a99aSjmcneill
4629432a99aSjmcneill nsegs = sunxi_emac_setup_txbuf(sc, sc->tx.cur, m);
463*62e8bc8bSthorpej if (__predict_false(nsegs <= 0)) {
464*62e8bc8bSthorpej if (nsegs == -1) {
465*62e8bc8bSthorpej /*
466*62e8bc8bSthorpej * We're being asked to discard this packet,
467*62e8bc8bSthorpej * but we can try to continue.
468*62e8bc8bSthorpej */
469*62e8bc8bSthorpej IFQ_DEQUEUE(&ifp->if_snd, m);
470*62e8bc8bSthorpej m_freem(m);
471*62e8bc8bSthorpej continue;
472*62e8bc8bSthorpej }
4739432a99aSjmcneill break;
4749432a99aSjmcneill }
4759432a99aSjmcneill IFQ_DEQUEUE(&ifp->if_snd, m);
4763cd62456Smsaitoh bpf_mtap(ifp, m, BPF_D_OUT);
4779432a99aSjmcneill
4789432a99aSjmcneill sc->tx.cur = TX_SKIP(sc->tx.cur, nsegs);
4799432a99aSjmcneill }
4809432a99aSjmcneill
4819432a99aSjmcneill if (cnt != 0) {
4829432a99aSjmcneill sunxi_emac_dma_sync(sc, sc->tx.desc_tag, sc->tx.desc_map,
4839432a99aSjmcneill start, sc->tx.cur, TX_DESC_COUNT,
4849432a99aSjmcneill BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
4859432a99aSjmcneill
4869432a99aSjmcneill /* Start and run TX DMA */
4879432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_1);
4889432a99aSjmcneill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_START);
4899432a99aSjmcneill }
4909432a99aSjmcneill }
4919432a99aSjmcneill
4929432a99aSjmcneill static void
sunxi_emac_start(struct ifnet * ifp)4939432a99aSjmcneill sunxi_emac_start(struct ifnet *ifp)
4949432a99aSjmcneill {
4959432a99aSjmcneill struct sunxi_emac_softc *sc = ifp->if_softc;
4969432a99aSjmcneill
4979432a99aSjmcneill EMAC_LOCK(sc);
4989432a99aSjmcneill sunxi_emac_start_locked(sc);
4999432a99aSjmcneill EMAC_UNLOCK(sc);
5009432a99aSjmcneill }
5019432a99aSjmcneill
5029432a99aSjmcneill static void
sunxi_emac_tick(void * softc)5039432a99aSjmcneill sunxi_emac_tick(void *softc)
5049432a99aSjmcneill {
5059432a99aSjmcneill struct sunxi_emac_softc *sc = softc;
5069432a99aSjmcneill struct mii_data *mii = &sc->mii;
5079432a99aSjmcneill #ifndef EMAC_MPSAFE
5089432a99aSjmcneill int s = splnet();
5099432a99aSjmcneill #endif
5109432a99aSjmcneill
5119432a99aSjmcneill EMAC_LOCK(sc);
5129432a99aSjmcneill mii_tick(mii);
5139432a99aSjmcneill callout_schedule(&sc->stat_ch, hz);
5149432a99aSjmcneill EMAC_UNLOCK(sc);
5159432a99aSjmcneill
5169432a99aSjmcneill #ifndef EMAC_MPSAFE
5179432a99aSjmcneill splx(s);
5189432a99aSjmcneill #endif
5199432a99aSjmcneill }
5209432a99aSjmcneill
5219432a99aSjmcneill /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */
5229432a99aSjmcneill static uint32_t
bitrev32(uint32_t x)5239432a99aSjmcneill bitrev32(uint32_t x)
5249432a99aSjmcneill {
5259432a99aSjmcneill x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
5269432a99aSjmcneill x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
5279432a99aSjmcneill x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
5289432a99aSjmcneill x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
5299432a99aSjmcneill
5309432a99aSjmcneill return (x >> 16) | (x << 16);
5319432a99aSjmcneill }
5329432a99aSjmcneill
5339432a99aSjmcneill static void
sunxi_emac_setup_rxfilter(struct sunxi_emac_softc * sc)5349432a99aSjmcneill sunxi_emac_setup_rxfilter(struct sunxi_emac_softc *sc)
5359432a99aSjmcneill {
536932c4f3eSmsaitoh struct ethercom *ec = &sc->ec;
537932c4f3eSmsaitoh struct ifnet *ifp = &ec->ec_if;
5389432a99aSjmcneill uint32_t val, crc, hashreg, hashbit, hash[2], machi, maclo;
5399432a99aSjmcneill struct ether_multi *enm;
5409432a99aSjmcneill struct ether_multistep step;
5419432a99aSjmcneill const uint8_t *eaddr;
5429432a99aSjmcneill
5439432a99aSjmcneill EMAC_ASSERT_LOCKED(sc);
5449432a99aSjmcneill
5459432a99aSjmcneill val = 0;
5469432a99aSjmcneill hash[0] = hash[1] = 0;
5479432a99aSjmcneill
5489432a99aSjmcneill if ((ifp->if_flags & IFF_PROMISC) != 0)
5499432a99aSjmcneill val |= DIS_ADDR_FILTER;
5509432a99aSjmcneill else if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
5519432a99aSjmcneill val |= RX_ALL_MULTICAST;
5529432a99aSjmcneill hash[0] = hash[1] = ~0;
5539432a99aSjmcneill } else {
5549432a99aSjmcneill val |= HASH_MULTICAST;
555932c4f3eSmsaitoh ETHER_LOCK(ec);
556932c4f3eSmsaitoh ETHER_FIRST_MULTI(step, ec, enm);
5579432a99aSjmcneill while (enm != NULL) {
5589432a99aSjmcneill crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
5599432a99aSjmcneill crc &= 0x7f;
5609432a99aSjmcneill crc = bitrev32(~crc) >> 26;
5619432a99aSjmcneill hashreg = (crc >> 5);
5629432a99aSjmcneill hashbit = (crc & 0x1f);
5639432a99aSjmcneill hash[hashreg] |= (1 << hashbit);
5649432a99aSjmcneill ETHER_NEXT_MULTI(step, enm);
5659432a99aSjmcneill }
566932c4f3eSmsaitoh ETHER_UNLOCK(ec);
5679432a99aSjmcneill }
5689432a99aSjmcneill
5699432a99aSjmcneill /* Write our unicast address */
5709432a99aSjmcneill eaddr = CLLADDR(ifp->if_sadl);
5719432a99aSjmcneill machi = (eaddr[5] << 8) | eaddr[4];
5729432a99aSjmcneill maclo = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) |
5739432a99aSjmcneill (eaddr[0] << 0);
5749432a99aSjmcneill WR4(sc, EMAC_ADDR_HIGH(0), machi);
5759432a99aSjmcneill WR4(sc, EMAC_ADDR_LOW(0), maclo);
5769432a99aSjmcneill
5779432a99aSjmcneill /* Multicast hash filters */
5789432a99aSjmcneill WR4(sc, EMAC_RX_HASH_0, hash[1]);
5799432a99aSjmcneill WR4(sc, EMAC_RX_HASH_1, hash[0]);
5809432a99aSjmcneill
5819432a99aSjmcneill /* RX frame filter config */
5829432a99aSjmcneill WR4(sc, EMAC_RX_FRM_FLT, val);
5839432a99aSjmcneill }
5849432a99aSjmcneill
5859432a99aSjmcneill static void
sunxi_emac_enable_intr(struct sunxi_emac_softc * sc)5869432a99aSjmcneill sunxi_emac_enable_intr(struct sunxi_emac_softc *sc)
5879432a99aSjmcneill {
5889432a99aSjmcneill /* Enable interrupts */
5899432a99aSjmcneill WR4(sc, EMAC_INT_EN, RX_INT_EN | TX_INT_EN | TX_BUF_UA_INT_EN);
5909432a99aSjmcneill }
5919432a99aSjmcneill
5929432a99aSjmcneill static void
sunxi_emac_disable_intr(struct sunxi_emac_softc * sc)5939432a99aSjmcneill sunxi_emac_disable_intr(struct sunxi_emac_softc *sc)
5949432a99aSjmcneill {
5959432a99aSjmcneill /* Disable interrupts */
5969432a99aSjmcneill WR4(sc, EMAC_INT_EN, 0);
5979432a99aSjmcneill }
5989432a99aSjmcneill
599006e901cSmartin #ifdef SUNXI_EMAC_DEBUG
600006e901cSmartin static void
sunxi_emac_dump_regs(struct sunxi_emac_softc * sc)601006e901cSmartin sunxi_emac_dump_regs(struct sunxi_emac_softc *sc)
602006e901cSmartin {
603006e901cSmartin static const struct {
604006e901cSmartin const char *name;
605006e901cSmartin u_int reg;
606006e901cSmartin } regs[] = {
607006e901cSmartin { "BASIC_CTL_0", EMAC_BASIC_CTL_0 },
608006e901cSmartin { "BASIC_CTL_1", EMAC_BASIC_CTL_1 },
609006e901cSmartin { "INT_STA", EMAC_INT_STA },
610006e901cSmartin { "INT_EN", EMAC_INT_EN },
611006e901cSmartin { "TX_CTL_0", EMAC_TX_CTL_0 },
612006e901cSmartin { "TX_CTL_1", EMAC_TX_CTL_1 },
613006e901cSmartin { "TX_FLOW_CTL", EMAC_TX_FLOW_CTL },
614006e901cSmartin { "TX_DMA_LIST", EMAC_TX_DMA_LIST },
615006e901cSmartin { "RX_CTL_0", EMAC_RX_CTL_0 },
616006e901cSmartin { "RX_CTL_1", EMAC_RX_CTL_1 },
617006e901cSmartin { "RX_DMA_LIST", EMAC_RX_DMA_LIST },
618006e901cSmartin { "RX_FRM_FLT", EMAC_RX_FRM_FLT },
619006e901cSmartin { "RX_HASH_0", EMAC_RX_HASH_0 },
620006e901cSmartin { "RX_HASH_1", EMAC_RX_HASH_1 },
621006e901cSmartin { "MII_CMD", EMAC_MII_CMD },
622006e901cSmartin { "ADDR_HIGH0", EMAC_ADDR_HIGH(0) },
623006e901cSmartin { "ADDR_LOW0", EMAC_ADDR_LOW(0) },
624006e901cSmartin { "TX_DMA_STA", EMAC_TX_DMA_STA },
625006e901cSmartin { "TX_DMA_CUR_DESC", EMAC_TX_DMA_CUR_DESC },
626006e901cSmartin { "TX_DMA_CUR_BUF", EMAC_TX_DMA_CUR_BUF },
627006e901cSmartin { "RX_DMA_STA", EMAC_RX_DMA_STA },
628006e901cSmartin { "RX_DMA_CUR_DESC", EMAC_RX_DMA_CUR_DESC },
629006e901cSmartin { "RX_DMA_CUR_BUF", EMAC_RX_DMA_CUR_BUF },
630006e901cSmartin { "RGMII_STA", EMAC_RGMII_STA },
631006e901cSmartin };
632006e901cSmartin u_int n;
633006e901cSmartin
634006e901cSmartin for (n = 0; n < __arraycount(regs); n++)
635006e901cSmartin device_printf(sc->dev, " %-20s %08x\n", regs[n].name,
636006e901cSmartin RD4(sc, regs[n].reg));
637006e901cSmartin }
638006e901cSmartin #endif
639006e901cSmartin
6409432a99aSjmcneill static int
sunxi_emac_reset(struct sunxi_emac_softc * sc)641ef41c138Sjmcneill sunxi_emac_reset(struct sunxi_emac_softc *sc)
642ef41c138Sjmcneill {
643ef41c138Sjmcneill int retry;
644ef41c138Sjmcneill
645ef41c138Sjmcneill /* Soft reset all registers and logic */
646ef41c138Sjmcneill WR4(sc, EMAC_BASIC_CTL_1, BASIC_CTL_SOFT_RST);
647ef41c138Sjmcneill
648ef41c138Sjmcneill /* Wait for soft reset bit to self-clear */
649ef41c138Sjmcneill for (retry = SOFT_RST_RETRY; retry > 0; retry--) {
650ef41c138Sjmcneill if ((RD4(sc, EMAC_BASIC_CTL_1) & BASIC_CTL_SOFT_RST) == 0)
651ef41c138Sjmcneill break;
652ef41c138Sjmcneill delay(10);
653ef41c138Sjmcneill }
654ef41c138Sjmcneill if (retry == 0) {
655ef41c138Sjmcneill aprint_debug_dev(sc->dev, "soft reset timed out\n");
656ef41c138Sjmcneill #ifdef SUNXI_EMAC_DEBUG
657ef41c138Sjmcneill sunxi_emac_dump_regs(sc);
658ef41c138Sjmcneill #endif
659ef41c138Sjmcneill return ETIMEDOUT;
660ef41c138Sjmcneill }
661ef41c138Sjmcneill
662ef41c138Sjmcneill return 0;
663ef41c138Sjmcneill }
664ef41c138Sjmcneill
665ef41c138Sjmcneill static int
sunxi_emac_init_locked(struct sunxi_emac_softc * sc)6669432a99aSjmcneill sunxi_emac_init_locked(struct sunxi_emac_softc *sc)
6679432a99aSjmcneill {
6689432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
6699432a99aSjmcneill struct mii_data *mii = &sc->mii;
6709432a99aSjmcneill uint32_t val;
6719432a99aSjmcneill
6729432a99aSjmcneill EMAC_ASSERT_LOCKED(sc);
6739432a99aSjmcneill
6749432a99aSjmcneill if ((ifp->if_flags & IFF_RUNNING) != 0)
6759432a99aSjmcneill return 0;
6769432a99aSjmcneill
677ef41c138Sjmcneill /* Soft reset EMAC core */
678ef41c138Sjmcneill sunxi_emac_reset(sc);
679ef41c138Sjmcneill
680ef41c138Sjmcneill /* Write transmit and receive descriptor base address registers */
681ef41c138Sjmcneill WR4(sc, EMAC_TX_DMA_LIST, sc->tx.desc_ring_paddr);
682ef41c138Sjmcneill WR4(sc, EMAC_RX_DMA_LIST, sc->rx.desc_ring_paddr);
683ef41c138Sjmcneill
6849432a99aSjmcneill sunxi_emac_setup_rxfilter(sc);
6859432a99aSjmcneill
6869432a99aSjmcneill /* Configure DMA burst length and priorities */
6879432a99aSjmcneill val = sunxi_emac_burst_len << BASIC_CTL_BURST_LEN_SHIFT;
6889432a99aSjmcneill if (sunxi_emac_rx_tx_pri)
6899432a99aSjmcneill val |= BASIC_CTL_RX_TX_PRI;
6909432a99aSjmcneill WR4(sc, EMAC_BASIC_CTL_1, val);
6919432a99aSjmcneill
6929432a99aSjmcneill /* Enable interrupts */
6939432a99aSjmcneill sunxi_emac_enable_intr(sc);
6949432a99aSjmcneill
6959432a99aSjmcneill /* Enable transmit DMA */
6969432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_1);
6979432a99aSjmcneill WR4(sc, EMAC_TX_CTL_1, val | TX_DMA_EN | TX_MD | TX_NEXT_FRAME);
6989432a99aSjmcneill
6999432a99aSjmcneill /* Enable receive DMA */
7009432a99aSjmcneill val = RD4(sc, EMAC_RX_CTL_1);
7019432a99aSjmcneill WR4(sc, EMAC_RX_CTL_1, val | RX_DMA_EN | RX_MD);
7029432a99aSjmcneill
7039432a99aSjmcneill /* Enable transmitter */
7049432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_0);
7059432a99aSjmcneill WR4(sc, EMAC_TX_CTL_0, val | TX_EN);
7069432a99aSjmcneill
7079432a99aSjmcneill /* Enable receiver */
7089432a99aSjmcneill val = RD4(sc, EMAC_RX_CTL_0);
7099432a99aSjmcneill WR4(sc, EMAC_RX_CTL_0, val | RX_EN | CHECK_CRC);
7109432a99aSjmcneill
7119432a99aSjmcneill ifp->if_flags |= IFF_RUNNING;
7129432a99aSjmcneill
7139432a99aSjmcneill mii_mediachg(mii);
7149432a99aSjmcneill callout_schedule(&sc->stat_ch, hz);
7159432a99aSjmcneill
7169432a99aSjmcneill return 0;
7179432a99aSjmcneill }
7189432a99aSjmcneill
7199432a99aSjmcneill static int
sunxi_emac_init(struct ifnet * ifp)7209432a99aSjmcneill sunxi_emac_init(struct ifnet *ifp)
7219432a99aSjmcneill {
7229432a99aSjmcneill struct sunxi_emac_softc *sc = ifp->if_softc;
7239432a99aSjmcneill int error;
7249432a99aSjmcneill
7259432a99aSjmcneill EMAC_LOCK(sc);
7269432a99aSjmcneill error = sunxi_emac_init_locked(sc);
7279432a99aSjmcneill EMAC_UNLOCK(sc);
7289432a99aSjmcneill
7299432a99aSjmcneill return error;
7309432a99aSjmcneill }
7319432a99aSjmcneill
7329432a99aSjmcneill static void
sunxi_emac_stop_locked(struct sunxi_emac_softc * sc,int disable)7339432a99aSjmcneill sunxi_emac_stop_locked(struct sunxi_emac_softc *sc, int disable)
7349432a99aSjmcneill {
7359432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
7369432a99aSjmcneill uint32_t val;
7379432a99aSjmcneill
7389432a99aSjmcneill EMAC_ASSERT_LOCKED(sc);
7399432a99aSjmcneill
7409432a99aSjmcneill callout_stop(&sc->stat_ch);
7419432a99aSjmcneill
7429432a99aSjmcneill mii_down(&sc->mii);
7439432a99aSjmcneill
7449432a99aSjmcneill /* Stop transmit DMA and flush data in the TX FIFO */
7459432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_1);
7469432a99aSjmcneill val &= ~TX_DMA_EN;
7479432a99aSjmcneill val |= FLUSH_TX_FIFO;
7489432a99aSjmcneill WR4(sc, EMAC_TX_CTL_1, val);
7499432a99aSjmcneill
7509432a99aSjmcneill /* Disable transmitter */
7519432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_0);
7529432a99aSjmcneill WR4(sc, EMAC_TX_CTL_0, val & ~TX_EN);
7539432a99aSjmcneill
7549432a99aSjmcneill /* Disable receiver */
7559432a99aSjmcneill val = RD4(sc, EMAC_RX_CTL_0);
7569432a99aSjmcneill WR4(sc, EMAC_RX_CTL_0, val & ~RX_EN);
7579432a99aSjmcneill
7589432a99aSjmcneill /* Disable interrupts */
7599432a99aSjmcneill sunxi_emac_disable_intr(sc);
7609432a99aSjmcneill
7619432a99aSjmcneill /* Disable transmit DMA */
7629432a99aSjmcneill val = RD4(sc, EMAC_TX_CTL_1);
7639432a99aSjmcneill WR4(sc, EMAC_TX_CTL_1, val & ~TX_DMA_EN);
7649432a99aSjmcneill
7659432a99aSjmcneill /* Disable receive DMA */
7669432a99aSjmcneill val = RD4(sc, EMAC_RX_CTL_1);
7679432a99aSjmcneill WR4(sc, EMAC_RX_CTL_1, val & ~RX_DMA_EN);
7689432a99aSjmcneill
769*62e8bc8bSthorpej ifp->if_flags &= ~IFF_RUNNING;
7709432a99aSjmcneill }
7719432a99aSjmcneill
7729432a99aSjmcneill static void
sunxi_emac_stop(struct ifnet * ifp,int disable)7739432a99aSjmcneill sunxi_emac_stop(struct ifnet *ifp, int disable)
7749432a99aSjmcneill {
7759432a99aSjmcneill struct sunxi_emac_softc * const sc = ifp->if_softc;
7769432a99aSjmcneill
7779432a99aSjmcneill EMAC_LOCK(sc);
7789432a99aSjmcneill sunxi_emac_stop_locked(sc, disable);
7799432a99aSjmcneill EMAC_UNLOCK(sc);
7809432a99aSjmcneill }
7819432a99aSjmcneill
7829432a99aSjmcneill static int
sunxi_emac_rxintr(struct sunxi_emac_softc * sc)7839432a99aSjmcneill sunxi_emac_rxintr(struct sunxi_emac_softc *sc)
7849432a99aSjmcneill {
7859432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
786538dd477Sjmcneill int error, index, len, npkt;
787538dd477Sjmcneill struct mbuf *m, *m0;
7889432a99aSjmcneill uint32_t status;
7899432a99aSjmcneill
7909432a99aSjmcneill npkt = 0;
7919432a99aSjmcneill
7929432a99aSjmcneill for (index = sc->rx.cur; ; index = RX_NEXT(index)) {
7939432a99aSjmcneill sunxi_emac_dma_sync(sc, sc->rx.desc_tag, sc->rx.desc_map,
794932c4f3eSmsaitoh index, index + 1, RX_DESC_COUNT,
795932c4f3eSmsaitoh BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
7969432a99aSjmcneill
7979432a99aSjmcneill status = le32toh(sc->rx.desc_ring[index].status);
7989432a99aSjmcneill if ((status & RX_DESC_CTL) != 0)
7999432a99aSjmcneill break;
8009432a99aSjmcneill
8019432a99aSjmcneill bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map,
8029432a99aSjmcneill 0, sc->rx.buf_map[index].map->dm_mapsize,
8039432a99aSjmcneill BUS_DMASYNC_POSTREAD);
8049432a99aSjmcneill bus_dmamap_unload(sc->rx.buf_tag, sc->rx.buf_map[index].map);
8059432a99aSjmcneill
8069432a99aSjmcneill len = (status & RX_FRM_LEN) >> RX_FRM_LEN_SHIFT;
8079432a99aSjmcneill if (len != 0) {
8089432a99aSjmcneill m = sc->rx.buf_map[index].mbuf;
8099432a99aSjmcneill m_set_rcvif(m, ifp);
8109432a99aSjmcneill m->m_flags |= M_HASFCS;
8119432a99aSjmcneill m->m_pkthdr.len = len;
8129432a99aSjmcneill m->m_len = len;
813538dd477Sjmcneill m->m_nextpkt = NULL;
8149432a99aSjmcneill
8159432a99aSjmcneill if ((ifp->if_capenable & IFCAP_CSUM_IPv4_Rx) != 0 &&
8169432a99aSjmcneill (status & RX_FRM_TYPE) != 0) {
817d1b80ee1Sjmcneill m->m_pkthdr.csum_flags = M_CSUM_IPv4 |
818d1b80ee1Sjmcneill M_CSUM_TCPv4 | M_CSUM_UDPv4;
8199432a99aSjmcneill if ((status & RX_HEADER_ERR) != 0)
8209432a99aSjmcneill m->m_pkthdr.csum_flags |=
8219432a99aSjmcneill M_CSUM_IPv4_BAD;
822d1b80ee1Sjmcneill if ((status & RX_PAYLOAD_ERR) != 0)
8239432a99aSjmcneill m->m_pkthdr.csum_flags |=
824d1b80ee1Sjmcneill M_CSUM_TCP_UDP_BAD;
8259432a99aSjmcneill }
8269432a99aSjmcneill
8279432a99aSjmcneill ++npkt;
8289432a99aSjmcneill
829538dd477Sjmcneill if_percpuq_enqueue(ifp->if_percpuq, m);
8309432a99aSjmcneill }
8319432a99aSjmcneill
8329432a99aSjmcneill if ((m0 = sunxi_emac_alloc_mbufcl(sc)) != NULL) {
8339432a99aSjmcneill error = sunxi_emac_setup_rxbuf(sc, index, m0);
8349432a99aSjmcneill if (error != 0) {
8359432a99aSjmcneill /* XXX hole in RX ring */
8369432a99aSjmcneill }
8379432a99aSjmcneill } else
838d18dc548Sthorpej if_statinc(ifp, if_ierrors);
8399432a99aSjmcneill
8409432a99aSjmcneill sunxi_emac_dma_sync(sc, sc->rx.desc_tag, sc->rx.desc_map,
8419432a99aSjmcneill index, index + 1,
842450959deSjmcneill RX_DESC_COUNT, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
8439432a99aSjmcneill }
8449432a99aSjmcneill
8459432a99aSjmcneill sc->rx.cur = index;
8469432a99aSjmcneill
8479432a99aSjmcneill return npkt;
8489432a99aSjmcneill }
8499432a99aSjmcneill
8509432a99aSjmcneill static void
sunxi_emac_txintr(struct sunxi_emac_softc * sc)8519432a99aSjmcneill sunxi_emac_txintr(struct sunxi_emac_softc *sc)
8529432a99aSjmcneill {
8539432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
8549432a99aSjmcneill struct sunxi_emac_bufmap *bmap;
8559432a99aSjmcneill struct sunxi_emac_desc *desc;
8569432a99aSjmcneill uint32_t status;
8579432a99aSjmcneill int i;
8589432a99aSjmcneill
8599432a99aSjmcneill EMAC_ASSERT_LOCKED(sc);
8609432a99aSjmcneill
8619432a99aSjmcneill for (i = sc->tx.next; sc->tx.queued > 0; i = TX_NEXT(i)) {
8629432a99aSjmcneill KASSERT(sc->tx.queued > 0 && sc->tx.queued <= TX_DESC_COUNT);
8639432a99aSjmcneill sunxi_emac_dma_sync(sc, sc->tx.desc_tag, sc->tx.desc_map,
8649432a99aSjmcneill i, i + 1, TX_DESC_COUNT,
8659432a99aSjmcneill BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
8669432a99aSjmcneill desc = &sc->tx.desc_ring[i];
8679432a99aSjmcneill status = le32toh(desc->status);
8689432a99aSjmcneill if ((status & TX_DESC_CTL) != 0)
8699432a99aSjmcneill break;
8709432a99aSjmcneill bmap = &sc->tx.buf_map[i];
8719432a99aSjmcneill if (bmap->mbuf != NULL) {
8729432a99aSjmcneill bus_dmamap_sync(sc->tx.buf_tag, bmap->map,
8739432a99aSjmcneill 0, bmap->map->dm_mapsize,
8749432a99aSjmcneill BUS_DMASYNC_POSTWRITE);
8759432a99aSjmcneill bus_dmamap_unload(sc->tx.buf_tag, bmap->map);
8769432a99aSjmcneill m_freem(bmap->mbuf);
8779432a99aSjmcneill bmap->mbuf = NULL;
8789432a99aSjmcneill }
8799432a99aSjmcneill
8809432a99aSjmcneill sunxi_emac_setup_txdesc(sc, i, 0, 0, 0);
881450959deSjmcneill sunxi_emac_dma_sync(sc, sc->tx.desc_tag, sc->tx.desc_map,
882450959deSjmcneill i, i + 1, TX_DESC_COUNT,
883450959deSjmcneill BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
8849432a99aSjmcneill
885d18dc548Sthorpej if_statinc(ifp, if_opackets);
8869432a99aSjmcneill }
8879432a99aSjmcneill
8889432a99aSjmcneill sc->tx.next = i;
8899432a99aSjmcneill }
8909432a99aSjmcneill
8919432a99aSjmcneill static int
sunxi_emac_intr(void * arg)8929432a99aSjmcneill sunxi_emac_intr(void *arg)
8939432a99aSjmcneill {
8949432a99aSjmcneill struct sunxi_emac_softc *sc = arg;
8959432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
8969432a99aSjmcneill uint32_t val;
8979432a99aSjmcneill
8989432a99aSjmcneill EMAC_LOCK(sc);
8999432a99aSjmcneill
9009432a99aSjmcneill val = RD4(sc, EMAC_INT_STA);
9019432a99aSjmcneill WR4(sc, EMAC_INT_STA, val);
9029432a99aSjmcneill
9039432a99aSjmcneill if (val & RX_INT)
9049432a99aSjmcneill sunxi_emac_rxintr(sc);
9059432a99aSjmcneill
9069432a99aSjmcneill if (val & (TX_INT | TX_BUF_UA_INT)) {
9079432a99aSjmcneill sunxi_emac_txintr(sc);
9089432a99aSjmcneill if_schedule_deferred_start(ifp);
9099432a99aSjmcneill }
9109432a99aSjmcneill
9119432a99aSjmcneill EMAC_UNLOCK(sc);
9129432a99aSjmcneill
9139432a99aSjmcneill return 1;
9149432a99aSjmcneill }
9159432a99aSjmcneill
9169432a99aSjmcneill static int
sunxi_emac_ioctl(struct ifnet * ifp,u_long cmd,void * data)9179432a99aSjmcneill sunxi_emac_ioctl(struct ifnet *ifp, u_long cmd, void *data)
9189432a99aSjmcneill {
9199432a99aSjmcneill struct sunxi_emac_softc *sc = ifp->if_softc;
9209432a99aSjmcneill int error, s;
9219432a99aSjmcneill
9229432a99aSjmcneill #ifndef EMAC_MPSAFE
9239432a99aSjmcneill s = splnet();
9249432a99aSjmcneill #endif
9259432a99aSjmcneill
9269432a99aSjmcneill switch (cmd) {
9279432a99aSjmcneill default:
9289432a99aSjmcneill #ifdef EMAC_MPSAFE
9299432a99aSjmcneill s = splnet();
9309432a99aSjmcneill #endif
9319432a99aSjmcneill error = ether_ioctl(ifp, cmd, data);
9329432a99aSjmcneill #ifdef EMAC_MPSAFE
9339432a99aSjmcneill splx(s);
9349432a99aSjmcneill #endif
9359432a99aSjmcneill if (error != ENETRESET)
9369432a99aSjmcneill break;
9379432a99aSjmcneill
9389432a99aSjmcneill error = 0;
9399432a99aSjmcneill
9409432a99aSjmcneill if (cmd == SIOCSIFCAP)
941b4d088cbSriastradh error = if_init(ifp);
9429432a99aSjmcneill else if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
9439432a99aSjmcneill ;
9449432a99aSjmcneill else if ((ifp->if_flags & IFF_RUNNING) != 0) {
9459432a99aSjmcneill EMAC_LOCK(sc);
9469432a99aSjmcneill sunxi_emac_setup_rxfilter(sc);
9479432a99aSjmcneill EMAC_UNLOCK(sc);
9489432a99aSjmcneill }
9499432a99aSjmcneill break;
9509432a99aSjmcneill }
9519432a99aSjmcneill
9529432a99aSjmcneill #ifndef EMAC_MPSAFE
9539432a99aSjmcneill splx(s);
9549432a99aSjmcneill #endif
9559432a99aSjmcneill
9569432a99aSjmcneill return error;
9579432a99aSjmcneill }
9589432a99aSjmcneill
95933381db0Sjmcneill static bool
sunxi_emac_has_internal_phy(struct sunxi_emac_softc * sc)96033381db0Sjmcneill sunxi_emac_has_internal_phy(struct sunxi_emac_softc *sc)
96133381db0Sjmcneill {
962ec189949Sthorpej static const struct device_compatible_entry mdio_internal_compat[] = {
963ec189949Sthorpej { .compat = "allwinner,sun8i-h3-mdio-internal" },
964ec189949Sthorpej DEVICE_COMPAT_EOL
96533381db0Sjmcneill };
96633381db0Sjmcneill int phy;
96733381db0Sjmcneill
96833381db0Sjmcneill /* Non-standard property, for compatible with old dts files */
96933381db0Sjmcneill if (of_hasprop(sc->phandle, "allwinner,use-internal-phy"))
97033381db0Sjmcneill return true;
97133381db0Sjmcneill
97233381db0Sjmcneill phy = fdtbus_get_phandle(sc->phandle, "phy-handle");
97333381db0Sjmcneill if (phy == -1)
97433381db0Sjmcneill return false;
97533381db0Sjmcneill
97633381db0Sjmcneill /* For internal PHY, check compatible string of parent node */
9776e54367aSthorpej return of_compatible_match(OF_parent(phy), mdio_internal_compat);
97833381db0Sjmcneill }
97933381db0Sjmcneill
9809432a99aSjmcneill static int
sunxi_emac_setup_phy(struct sunxi_emac_softc * sc)9819432a99aSjmcneill sunxi_emac_setup_phy(struct sunxi_emac_softc *sc)
9829432a99aSjmcneill {
9839432a99aSjmcneill uint32_t reg, tx_delay, rx_delay;
9849432a99aSjmcneill const char *phy_type;
9859432a99aSjmcneill
9869432a99aSjmcneill phy_type = fdtbus_get_string(sc->phandle, "phy-mode");
9879432a99aSjmcneill if (phy_type == NULL)
9889432a99aSjmcneill return 0;
9899432a99aSjmcneill
9909432a99aSjmcneill aprint_debug_dev(sc->dev, "PHY type: %s\n", phy_type);
9919432a99aSjmcneill
99287578108Sjmcneill syscon_lock(sc->syscon);
99387578108Sjmcneill reg = syscon_read_4(sc->syscon, EMAC_CLK_REG);
9949432a99aSjmcneill
9959432a99aSjmcneill reg &= ~(EMAC_CLK_PIT | EMAC_CLK_SRC | EMAC_CLK_RMII_EN);
996e0e63425Sjmcneill if (strncmp(phy_type, "rgmii", 5) == 0)
9979432a99aSjmcneill reg |= EMAC_CLK_PIT_RGMII | EMAC_CLK_SRC_RGMII;
9989432a99aSjmcneill else if (strcmp(phy_type, "rmii") == 0)
9999432a99aSjmcneill reg |= EMAC_CLK_RMII_EN;
10009432a99aSjmcneill else
10019432a99aSjmcneill reg |= EMAC_CLK_PIT_MII | EMAC_CLK_SRC_MII;
10029432a99aSjmcneill
10034156dcc5Sjmcneill if (of_getprop_uint32(sc->phandle, "allwinner,tx-delay-ps",
10044156dcc5Sjmcneill &tx_delay) == 0) {
10054156dcc5Sjmcneill reg &= ~EMAC_CLK_ETXDC;
10064156dcc5Sjmcneill reg |= ((tx_delay / 100) << EMAC_CLK_ETXDC_SHIFT);
10074156dcc5Sjmcneill } else if (of_getprop_uint32(sc->phandle, "tx-delay", &tx_delay) == 0) {
10089432a99aSjmcneill reg &= ~EMAC_CLK_ETXDC;
10099432a99aSjmcneill reg |= (tx_delay << EMAC_CLK_ETXDC_SHIFT);
10109432a99aSjmcneill }
10114156dcc5Sjmcneill if (of_getprop_uint32(sc->phandle, "allwinner,rx-delay-ps",
10124156dcc5Sjmcneill &rx_delay) == 0) {
10134156dcc5Sjmcneill reg &= ~EMAC_CLK_ERXDC;
10144156dcc5Sjmcneill reg |= ((rx_delay / 100) << EMAC_CLK_ERXDC_SHIFT);
10154156dcc5Sjmcneill } else if (of_getprop_uint32(sc->phandle, "rx-delay", &rx_delay) == 0) {
10169432a99aSjmcneill reg &= ~EMAC_CLK_ERXDC;
10179432a99aSjmcneill reg |= (rx_delay << EMAC_CLK_ERXDC_SHIFT);
10189432a99aSjmcneill }
10199432a99aSjmcneill
102002e565c6Sjmcneill if (sc->type == EMAC_H3 || sc->type == EMAC_H6) {
102133381db0Sjmcneill if (sunxi_emac_has_internal_phy(sc)) {
10229432a99aSjmcneill reg |= EMAC_CLK_EPHY_SELECT;
10239432a99aSjmcneill reg &= ~EMAC_CLK_EPHY_SHUTDOWN;
10249432a99aSjmcneill if (of_hasprop(sc->phandle,
10259432a99aSjmcneill "allwinner,leds-active-low"))
10269432a99aSjmcneill reg |= EMAC_CLK_EPHY_LED_POL;
10279432a99aSjmcneill else
10289432a99aSjmcneill reg &= ~EMAC_CLK_EPHY_LED_POL;
10299432a99aSjmcneill
10309432a99aSjmcneill /* Set internal PHY addr to 1 */
10319432a99aSjmcneill reg &= ~EMAC_CLK_EPHY_ADDR;
10329432a99aSjmcneill reg |= (1 << EMAC_CLK_EPHY_ADDR_SHIFT);
10339432a99aSjmcneill } else {
10349432a99aSjmcneill reg &= ~EMAC_CLK_EPHY_SELECT;
10359432a99aSjmcneill }
10369432a99aSjmcneill }
10379432a99aSjmcneill
10389432a99aSjmcneill aprint_debug_dev(sc->dev, "EMAC clock: 0x%08x\n", reg);
10399432a99aSjmcneill
104087578108Sjmcneill syscon_write_4(sc->syscon, EMAC_CLK_REG, reg);
104187578108Sjmcneill syscon_unlock(sc->syscon);
10429432a99aSjmcneill
10439432a99aSjmcneill return 0;
10449432a99aSjmcneill }
10459432a99aSjmcneill
10469432a99aSjmcneill static int
sunxi_emac_setup_resources(struct sunxi_emac_softc * sc)10479432a99aSjmcneill sunxi_emac_setup_resources(struct sunxi_emac_softc *sc)
10489432a99aSjmcneill {
10499432a99aSjmcneill u_int freq;
10509432a99aSjmcneill int error, div;
10519432a99aSjmcneill
10529432a99aSjmcneill /* Configure PHY for MII or RGMII mode */
10539432a99aSjmcneill if (sunxi_emac_setup_phy(sc) != 0)
10549432a99aSjmcneill return ENXIO;
10559432a99aSjmcneill
10569432a99aSjmcneill /* Enable clocks */
10579432a99aSjmcneill error = clk_enable(sc->clk_ahb);
10589432a99aSjmcneill if (error != 0) {
10599432a99aSjmcneill aprint_error_dev(sc->dev, "cannot enable ahb clock\n");
10609432a99aSjmcneill return error;
10619432a99aSjmcneill }
10629432a99aSjmcneill
10639432a99aSjmcneill if (sc->clk_ephy != NULL) {
10649432a99aSjmcneill error = clk_enable(sc->clk_ephy);
10659432a99aSjmcneill if (error != 0) {
10669432a99aSjmcneill aprint_error_dev(sc->dev, "cannot enable ephy clock\n");
10679432a99aSjmcneill return error;
10689432a99aSjmcneill }
10699432a99aSjmcneill }
10709432a99aSjmcneill
10719432a99aSjmcneill /* De-assert reset */
10729432a99aSjmcneill error = fdtbus_reset_deassert(sc->rst_ahb);
10739432a99aSjmcneill if (error != 0) {
10749432a99aSjmcneill aprint_error_dev(sc->dev, "cannot de-assert ahb reset\n");
10759432a99aSjmcneill return error;
10769432a99aSjmcneill }
10779432a99aSjmcneill if (sc->rst_ephy != NULL) {
10789432a99aSjmcneill error = fdtbus_reset_deassert(sc->rst_ephy);
10799432a99aSjmcneill if (error != 0) {
10809432a99aSjmcneill aprint_error_dev(sc->dev,
10819432a99aSjmcneill "cannot de-assert ephy reset\n");
10829432a99aSjmcneill return error;
10839432a99aSjmcneill }
10849432a99aSjmcneill }
10859432a99aSjmcneill
10869432a99aSjmcneill /* Enable PHY regulator if applicable */
10879432a99aSjmcneill if (sc->reg_phy != NULL) {
10889432a99aSjmcneill error = fdtbus_regulator_enable(sc->reg_phy);
10899432a99aSjmcneill if (error != 0) {
10909432a99aSjmcneill aprint_error_dev(sc->dev,
10919432a99aSjmcneill "cannot enable PHY regulator\n");
10929432a99aSjmcneill return error;
10939432a99aSjmcneill }
10949432a99aSjmcneill }
10959432a99aSjmcneill
10969432a99aSjmcneill /* Determine MDC clock divide ratio based on AHB clock */
10979432a99aSjmcneill freq = clk_get_rate(sc->clk_ahb);
10989432a99aSjmcneill if (freq == 0) {
10999432a99aSjmcneill aprint_error_dev(sc->dev, "cannot get AHB clock frequency\n");
11009432a99aSjmcneill return ENXIO;
11019432a99aSjmcneill }
11029432a99aSjmcneill div = freq / MDIO_FREQ;
11039432a99aSjmcneill if (div <= 16)
11049432a99aSjmcneill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_16;
11059432a99aSjmcneill else if (div <= 32)
11069432a99aSjmcneill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_32;
11079432a99aSjmcneill else if (div <= 64)
11089432a99aSjmcneill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_64;
11099432a99aSjmcneill else if (div <= 128)
11109432a99aSjmcneill sc->mdc_div_ratio_m = MDC_DIV_RATIO_M_128;
11119432a99aSjmcneill else {
11129432a99aSjmcneill aprint_error_dev(sc->dev,
11139432a99aSjmcneill "cannot determine MDC clock divide ratio\n");
11149432a99aSjmcneill return ENXIO;
11159432a99aSjmcneill }
11169432a99aSjmcneill
11179432a99aSjmcneill aprint_debug_dev(sc->dev, "AHB frequency %u Hz, MDC div: 0x%x\n",
11189432a99aSjmcneill freq, sc->mdc_div_ratio_m);
11199432a99aSjmcneill
11209432a99aSjmcneill return 0;
11219432a99aSjmcneill }
11229432a99aSjmcneill
11239432a99aSjmcneill static void
sunxi_emac_get_eaddr(struct sunxi_emac_softc * sc,uint8_t * eaddr)11249432a99aSjmcneill sunxi_emac_get_eaddr(struct sunxi_emac_softc *sc, uint8_t *eaddr)
11259432a99aSjmcneill {
11269432a99aSjmcneill uint32_t maclo, machi;
11279432a99aSjmcneill #if notyet
11289432a99aSjmcneill u_char rootkey[16];
11299432a99aSjmcneill #endif
11309432a99aSjmcneill
11319432a99aSjmcneill machi = RD4(sc, EMAC_ADDR_HIGH(0)) & 0xffff;
11329432a99aSjmcneill maclo = RD4(sc, EMAC_ADDR_LOW(0));
11339432a99aSjmcneill
11349432a99aSjmcneill if (maclo == 0xffffffff && machi == 0xffff) {
11359432a99aSjmcneill #if notyet
11369432a99aSjmcneill /* MAC address in hardware is invalid, create one */
11379432a99aSjmcneill if (aw_sid_get_rootkey(rootkey) == 0 &&
11389432a99aSjmcneill (rootkey[3] | rootkey[12] | rootkey[13] | rootkey[14] |
11399432a99aSjmcneill rootkey[15]) != 0) {
11409432a99aSjmcneill /* MAC address is derived from the root key in SID */
11419432a99aSjmcneill maclo = (rootkey[13] << 24) | (rootkey[12] << 16) |
11429432a99aSjmcneill (rootkey[3] << 8) | 0x02;
11439432a99aSjmcneill machi = (rootkey[15] << 8) | rootkey[14];
11449432a99aSjmcneill } else {
11459432a99aSjmcneill #endif
11469432a99aSjmcneill /* Create one */
11479432a99aSjmcneill maclo = 0x00f2 | (cprng_strong32() & 0xffff0000);
11489432a99aSjmcneill machi = cprng_strong32() & 0xffff;
11499432a99aSjmcneill #if notyet
11509432a99aSjmcneill }
11519432a99aSjmcneill #endif
11529432a99aSjmcneill }
11539432a99aSjmcneill
11549432a99aSjmcneill eaddr[0] = maclo & 0xff;
11559432a99aSjmcneill eaddr[1] = (maclo >> 8) & 0xff;
11569432a99aSjmcneill eaddr[2] = (maclo >> 16) & 0xff;
11579432a99aSjmcneill eaddr[3] = (maclo >> 24) & 0xff;
11589432a99aSjmcneill eaddr[4] = machi & 0xff;
11599432a99aSjmcneill eaddr[5] = (machi >> 8) & 0xff;
11609432a99aSjmcneill }
11619432a99aSjmcneill
11629432a99aSjmcneill static int
sunxi_emac_phy_reset(struct sunxi_emac_softc * sc)11639432a99aSjmcneill sunxi_emac_phy_reset(struct sunxi_emac_softc *sc)
11649432a99aSjmcneill {
11659432a99aSjmcneill uint32_t delay_prop[3];
11669432a99aSjmcneill int pin_value;
11679432a99aSjmcneill
11689432a99aSjmcneill if (sc->pin_reset == NULL)
11699432a99aSjmcneill return 0;
11709432a99aSjmcneill
11719432a99aSjmcneill if (OF_getprop(sc->phandle, "allwinner,reset-delays-us", delay_prop,
11729432a99aSjmcneill sizeof(delay_prop)) <= 0)
11739432a99aSjmcneill return ENXIO;
11749432a99aSjmcneill
11759432a99aSjmcneill pin_value = of_hasprop(sc->phandle, "allwinner,reset-active-low");
11769432a99aSjmcneill
11779432a99aSjmcneill fdtbus_gpio_write(sc->pin_reset, pin_value);
11789432a99aSjmcneill delay(htole32(delay_prop[0]));
11799432a99aSjmcneill fdtbus_gpio_write(sc->pin_reset, !pin_value);
11809432a99aSjmcneill delay(htole32(delay_prop[1]));
11819432a99aSjmcneill fdtbus_gpio_write(sc->pin_reset, pin_value);
11829432a99aSjmcneill delay(htole32(delay_prop[2]));
11839432a99aSjmcneill
11849432a99aSjmcneill return 0;
11859432a99aSjmcneill }
11869432a99aSjmcneill
11879432a99aSjmcneill static int
sunxi_emac_setup_dma(struct sunxi_emac_softc * sc)11889432a99aSjmcneill sunxi_emac_setup_dma(struct sunxi_emac_softc *sc)
11899432a99aSjmcneill {
11909432a99aSjmcneill struct mbuf *m;
11919432a99aSjmcneill int error, nsegs, i;
11929432a99aSjmcneill
11939432a99aSjmcneill /* Setup TX ring */
11949432a99aSjmcneill sc->tx.buf_tag = sc->tx.desc_tag = sc->dmat;
11959432a99aSjmcneill error = bus_dmamap_create(sc->dmat, TX_DESC_SIZE, 1, TX_DESC_SIZE, 0,
11969432a99aSjmcneill BUS_DMA_WAITOK, &sc->tx.desc_map);
11979432a99aSjmcneill if (error)
11989432a99aSjmcneill return error;
11999432a99aSjmcneill error = bus_dmamem_alloc(sc->dmat, TX_DESC_SIZE, DESC_ALIGN, 0,
12009432a99aSjmcneill &sc->tx.desc_dmaseg, 1, &nsegs, BUS_DMA_WAITOK);
12019432a99aSjmcneill if (error)
12029432a99aSjmcneill return error;
12039432a99aSjmcneill error = bus_dmamem_map(sc->dmat, &sc->tx.desc_dmaseg, nsegs,
12049432a99aSjmcneill TX_DESC_SIZE, (void *)&sc->tx.desc_ring,
1205450959deSjmcneill BUS_DMA_WAITOK);
12069432a99aSjmcneill if (error)
12079432a99aSjmcneill return error;
12089432a99aSjmcneill error = bus_dmamap_load(sc->dmat, sc->tx.desc_map, sc->tx.desc_ring,
12099432a99aSjmcneill TX_DESC_SIZE, NULL, BUS_DMA_WAITOK);
12109432a99aSjmcneill if (error)
12119432a99aSjmcneill return error;
12129432a99aSjmcneill sc->tx.desc_ring_paddr = sc->tx.desc_map->dm_segs[0].ds_addr;
12139432a99aSjmcneill
12149432a99aSjmcneill memset(sc->tx.desc_ring, 0, TX_DESC_SIZE);
12159432a99aSjmcneill bus_dmamap_sync(sc->dmat, sc->tx.desc_map, 0, TX_DESC_SIZE,
1216450959deSjmcneill BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
12179432a99aSjmcneill
12189432a99aSjmcneill for (i = 0; i < TX_DESC_COUNT; i++)
12199432a99aSjmcneill sc->tx.desc_ring[i].next =
12209432a99aSjmcneill htole32(sc->tx.desc_ring_paddr + DESC_OFF(TX_NEXT(i)));
12219432a99aSjmcneill
12229432a99aSjmcneill sc->tx.queued = TX_DESC_COUNT;
12239432a99aSjmcneill for (i = 0; i < TX_DESC_COUNT; i++) {
12249432a99aSjmcneill error = bus_dmamap_create(sc->tx.buf_tag, MCLBYTES,
12259432a99aSjmcneill TX_MAX_SEGS, MCLBYTES, 0, BUS_DMA_WAITOK,
12269432a99aSjmcneill &sc->tx.buf_map[i].map);
12279432a99aSjmcneill if (error != 0) {
12289432a99aSjmcneill device_printf(sc->dev, "cannot create TX buffer map\n");
12299432a99aSjmcneill return error;
12309432a99aSjmcneill }
12319432a99aSjmcneill sunxi_emac_setup_txdesc(sc, i, 0, 0, 0);
12329432a99aSjmcneill }
12339432a99aSjmcneill
12349432a99aSjmcneill /* Setup RX ring */
12359432a99aSjmcneill sc->rx.buf_tag = sc->rx.desc_tag = sc->dmat;
12369432a99aSjmcneill error = bus_dmamap_create(sc->dmat, RX_DESC_SIZE, 1, RX_DESC_SIZE, 0,
12379432a99aSjmcneill BUS_DMA_WAITOK, &sc->rx.desc_map);
12389432a99aSjmcneill if (error)
12399432a99aSjmcneill return error;
12409432a99aSjmcneill error = bus_dmamem_alloc(sc->dmat, RX_DESC_SIZE, DESC_ALIGN, 0,
12419432a99aSjmcneill &sc->rx.desc_dmaseg, 1, &nsegs, BUS_DMA_WAITOK);
12429432a99aSjmcneill if (error)
12439432a99aSjmcneill return error;
12449432a99aSjmcneill error = bus_dmamem_map(sc->dmat, &sc->rx.desc_dmaseg, nsegs,
12459432a99aSjmcneill RX_DESC_SIZE, (void *)&sc->rx.desc_ring,
1246450959deSjmcneill BUS_DMA_WAITOK);
12479432a99aSjmcneill if (error)
12489432a99aSjmcneill return error;
12499432a99aSjmcneill error = bus_dmamap_load(sc->dmat, sc->rx.desc_map, sc->rx.desc_ring,
12509432a99aSjmcneill RX_DESC_SIZE, NULL, BUS_DMA_WAITOK);
12519432a99aSjmcneill if (error)
12529432a99aSjmcneill return error;
12539432a99aSjmcneill sc->rx.desc_ring_paddr = sc->rx.desc_map->dm_segs[0].ds_addr;
12549432a99aSjmcneill
12559432a99aSjmcneill memset(sc->rx.desc_ring, 0, RX_DESC_SIZE);
12569432a99aSjmcneill
12579432a99aSjmcneill for (i = 0; i < RX_DESC_COUNT; i++) {
12589432a99aSjmcneill error = bus_dmamap_create(sc->rx.buf_tag, MCLBYTES,
12599432a99aSjmcneill RX_DESC_COUNT, MCLBYTES, 0, BUS_DMA_WAITOK,
12609432a99aSjmcneill &sc->rx.buf_map[i].map);
12619432a99aSjmcneill if (error != 0) {
12629432a99aSjmcneill device_printf(sc->dev, "cannot create RX buffer map\n");
12639432a99aSjmcneill return error;
12649432a99aSjmcneill }
12659432a99aSjmcneill if ((m = sunxi_emac_alloc_mbufcl(sc)) == NULL) {
12669432a99aSjmcneill device_printf(sc->dev, "cannot allocate RX mbuf\n");
12679432a99aSjmcneill return ENOMEM;
12689432a99aSjmcneill }
12699432a99aSjmcneill error = sunxi_emac_setup_rxbuf(sc, i, m);
12709432a99aSjmcneill if (error != 0) {
12719432a99aSjmcneill device_printf(sc->dev, "cannot create RX buffer\n");
12729432a99aSjmcneill return error;
12739432a99aSjmcneill }
12749432a99aSjmcneill }
12759432a99aSjmcneill bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map,
12769432a99aSjmcneill 0, sc->rx.desc_map->dm_mapsize,
1277450959deSjmcneill BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
12789432a99aSjmcneill
12799432a99aSjmcneill return 0;
12809432a99aSjmcneill }
12819432a99aSjmcneill
12829432a99aSjmcneill static int
sunxi_emac_get_resources(struct sunxi_emac_softc * sc)12839432a99aSjmcneill sunxi_emac_get_resources(struct sunxi_emac_softc *sc)
12849432a99aSjmcneill {
12859432a99aSjmcneill const int phandle = sc->phandle;
12869432a99aSjmcneill bus_addr_t addr, size;
12879432a99aSjmcneill
128833381db0Sjmcneill /* Map EMAC registers */
1289664653b0Sthorpej if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1290664653b0Sthorpej aprint_error_dev(sc->dev, "unable to get registers\n");
12919432a99aSjmcneill return ENXIO;
1292664653b0Sthorpej }
1293664653b0Sthorpej if (bus_space_map(sc->bst, addr, size, 0, &sc->bsh) != 0) {
1294664653b0Sthorpej aprint_error_dev(sc->dev, "unable to map registers\n");
129533381db0Sjmcneill return ENXIO;
1296664653b0Sthorpej }
129733381db0Sjmcneill
129887578108Sjmcneill /* Get SYSCON registers */
129987578108Sjmcneill sc->syscon = fdtbus_syscon_acquire(phandle, "syscon");
1300664653b0Sthorpej if (sc->syscon == NULL) {
1301664653b0Sthorpej aprint_error_dev(sc->dev, "unable to acquire syscon\n");
130233381db0Sjmcneill return ENXIO;
1303664653b0Sthorpej }
13049432a99aSjmcneill
13057b8150dfSjmcneill /* The "ahb"/"stmmaceth" clock and reset is required */
13067b8150dfSjmcneill if ((sc->clk_ahb = fdtbus_clock_get(phandle, "ahb")) == NULL &&
1307664653b0Sthorpej (sc->clk_ahb = fdtbus_clock_get(phandle, "stmmaceth")) == NULL) {
1308664653b0Sthorpej aprint_error_dev(sc->dev, "unable to get clock\n");
13099432a99aSjmcneill return ENXIO;
1310664653b0Sthorpej }
13117b8150dfSjmcneill if ((sc->rst_ahb = fdtbus_reset_get(phandle, "ahb")) == NULL &&
1312664653b0Sthorpej (sc->rst_ahb = fdtbus_reset_get(phandle, "stmmaceth")) == NULL) {
1313664653b0Sthorpej aprint_error_dev(sc->dev, "unable to get reset\n");
13147b8150dfSjmcneill return ENXIO;
1315664653b0Sthorpej }
13167b8150dfSjmcneill
13177b8150dfSjmcneill /* Internal PHY clock and reset are optional properties. */
13189432a99aSjmcneill sc->clk_ephy = fdtbus_clock_get(phandle, "ephy");
13197b8150dfSjmcneill if (sc->clk_ephy == NULL) {
13207b8150dfSjmcneill int phy_phandle = fdtbus_get_phandle(phandle, "phy-handle");
13217b8150dfSjmcneill if (phy_phandle != -1)
13227b8150dfSjmcneill sc->clk_ephy = fdtbus_clock_get_index(phy_phandle, 0);
13237b8150dfSjmcneill }
1324f7089895Sjmcneill sc->rst_ephy = fdtbus_reset_get(phandle, "ephy");
13257b8150dfSjmcneill if (sc->rst_ephy == NULL) {
1326b97c5938Sjmcneill int phy_phandle = fdtbus_get_phandle(phandle, "phy-handle");
13277b8150dfSjmcneill if (phy_phandle != -1)
13287b8150dfSjmcneill sc->rst_ephy = fdtbus_reset_get_index(phy_phandle, 0);
13297b8150dfSjmcneill }
13309432a99aSjmcneill
13319432a99aSjmcneill /* Regulator is optional */
13329432a99aSjmcneill sc->reg_phy = fdtbus_regulator_acquire(phandle, "phy-supply");
13339432a99aSjmcneill
13349432a99aSjmcneill /* Reset GPIO is optional */
13359432a99aSjmcneill sc->pin_reset = fdtbus_gpio_acquire(sc->phandle,
13369432a99aSjmcneill "allwinner,reset-gpio", GPIO_PIN_OUTPUT);
13379432a99aSjmcneill
13389432a99aSjmcneill return 0;
13399432a99aSjmcneill }
13409432a99aSjmcneill
13419432a99aSjmcneill static int
sunxi_emac_get_phyid(struct sunxi_emac_softc * sc)134297456612Sjmcneill sunxi_emac_get_phyid(struct sunxi_emac_softc *sc)
134397456612Sjmcneill {
134497456612Sjmcneill bus_addr_t addr;
13457b8150dfSjmcneill int phy_phandle;
134697456612Sjmcneill
13477b8150dfSjmcneill phy_phandle = fdtbus_get_phandle(sc->phandle, "phy");
13487b8150dfSjmcneill if (phy_phandle == -1)
13497b8150dfSjmcneill phy_phandle = fdtbus_get_phandle(sc->phandle, "phy-handle");
135097456612Sjmcneill if (phy_phandle == -1)
135197456612Sjmcneill return MII_PHY_ANY;
135297456612Sjmcneill
135397456612Sjmcneill if (fdtbus_get_reg(phy_phandle, 0, &addr, NULL) != 0)
135497456612Sjmcneill return MII_PHY_ANY;
135597456612Sjmcneill
135697456612Sjmcneill return (int)addr;
135797456612Sjmcneill }
135897456612Sjmcneill
135997456612Sjmcneill static int
sunxi_emac_match(device_t parent,cfdata_t cf,void * aux)13609432a99aSjmcneill sunxi_emac_match(device_t parent, cfdata_t cf, void *aux)
13619432a99aSjmcneill {
13629432a99aSjmcneill struct fdt_attach_args * const faa = aux;
13639432a99aSjmcneill
13646e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
13659432a99aSjmcneill }
13669432a99aSjmcneill
13679432a99aSjmcneill static void
sunxi_emac_attach(device_t parent,device_t self,void * aux)13689432a99aSjmcneill sunxi_emac_attach(device_t parent, device_t self, void *aux)
13699432a99aSjmcneill {
13709432a99aSjmcneill struct fdt_attach_args * const faa = aux;
13719432a99aSjmcneill struct sunxi_emac_softc * const sc = device_private(self);
13729432a99aSjmcneill const int phandle = faa->faa_phandle;
13739432a99aSjmcneill struct mii_data *mii = &sc->mii;
13749432a99aSjmcneill struct ifnet *ifp = &sc->ec.ec_if;
13759432a99aSjmcneill uint8_t eaddr[ETHER_ADDR_LEN];
13769432a99aSjmcneill char intrstr[128];
13779432a99aSjmcneill
13789432a99aSjmcneill sc->dev = self;
13799432a99aSjmcneill sc->phandle = phandle;
13809432a99aSjmcneill sc->bst = faa->faa_bst;
13819432a99aSjmcneill sc->dmat = faa->faa_dmat;
13826e54367aSthorpej sc->type = of_compatible_lookup(phandle, compat_data)->value;
138397456612Sjmcneill sc->phy_id = sunxi_emac_get_phyid(sc);
13849432a99aSjmcneill
1385664653b0Sthorpej aprint_naive("\n");
1386664653b0Sthorpej aprint_normal(": EMAC\n");
1387664653b0Sthorpej
13889432a99aSjmcneill if (sunxi_emac_get_resources(sc) != 0) {
1389664653b0Sthorpej aprint_error_dev(self,
1390664653b0Sthorpej "cannot allocate resources for device\n");
13919432a99aSjmcneill return;
13929432a99aSjmcneill }
13939432a99aSjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1394664653b0Sthorpej aprint_error_dev(self, "cannot decode interrupt\n");
13959432a99aSjmcneill return;
13969432a99aSjmcneill }
13979432a99aSjmcneill
13989432a99aSjmcneill mutex_init(&sc->mtx, MUTEX_DEFAULT, IPL_NET);
13999432a99aSjmcneill callout_init(&sc->stat_ch, CALLOUT_FLAGS);
14009432a99aSjmcneill callout_setfunc(&sc->stat_ch, sunxi_emac_tick, sc);
14019432a99aSjmcneill
14029432a99aSjmcneill /* Setup clocks and regulators */
14039432a99aSjmcneill if (sunxi_emac_setup_resources(sc) != 0)
14049432a99aSjmcneill return;
14059432a99aSjmcneill
14069432a99aSjmcneill /* Read MAC address before resetting the chip */
14079432a99aSjmcneill sunxi_emac_get_eaddr(sc, eaddr);
1408e3d5c5f5Sjmcneill aprint_normal_dev(self, "Ethernet address %s\n", ether_sprintf(eaddr));
14099432a99aSjmcneill
1410ef41c138Sjmcneill /* Reset PHY if necessary */
1411ef41c138Sjmcneill if (sunxi_emac_phy_reset(sc) != 0) {
1412ef41c138Sjmcneill aprint_error_dev(self, "failed to reset PHY\n");
14139432a99aSjmcneill return;
1414ef41c138Sjmcneill }
14159432a99aSjmcneill
14169432a99aSjmcneill /* Setup DMA descriptors */
14179432a99aSjmcneill if (sunxi_emac_setup_dma(sc) != 0) {
14189432a99aSjmcneill aprint_error_dev(self, "failed to setup DMA descriptors\n");
14199432a99aSjmcneill return;
14209432a99aSjmcneill }
14219432a99aSjmcneill
14229432a99aSjmcneill /* Install interrupt handler */
1423076a1169Sjmcneill sc->ih = fdtbus_intr_establish_xname(phandle, 0, IPL_NET,
1424076a1169Sjmcneill FDT_INTR_FLAGS, sunxi_emac_intr, sc, device_xname(self));
14259432a99aSjmcneill if (sc->ih == NULL) {
14269432a99aSjmcneill aprint_error_dev(self, "failed to establish interrupt on %s\n",
14279432a99aSjmcneill intrstr);
14289432a99aSjmcneill return;
14299432a99aSjmcneill }
14309432a99aSjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
14319432a99aSjmcneill
14329432a99aSjmcneill /* Setup ethernet interface */
14339432a99aSjmcneill ifp->if_softc = sc;
14349432a99aSjmcneill snprintf(ifp->if_xname, IFNAMSIZ, EMAC_IFNAME, device_unit(self));
14359432a99aSjmcneill ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
14369432a99aSjmcneill #ifdef EMAC_MPSAFE
1437ab3cd725Sozaki-r ifp->if_extflags = IFEF_MPSAFE;
14389432a99aSjmcneill #endif
14399432a99aSjmcneill ifp->if_start = sunxi_emac_start;
14409432a99aSjmcneill ifp->if_ioctl = sunxi_emac_ioctl;
14419432a99aSjmcneill ifp->if_init = sunxi_emac_init;
14429432a99aSjmcneill ifp->if_stop = sunxi_emac_stop;
14439432a99aSjmcneill ifp->if_capabilities = IFCAP_CSUM_IPv4_Rx |
14449432a99aSjmcneill IFCAP_CSUM_IPv4_Tx |
14459432a99aSjmcneill IFCAP_CSUM_TCPv4_Rx |
14469432a99aSjmcneill IFCAP_CSUM_TCPv4_Tx |
14479432a99aSjmcneill IFCAP_CSUM_UDPv4_Rx |
14489432a99aSjmcneill IFCAP_CSUM_UDPv4_Tx;
1449ceda354dSjmcneill ifp->if_capenable = ifp->if_capabilities;
14509432a99aSjmcneill IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
14519432a99aSjmcneill IFQ_SET_READY(&ifp->if_snd);
14529432a99aSjmcneill
14539432a99aSjmcneill /* 802.1Q VLAN-sized frames are supported */
14549432a99aSjmcneill sc->ec.ec_capabilities |= ETHERCAP_VLAN_MTU;
14559432a99aSjmcneill
14569432a99aSjmcneill /* Attach MII driver */
14579432a99aSjmcneill sc->ec.ec_mii = mii;
14589432a99aSjmcneill ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
14599432a99aSjmcneill mii->mii_ifp = ifp;
14609432a99aSjmcneill mii->mii_readreg = sunxi_emac_mii_readreg;
14619432a99aSjmcneill mii->mii_writereg = sunxi_emac_mii_writereg;
14629432a99aSjmcneill mii->mii_statchg = sunxi_emac_mii_statchg;
146397456612Sjmcneill mii_attach(self, mii, 0xffffffff, sc->phy_id, MII_OFFSET_ANY,
14649432a99aSjmcneill MIIF_DOPAUSE);
14659432a99aSjmcneill
14669432a99aSjmcneill if (LIST_EMPTY(&mii->mii_phys)) {
14679432a99aSjmcneill aprint_error_dev(self, "no PHY found!\n");
14689432a99aSjmcneill return;
14699432a99aSjmcneill }
14709432a99aSjmcneill ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
14719432a99aSjmcneill
14729432a99aSjmcneill /* Attach interface */
14739432a99aSjmcneill if_attach(ifp);
14749432a99aSjmcneill if_deferred_start_init(ifp, NULL);
14759432a99aSjmcneill
14769432a99aSjmcneill /* Attach ethernet interface */
14779432a99aSjmcneill ether_ifattach(ifp, eaddr);
14789432a99aSjmcneill }
14799432a99aSjmcneill
14809432a99aSjmcneill CFATTACH_DECL_NEW(sunxi_emac, sizeof(struct sunxi_emac_softc),
14819432a99aSjmcneill sunxi_emac_match, sunxi_emac_attach, NULL, NULL);
1482