Author: karels Date: Wed Apr 22 00:42:10 2020 New Revision: 360181 URL: https://svnweb.freebsd.org/changeset/base/360181
Log: Add genet driver for Raspberry Pi 4B Ethernet Add driver for Broadcom "GENET" version 5, as found in BCM-2711 on Raspberry Pi 4B. The driver is derived in part from the bcmgenet.c driver in NetBSD, along with bcmgenetreg.h. Reviewed by: manu Obtained from: in part from NetBSD Relnotes: yes, note addition Differential Revision: https://reviews.freebsd.org/D24436 Added: head/sys/arm64/broadcom/genet/ head/sys/arm64/broadcom/genet/if_genet.c (contents, props changed) head/sys/arm64/broadcom/genet/if_genetreg.h (contents, props changed) Modified: head/sys/arm64/conf/GENERIC head/sys/arm64/include/bus.h head/sys/conf/files.arm64 Added: head/sys/arm64/broadcom/genet/if_genet.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/broadcom/genet/if_genet.c Wed Apr 22 00:42:10 2020 (r360181) @@ -0,0 +1,1678 @@ +/*- + * Copyright (c) 2020 Michael J Karels + * Copyright (c) 2016, 2020 Jared McNeill <jmcne...@invisible.ca> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * RPi4 (BCM 2711) Gigabit Ethernet ("GENET") controller + * + * This driver is derived in large part from bcmgenet.c from NetBSD by + * Jared McNeill. Parts of the structure and other common code in + * this driver have been copied from if_awg.c for the Allwinner EMAC, + * also by Jared McNeill. + */ + +#include "opt_device_polling.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/module.h> +#include <sys/taskqueue.h> +#include <sys/gpio.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_var.h> + +#include <machine/bus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#define __BIT(_x) (1 << (_x)) +#include "if_genetreg.h" + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/mii_fdt.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include "syscon_if.h" +#include "miibus_if.h" +#include "gpio_if.h" + +#define RD4(sc, reg) bus_read_4((sc)->res[_RES_MAC], (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res[_RES_MAC], (reg), (val)) + +#define GEN_LOCK(sc) mtx_lock(&(sc)->mtx) +#define GEN_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define GEN_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) +#define GEN_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) + +#define TX_DESC_COUNT GENET_DMA_DESC_COUNT +#define RX_DESC_COUNT GENET_DMA_DESC_COUNT + +#define TX_NEXT(n, count) (((n) + 1) & ((count) - 1)) +#define RX_NEXT(n, count) (((n) + 1) & ((count) - 1)) + + +#define TX_MAX_SEGS 20 + +/* Maximum number of mbufs to send to if_input */ +static int gen_rx_batch = 16 /* RX_BATCH_DEFAULT */; +TUNABLE_INT("hw.gen.rx_batch", &gen_rx_batch); + +static struct ofw_compat_data compat_data[] = { + { "brcm,genet-v1", 1 }, + { "brcm,genet-v2", 2 }, + { "brcm,genet-v3", 3 }, + { "brcm,genet-v4", 4 }, + { "brcm,genet-v5", 5 }, + { NULL, 0 } +}; + +enum { + _RES_MAC, /* what to call this? */ + _RES_IRQ1, + _RES_IRQ2, + _RES_NITEMS +}; + +static struct resource_spec gen_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { -1, 0 } +}; + +/* structure per ring entry */ +struct gen_ring_ent { + bus_dmamap_t map; + struct mbuf *mbuf; +}; + +struct tx_queue { + int hwindex; /* hardware index */ + int nentries; + u_int queued; /* or avail? */ + u_int cur; + u_int next; + u_int prod_idx; + u_int cons_idx; + struct gen_ring_ent *entries; +}; + +struct rx_queue { + int hwindex; /* hardware index */ + int nentries; + u_int cur; + u_int prod_idx; + u_int cons_idx; + struct gen_ring_ent *entries; +}; + +struct gen_softc { + struct resource *res[_RES_NITEMS]; + struct mtx mtx; + if_t ifp; + device_t dev; + device_t miibus; + mii_contype_t phy_mode; + + struct callout stat_ch; + struct task link_task; + void *ih; + void *ih2; + int type; + int if_flags; + int link; + bus_dma_tag_t tx_buf_tag; + /* + * The genet chip has multiple queues for transmit and receive. + * This driver uses only one (queue 16, the default), but is cast + * with multiple rings. The additional rings are used for different + * priorities. + */ +#define DEF_TXQUEUE 0 +#define NTXQUEUE 1 + struct tx_queue tx_queue[NTXQUEUE]; + struct gen_ring_ent tx_ring_ent[TX_DESC_COUNT]; /* ring entries */ + + bus_dma_tag_t rx_buf_tag; +#define DEF_RXQUEUE 0 +#define NRXQUEUE 1 + struct rx_queue rx_queue[NRXQUEUE]; + struct gen_ring_ent rx_ring_ent[RX_DESC_COUNT]; /* ring entries */ +}; + +static void gen_init(void *softc); +static void gen_start(if_t ifp); +static void gen_destroy(struct gen_softc *sc); +static int gen_encap(struct gen_softc *sc, struct mbuf **mp); +static int gen_parse_tx(struct mbuf *m, int csum_flags); +static int gen_ioctl(if_t ifp, u_long cmd, caddr_t data); +static int gen_get_phy_mode(device_t dev); +static bool gen_get_eaddr(device_t dev, struct ether_addr *eaddr); +static void gen_set_enaddr(struct gen_softc *sc); +static void gen_setup_rxfilter(struct gen_softc *sc); +static void gen_reset(struct gen_softc *sc); +static void gen_enable(struct gen_softc *sc); +static void gen_dma_disable(device_t dev); +static int gen_bus_dma_init(struct gen_softc *sc); +static void gen_bus_dma_teardown(struct gen_softc *sc); +static void gen_enable_intr(struct gen_softc *sc); +static void gen_init_txrings(struct gen_softc *sc); +static void gen_init_rxrings(struct gen_softc *sc); +static void gen_intr(void *softc); +static int gen_rxintr(struct gen_softc *sc, struct rx_queue *q); +static void gen_txintr(struct gen_softc *sc, struct tx_queue *q); +static void gen_intr2(void *softc); +static int gen_newbuf_rx(struct gen_softc *sc, struct rx_queue *q, int index); +static int gen_mapbuf_rx(struct gen_softc *sc, struct rx_queue *q, int index, + struct mbuf *m); +static void gen_link_task(void *arg, int pending); +static void gen_media_status(if_t ifp, struct ifmediareq *ifmr); +static int gen_media_change(if_t ifp); +static void gen_tick(void *softc); + +static int +gen_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "RPi4 Gigabit Ethernet"); + return (BUS_PROBE_DEFAULT); +} + +static int +gen_attach(device_t dev) +{ + struct ether_addr eaddr; + struct gen_softc *sc; + int major, minor, error; + bool eaddr_found; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + if (bus_alloc_resources(dev, gen_spec, sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + error = ENXIO; + goto fail; + } + + major = (RD4(sc, GENET_SYS_REV_CTRL) & REV_MAJOR) >> REV_MAJOR_SHIFT; + if (major != REV_MAJOR_V5) { + device_printf(dev, "version %d is not supported\n", major); + error = ENXIO; + goto fail; + } + minor = (RD4(sc, GENET_SYS_REV_CTRL) & REV_MINOR) >> REV_MINOR_SHIFT; + device_printf(dev, "GENET version 5.%d phy 0x%04x\n", minor, + RD4(sc, GENET_SYS_REV_CTRL) & REV_PHY); + + mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); + callout_init_mtx(&sc->stat_ch, &sc->mtx, 0); + TASK_INIT(&sc->link_task, 0, gen_link_task, sc); + + error = gen_get_phy_mode(dev); + if (error != 0) + goto fail; + + bzero(&eaddr, sizeof(eaddr)); + eaddr_found = gen_get_eaddr(dev, &eaddr); + + /* reset core */ + gen_reset(sc); + + gen_dma_disable(dev); + + /* Setup DMA */ + error = gen_bus_dma_init(sc); + if (error != 0) { + device_printf(dev, "cannot setup bus dma\n"); + goto fail; + } + + /* Install interrupt handlers */ + error = bus_setup_intr(dev, sc->res[_RES_IRQ1], + INTR_TYPE_NET | INTR_MPSAFE, NULL, gen_intr, sc, &sc->ih); + if (error != 0) { + device_printf(dev, "cannot setup interrupt handler1\n"); + goto fail; + } + + error = bus_setup_intr(dev, sc->res[_RES_IRQ2], + INTR_TYPE_NET | INTR_MPSAFE, NULL, gen_intr2, sc, &sc->ih2); + if (error != 0) { + device_printf(dev, "cannot setup interrupt handler2\n"); + goto fail; + } + + /* Setup ethernet interface */ + sc->ifp = if_alloc(IFT_ETHER); + if_setsoftc(sc->ifp, sc); + if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev)); + if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); + if_setstartfn(sc->ifp, gen_start); + if_setioctlfn(sc->ifp, gen_ioctl); + if_setinitfn(sc->ifp, gen_init); + if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1); + if_setsendqready(sc->ifp); +#define GEN_CSUM_FEATURES (CSUM_UDP | CSUM_TCP) + if_sethwassist(sc->ifp, GEN_CSUM_FEATURES); + if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM | + IFCAP_HWCSUM_IPV6); + if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); + + /* Attach MII driver */ + error = mii_attach(dev, &sc->miibus, sc->ifp, gen_media_change, + gen_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, + MIIF_DOPAUSE); + if (error != 0) { + device_printf(dev, "cannot attach PHY\n"); + goto fail; + } + + /* If address was not found, create one based on the hostid and name. */ + if (eaddr_found == 0) + ether_gen_addr(sc->ifp, &eaddr); + /* Attach ethernet interface */ + ether_ifattach(sc->ifp, eaddr.octet); + +fail: + if (error) + gen_destroy(sc); + return (error); +} + +/* Free resources after failed attach. This is not a complete detach. */ +static void +gen_destroy(struct gen_softc *sc) +{ + + if (sc->miibus) { /* can't happen */ + device_delete_child(sc->dev, sc->miibus); + sc->miibus = NULL; + } + bus_teardown_intr(sc->dev, sc->res[_RES_IRQ1], sc->ih); + bus_teardown_intr(sc->dev, sc->res[_RES_IRQ2], sc->ih2); + gen_bus_dma_teardown(sc); + callout_drain(&sc->stat_ch); + if (mtx_initialized(&sc->mtx)) + mtx_destroy(&sc->mtx); + bus_release_resources(sc->dev, gen_spec, sc->res); + if (sc->ifp != NULL) { + if_free(sc->ifp); + sc->ifp = NULL; + } +} + +static int +gen_get_phy_mode(device_t dev) +{ + struct gen_softc *sc; + phandle_t node; + mii_contype_t type; + int error = 0; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + type = mii_fdt_get_contype(node); + + switch (type) { + case MII_CONTYPE_RGMII: + case MII_CONTYPE_RGMII_RXID: + case MII_CONTYPE_RGMII_TXID: + sc->phy_mode = type; + break; + default: + device_printf(dev, "unknown phy-mode '%s'\n", + mii_fdt_contype_to_name(type)); + error = ENXIO; + break; + } + + return (error); +} + +static bool +gen_get_eaddr(device_t dev, struct ether_addr *eaddr) +{ + struct gen_softc *sc; + uint32_t maclo, machi, val; + phandle_t node; + + sc = device_get_softc(dev); + + node = ofw_bus_get_node(dev); + if (OF_getprop(node, "mac-address", eaddr->octet, + ETHER_ADDR_LEN) != -1 || + OF_getprop(node, "local-mac-address", eaddr->octet, + ETHER_ADDR_LEN) != -1 || + OF_getprop(node, "address", eaddr->octet, ETHER_ADDR_LEN) != -1) + return (true); + + device_printf(dev, "No Ethernet address found in fdt!\n"); + maclo = machi = 0; + + val = RD4(sc, GENET_SYS_RBUF_FLUSH_CTRL); + if ((val & GENET_SYS_RBUF_FLUSH_RESET) == 0) { + maclo = htobe32(RD4(sc, GENET_UMAC_MAC0)); + machi = htobe16(RD4(sc, GENET_UMAC_MAC1) & 0xffff); + } + + if (maclo == 0 && machi == 0) { + if (bootverbose) + device_printf(dev, + "No Ethernet address found in controller\n"); + return (false); + } else { + eaddr->octet[0] = maclo & 0xff; + eaddr->octet[1] = (maclo >> 8) & 0xff; + eaddr->octet[2] = (maclo >> 16) & 0xff; + eaddr->octet[3] = (maclo >> 24) & 0xff; + eaddr->octet[4] = machi & 0xff; + eaddr->octet[5] = (machi >> 8) & 0xff; + return (true); + } +} + +static void +gen_reset(struct gen_softc *sc) +{ + uint32_t val; + + val = RD4(sc, GENET_SYS_RBUF_FLUSH_CTRL); + val |= GENET_SYS_RBUF_FLUSH_RESET; + WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, val); + DELAY(10); + + val &= ~GENET_SYS_RBUF_FLUSH_RESET; + WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, val); + DELAY(10); + + WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, 0); + DELAY(10); + + WR4(sc, GENET_UMAC_CMD, 0); + WR4(sc, GENET_UMAC_CMD, + GENET_UMAC_CMD_LCL_LOOP_EN | GENET_UMAC_CMD_SW_RESET); + DELAY(10); + WR4(sc, GENET_UMAC_CMD, 0); + + WR4(sc, GENET_UMAC_MIB_CTRL, GENET_UMAC_MIB_RESET_RUNT | + GENET_UMAC_MIB_RESET_RX | GENET_UMAC_MIB_RESET_TX); + WR4(sc, GENET_UMAC_MIB_CTRL, 0); + + WR4(sc, GENET_UMAC_MAX_FRAME_LEN, 1536); + + val = RD4(sc, GENET_RBUF_CTRL); + val |= GENET_RBUF_ALIGN_2B; + WR4(sc, GENET_RBUF_CTRL, val); + + WR4(sc, GENET_RBUF_TBUF_SIZE_CTRL, 1); +} + +static void +gen_enable(struct gen_softc *sc) +{ + u_int val; + + /* Enable transmitter and receiver */ + val = RD4(sc, GENET_UMAC_CMD); + val |= GENET_UMAC_CMD_TXEN; + val |= GENET_UMAC_CMD_RXEN; + WR4(sc, GENET_UMAC_CMD, val); + + /* Enable interrupts */ + gen_enable_intr(sc); + WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK, + GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE); +} + +static void +gen_enable_offload(struct gen_softc *sc) +{ + uint32_t check_ctrl, buf_ctrl; + + check_ctrl = RD4(sc, GENET_RBUF_CHECK_CTRL); + buf_ctrl = RD4(sc, GENET_RBUF_CTRL); + if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0) { + check_ctrl |= GENET_RBUF_CHECK_CTRL_EN; + buf_ctrl |= GENET_RBUF_64B_EN; + } else { + check_ctrl &= ~GENET_RBUF_CHECK_CTRL_EN; + buf_ctrl &= ~GENET_RBUF_64B_EN; + } + WR4(sc, GENET_RBUF_CHECK_CTRL, check_ctrl); + WR4(sc, GENET_RBUF_CTRL, buf_ctrl); + + buf_ctrl = RD4(sc, GENET_TBUF_CTRL); + if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) != + 0) + buf_ctrl |= GENET_RBUF_64B_EN; + else + buf_ctrl &= ~GENET_RBUF_64B_EN; + WR4(sc, GENET_TBUF_CTRL, buf_ctrl); +} + +static void +gen_dma_disable(device_t dev) +{ + struct gen_softc *sc = device_get_softc(dev); + int val; + + val = RD4(sc, GENET_TX_DMA_CTRL); + val &= ~GENET_TX_DMA_CTRL_EN; + val &= ~GENET_TX_DMA_CTRL_RBUF_EN(GENET_DMA_DEFAULT_QUEUE); + WR4(sc, GENET_TX_DMA_CTRL, val); + + val = RD4(sc, GENET_RX_DMA_CTRL); + val &= ~GENET_RX_DMA_CTRL_EN; + val &= ~GENET_RX_DMA_CTRL_RBUF_EN(GENET_DMA_DEFAULT_QUEUE); + WR4(sc, GENET_RX_DMA_CTRL, val); +} + +static int +gen_bus_dma_init(struct gen_softc *sc) +{ + struct device *dev = sc->dev; + int i, error; + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + 4, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_40BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, TX_MAX_SEGS, /* maxsize, nsegs */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->tx_buf_tag); + if (error != 0) { + device_printf(dev, "cannot create TX buffer tag\n"); + return (error); + } + + for (i = 0; i < TX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->tx_buf_tag, 0, + &sc->tx_ring_ent[i].map); + if (error != 0) { + device_printf(dev, "cannot create TX buffer map\n"); + return (error); + } + } + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* Parent tag */ + 4, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_40BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MCLBYTES, 1, /* maxsize, nsegs */ + MCLBYTES, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->rx_buf_tag); + if (error != 0) { + device_printf(dev, "cannot create RX buffer tag\n"); + return (error); + } + + for (i = 0; i < RX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->rx_buf_tag, 0, + &sc->rx_ring_ent[i].map); + if (error != 0) { + device_printf(dev, "cannot create RX buffer map\n"); + return (error); + } + } + return (0); +} + +static void +gen_bus_dma_teardown(struct gen_softc *sc) +{ + int i, error; + + if (sc->tx_buf_tag != NULL) { + for (i = 0; i < TX_DESC_COUNT; i++) { + error = bus_dmamap_destroy(sc->tx_buf_tag, + sc->tx_ring_ent[i].map); + sc->tx_ring_ent[i].map = NULL; + if (error) + device_printf(sc->dev, + "%s: bus_dmamap_destroy failed: %d\n", + __func__, error); + } + error = bus_dma_tag_destroy(sc->tx_buf_tag); + sc->tx_buf_tag = NULL; + if (error) + device_printf(sc->dev, + "%s: bus_dma_tag_destroy failed: %d\n", __func__, + error); + } + + if (sc->tx_buf_tag != NULL) { + for (i = 0; i < RX_DESC_COUNT; i++) { + error = bus_dmamap_destroy(sc->rx_buf_tag, + sc->rx_ring_ent[i].map); + sc->rx_ring_ent[i].map = NULL; + if (error) + device_printf(sc->dev, + "%s: bus_dmamap_destroy failed: %d\n", + __func__, error); + } + error = bus_dma_tag_destroy(sc->rx_buf_tag); + sc->rx_buf_tag = NULL; + if (error) + device_printf(sc->dev, + "%s: bus_dma_tag_destroy failed: %d\n", __func__, + error); + } +} + +static void +gen_enable_intr(struct gen_softc *sc) +{ + + WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK, + GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE); +} + +/* + * "queue" is the software queue index (0-4); "qid" is the hardware index + * (0-16). "base" is the starting index in the ring array. + */ +static void +gen_init_txring(struct gen_softc *sc, int queue, int qid, int base, + int nentries) +{ + struct tx_queue *q; + uint32_t val; + + q = &sc->tx_queue[queue]; + q->entries = &sc->tx_ring_ent[base]; + q->hwindex = qid; + q->nentries = nentries; + + /* TX ring */ + + q->queued = 0; + q->cons_idx = q->prod_idx = 0; + + WR4(sc, GENET_TX_SCB_BURST_SIZE, 0x08); + + WR4(sc, GENET_TX_DMA_READ_PTR_LO(qid), 0); + WR4(sc, GENET_TX_DMA_READ_PTR_HI(qid), 0); + WR4(sc, GENET_TX_DMA_CONS_INDEX(qid), 0); + WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), 0); + WR4(sc, GENET_TX_DMA_RING_BUF_SIZE(qid), + (nentries << GENET_TX_DMA_RING_BUF_SIZE_DESC_SHIFT) | + (MCLBYTES & GENET_TX_DMA_RING_BUF_SIZE_BUF_LEN_MASK)); + WR4(sc, GENET_TX_DMA_START_ADDR_LO(qid), 0); + WR4(sc, GENET_TX_DMA_START_ADDR_HI(qid), 0); + WR4(sc, GENET_TX_DMA_END_ADDR_LO(qid), + TX_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1); + WR4(sc, GENET_TX_DMA_END_ADDR_HI(qid), 0); + WR4(sc, GENET_TX_DMA_MBUF_DONE_THRES(qid), 1); + WR4(sc, GENET_TX_DMA_FLOW_PERIOD(qid), 0); + WR4(sc, GENET_TX_DMA_WRITE_PTR_LO(qid), 0); + WR4(sc, GENET_TX_DMA_WRITE_PTR_HI(qid), 0); + + WR4(sc, GENET_TX_DMA_RING_CFG, __BIT(qid)); /* enable */ + + /* Enable transmit DMA */ + val = RD4(sc, GENET_TX_DMA_CTRL); + val |= GENET_TX_DMA_CTRL_EN; + val |= GENET_TX_DMA_CTRL_RBUF_EN(qid); + WR4(sc, GENET_TX_DMA_CTRL, val); +} + +/* + * "queue" is the software queue index (0-4); "qid" is the hardware index + * (0-16). "base" is the starting index in the ring array. + */ +static void +gen_init_rxring(struct gen_softc *sc, int queue, int qid, int base, + int nentries) +{ + struct rx_queue *q; + uint32_t val; + int i; + + q = &sc->rx_queue[queue]; + q->entries = &sc->rx_ring_ent[base]; + q->hwindex = qid; + q->nentries = nentries; + q->cons_idx = q->prod_idx = 0; + + WR4(sc, GENET_RX_SCB_BURST_SIZE, 0x08); + + WR4(sc, GENET_RX_DMA_WRITE_PTR_LO(qid), 0); + WR4(sc, GENET_RX_DMA_WRITE_PTR_HI(qid), 0); + WR4(sc, GENET_RX_DMA_PROD_INDEX(qid), 0); + WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), 0); + WR4(sc, GENET_RX_DMA_RING_BUF_SIZE(qid), + (nentries << GENET_RX_DMA_RING_BUF_SIZE_DESC_SHIFT) | + (MCLBYTES & GENET_RX_DMA_RING_BUF_SIZE_BUF_LEN_MASK)); + WR4(sc, GENET_RX_DMA_START_ADDR_LO(qid), 0); + WR4(sc, GENET_RX_DMA_START_ADDR_HI(qid), 0); + WR4(sc, GENET_RX_DMA_END_ADDR_LO(qid), + RX_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1); + WR4(sc, GENET_RX_DMA_END_ADDR_HI(qid), 0); + WR4(sc, GENET_RX_DMA_XON_XOFF_THRES(qid), + (5 << GENET_RX_DMA_XON_XOFF_THRES_LO_SHIFT) | (RX_DESC_COUNT >> 4)); + WR4(sc, GENET_RX_DMA_READ_PTR_LO(qid), 0); + WR4(sc, GENET_RX_DMA_READ_PTR_HI(qid), 0); + + WR4(sc, GENET_RX_DMA_RING_CFG, __BIT(qid)); /* enable */ + + /* fill ring */ + for (i = 0; i < RX_DESC_COUNT; i++) + gen_newbuf_rx(sc, &sc->rx_queue[DEF_RXQUEUE], i); + + /* Enable receive DMA */ + val = RD4(sc, GENET_RX_DMA_CTRL); + val |= GENET_RX_DMA_CTRL_EN; + val |= GENET_RX_DMA_CTRL_RBUF_EN(qid); + WR4(sc, GENET_RX_DMA_CTRL, val); +} + +static void +gen_init_txrings(struct gen_softc *sc) +{ + int base = 0; +#ifdef PRI_RINGS + int i; + + /* init priority rings */ + for (i = 0; i < PRI_RINGS; i++) { + gen_init_txring(sc, i, i, base, TX_DESC_PRICOUNT); + sc->tx_queue[i].queue = i; + base += TX_DESC_PRICOUNT; + dma_ring_conf |= 1 << i; + dma_control |= DMA_RENABLE(i); + } +#endif + + /* init GENET_DMA_DEFAULT_QUEUE (16) */ + gen_init_txring(sc, DEF_TXQUEUE, GENET_DMA_DEFAULT_QUEUE, base, + TX_DESC_COUNT); + sc->tx_queue[DEF_TXQUEUE].hwindex = GENET_DMA_DEFAULT_QUEUE; +} + +static void +gen_init_rxrings(struct gen_softc *sc) +{ + int base = 0; +#ifdef PRI_RINGS + int i; + + /* init priority rings */ + for (i = 0; i < PRI_RINGS; i++) { + gen_init_rxring(sc, i, i, base, TX_DESC_PRICOUNT); + sc->rx_queue[i].queue = i; + base += TX_DESC_PRICOUNT; + dma_ring_conf |= 1 << i; + dma_control |= DMA_RENABLE(i); + } +#endif + + /* init GENET_DMA_DEFAULT_QUEUE (16) */ + gen_init_rxring(sc, DEF_RXQUEUE, GENET_DMA_DEFAULT_QUEUE, base, + RX_DESC_COUNT); + sc->rx_queue[DEF_RXQUEUE].hwindex = GENET_DMA_DEFAULT_QUEUE; + +} + +static void +gen_init_locked(struct gen_softc *sc) +{ + struct mii_data *mii; + if_t ifp; + + mii = device_get_softc(sc->miibus); + ifp = sc->ifp; + + GEN_ASSERT_LOCKED(sc); + + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + return; + + if (sc->phy_mode == MII_CONTYPE_RGMII || + sc->phy_mode == MII_CONTYPE_RGMII_RXID) + WR4(sc, GENET_SYS_PORT_CTRL, + GENET_SYS_PORT_MODE_EXT_GPHY); + + gen_set_enaddr(sc); + + /* Setup RX filter */ + gen_setup_rxfilter(sc); + + gen_init_txrings(sc); + gen_init_rxrings(sc); + gen_enable(sc); + gen_enable_offload(sc); + + if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE); + + mii_mediachg(mii); + callout_reset(&sc->stat_ch, hz, gen_tick, sc); +} + +static void +gen_init(void *softc) +{ + struct gen_softc *sc; + + sc = softc; + GEN_LOCK(sc); + gen_init_locked(sc); + GEN_UNLOCK(sc); +} + +static uint8_t ether_broadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static void +gen_setup_rxfilter_mdf(struct gen_softc *sc, u_int n, const uint8_t *ea) +{ + uint32_t addr0 = (ea[0] << 8) | ea[1]; + uint32_t addr1 = (ea[2] << 24) | (ea[3] << 16) | (ea[4] << 8) | ea[5]; + + WR4(sc, GENET_UMAC_MDF_ADDR0(n), addr0); + WR4(sc, GENET_UMAC_MDF_ADDR1(n), addr1); +} + +static u_int +gen_setup_multi(void *arg, struct sockaddr_dl *sdl, u_int count) +{ + struct gen_softc *sc = arg; + + /* "count + 2" to account for unicast and broadcast */ + gen_setup_rxfilter_mdf(sc, count + 2, LLADDR(sdl)); + return (1); /* increment to count */ +} + +static void +gen_setup_rxfilter(struct gen_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + uint32_t cmd, mdf_ctrl; + u_int n; + + GEN_ASSERT_LOCKED(sc); + + cmd = RD4(sc, GENET_UMAC_CMD); + + /* + * Count the required number of hardware filters. We need one + * for each multicast address, plus one for our own address and + * the broadcast address. + */ + n = if_llmaddr_count(ifp) + 2; + + if (n > GENET_MAX_MDF_FILTER) + ifp->if_flags |= IFF_ALLMULTI; + else + ifp->if_flags &= ~IFF_ALLMULTI; + + if ((ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) { + cmd |= GENET_UMAC_CMD_PROMISC; + mdf_ctrl = 0; + } else { + cmd &= ~GENET_UMAC_CMD_PROMISC; + gen_setup_rxfilter_mdf(sc, 0, ether_broadcastaddr); + gen_setup_rxfilter_mdf(sc, 1, IF_LLADDR(ifp)); + (void) if_foreach_llmaddr(ifp, gen_setup_multi, sc); + mdf_ctrl = (__BIT(GENET_MAX_MDF_FILTER) - 1) &~ + (__BIT(GENET_MAX_MDF_FILTER - n) - 1); + } + + WR4(sc, GENET_UMAC_CMD, cmd); + WR4(sc, GENET_UMAC_MDF_CTRL, mdf_ctrl); +} + +static void +gen_set_enaddr(struct gen_softc *sc) +{ + uint8_t *enaddr; + uint32_t val; + if_t ifp; + + GEN_ASSERT_LOCKED(sc); + + ifp = sc->ifp; + + /* Write our unicast address */ + enaddr = IF_LLADDR(ifp); + /* Write hardware address */ + val = enaddr[3] | (enaddr[2] << 8) | (enaddr[1] << 16) | + (enaddr[0] << 24); + WR4(sc, GENET_UMAC_MAC0, val); + val = enaddr[5] | (enaddr[4] << 8); + WR4(sc, GENET_UMAC_MAC1, val); +} + +static void +gen_start_locked(struct gen_softc *sc) +{ + struct mbuf *m; + if_t ifp; + int cnt, err; + + GEN_ASSERT_LOCKED(sc); + + if (!sc->link) + return; + + ifp = sc->ifp; + + if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING) + return; + + for (cnt = 0; ; cnt++) { + m = if_dequeue(ifp); + if (m == NULL) + break; + + err = gen_encap(sc, &m); + if (err != 0) { + if (err == ENOBUFS) + if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); + if (m != NULL) + if_sendq_prepend(ifp, m); + break; + } + if_bpfmtap(ifp, m); + } +} + +static void +gen_start(if_t ifp) +{ + struct gen_softc *sc; + + sc = if_getsoftc(ifp); + + GEN_LOCK(sc); + gen_start_locked(sc); + GEN_UNLOCK(sc); +} + +static int +gen_encap(struct gen_softc *sc, struct mbuf **mp) +{ + bus_dmamap_t map; + bus_dma_segment_t segs[TX_MAX_SEGS]; + int error, nsegs, cur, first, i, index, offset; + uint32_t csuminfo, length_status, csum_flags = 0, csumdata; + struct mbuf *m; + struct statusblock *sb = NULL; + struct tx_queue *q; + struct gen_ring_ent *ent; + + GEN_ASSERT_LOCKED(sc); + + q = &sc->tx_queue[DEF_TXQUEUE]; + + m = *mp; + if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) != + 0) { + csum_flags = m->m_pkthdr.csum_flags; + csumdata = m->m_pkthdr.csum_data; + M_PREPEND(m, sizeof(struct statusblock), M_NOWAIT); + if (m == NULL) { + if (sc->ifp->if_flags & IFF_DEBUG) + device_printf(sc->dev, "prepend fail\n"); + *mp = NULL; + return (ENOMEM); + } + offset = gen_parse_tx(m, csum_flags); + sb = mtod(m, struct statusblock *); + if (csum_flags != 0) { + csuminfo = (offset << TXCSUM_OFF_SHIFT) | + (offset + csumdata); + if (csum_flags & (CSUM_TCP | CSUM_UDP)) + csuminfo |= TXCSUM_LEN_VALID; + if (csum_flags & CSUM_UDP) + csuminfo |= TXCSUM_UDP; + sb->txcsuminfo = csuminfo; + } else + sb->txcsuminfo = 0; + } + + *mp = m; + + cur = first = q->cur; + ent = &q->entries[cur]; + map = ent->map; *** DIFF OUTPUT TRUNCATED AT 1000 LINES *** _______________________________________________ svn-src-head@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"