diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-04-14 21:02:40 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2020-04-14 21:02:40 +0000 |
commit | 56d9f1f08cb7b0c41e64aed3e02fd7c8e5c9c10b (patch) | |
tree | 90d0ffd7a326ec4c98ad6852bf5ba6ca6b731fba /sys | |
parent | 18ab39925eabb13e61344342b790f2640fedbacf (diff) |
Add bse(4), a driver for the Broadcom GENET v5 network interface found on
the Raspberry Pi4.
ok patrick@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/acpi/files.acpi | 6 | ||||
-rw-r--r-- | sys/dev/acpi/if_bse_acpi.c | 168 | ||||
-rw-r--r-- | sys/dev/ic/bcmgenet.c | 1020 | ||||
-rw-r--r-- | sys/dev/ic/bcmgenetreg.h | 175 | ||||
-rw-r--r-- | sys/dev/ic/bcmgenetvar.h | 83 |
5 files changed, 1451 insertions, 1 deletions
diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index 1a635fd86be..496d7ef1ed1 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -1,4 +1,4 @@ -# $OpenBSD: files.acpi,v 1.53 2019/12/23 08:05:42 kettenis Exp $ +# $OpenBSD: files.acpi,v 1.54 2020/04/14 21:02:39 kettenis Exp $ # # Config file and device description for machine-independent ACPI code. # Included by ports that need it. @@ -212,3 +212,7 @@ file dev/acpi/ccp_acpi.c ccp_acpi device amdgpio attach amdgpio at acpi file dev/acpi/amdgpio.c amdgpio + +# Broadcom BC7XXX Ethernet controller +attach bse at acpi with bse_acpi +file dev/acpi/if_bse_acpi.c bse_acpi diff --git a/sys/dev/acpi/if_bse_acpi.c b/sys/dev/acpi/if_bse_acpi.c new file mode 100644 index 00000000000..5ba0b162129 --- /dev/null +++ b/sys/dev/acpi/if_bse_acpi.c @@ -0,0 +1,168 @@ +/* $OpenBSD: if_bse_acpi.c,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */ +/* + * Copyright (c) 2020 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <net/if.h> +#include <net/if_media.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> +#include <dev/acpi/amltypes.h> +#include <dev/acpi/dsdt.h> + +#include <dev/mii/miivar.h> + +#include <dev/ic/bcmgenetvar.h> + +struct bse_acpi_softc { + struct genet_softc sc; + struct acpi_softc *sc_acpi; + struct aml_node *sc_node; + + bus_addr_t sc_addr; + bus_size_t sc_size; + + int sc_irq; + int sc_irq_flags; +}; + +int bse_acpi_match(struct device *, void *, void *); +void bse_acpi_attach(struct device *, struct device *, void *); + +struct cfattach bse_acpi_ca = { + sizeof(struct bse_acpi_softc), bse_acpi_match, bse_acpi_attach +}; + +const char *bse_hids[] = { + "BCM6E4E", + NULL +}; + +int bse_acpi_parse_resources(int, union acpi_resource *, void *); + +int +bse_acpi_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct cfdata *cf = match; + + return acpi_matchhids(aaa, bse_hids, cf->cf_driver->cd_name); +} + +void +bse_acpi_attach(struct device *parent, struct device *self, void *aux) +{ + struct bse_acpi_softc *sc = (struct bse_acpi_softc *)self; + struct acpi_attach_args *aaa = aux; + struct aml_value res; + char phy_mode[16] = { 0 }; + int error; + + sc->sc_acpi = (struct acpi_softc *)parent; + sc->sc_node = aaa->aaa_node; + printf(" %s", sc->sc_node->name); + + if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) { + printf(": can't find registers\n"); + return; + } + + aml_parse_resource(&res, bse_acpi_parse_resources, sc); + printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size); + if (sc->sc_addr == 0 || sc->sc_size == 0) { + printf("\n"); + return; + } + + printf(" irq %d", sc->sc_irq); + + sc->sc.sc_bst = aaa->aaa_memt; + sc->sc.sc_dmat = aaa->aaa_dmat; + + if (bus_space_map(sc->sc.sc_bst, sc->sc_addr, sc->sc_size, 0, + &sc->sc.sc_bsh)) { + printf(": can't map registers\n"); + return; + } + + sc->sc.sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags, + IPL_NET, genet_intr, sc, sc->sc.sc_dev.dv_xname); + if (sc->sc.sc_ih == NULL) { + printf(": can't establish interrupt\n"); + goto unmap; + } + + /* + * UEFI firmware initializes the hardware MAC address + * registers. Read them here before we reset the hardware. + */ + genet_lladdr_read(&sc->sc, sc->sc.sc_lladdr); + + acpi_getprop(sc->sc_node, "phy-mode", phy_mode, sizeof(phy_mode)); + if (strcmp(phy_mode, "rgmii-id") == 0) + sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_ID; + else if (strcmp(phy_mode, "rgmii-rxid") == 0) + sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_RXID; + else if (strcmp(phy_mode, "rgmii-txid") == 0) + sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_TXID; + else + sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII; + + sc->sc.sc_phy_id = MII_PHY_ANY; + error = genet_attach(&sc->sc); + if (error) + goto disestablish; + + return; + +disestablish: +#ifdef notyet + acpi_intr_disestablish(sc->sc.sc_ih); +#endif +unmap: + bus_space_unmap(sc->sc.sc_bst, sc->sc.sc_bsh, sc->sc_size); + return; +} + +int +bse_acpi_parse_resources(int crsidx, union acpi_resource *crs, void *arg) +{ + struct bse_acpi_softc *sc = arg; + int type = AML_CRSTYPE(crs); + + switch (type) { + case LR_MEM32FIXED: + sc->sc_addr = crs->lr_m32fixed._bas; + sc->sc_size = crs->lr_m32fixed._len; + break; + case LR_EXTIRQ: + sc->sc_irq = crs->lr_extirq.irq[0]; + sc->sc_irq_flags = crs->lr_extirq.flags; + break; + } + + return 0; +} diff --git a/sys/dev/ic/bcmgenet.c b/sys/dev/ic/bcmgenet.c new file mode 100644 index 00000000000..60ca5145606 --- /dev/null +++ b/sys/dev/ic/bcmgenet.c @@ -0,0 +1,1020 @@ +/* $OpenBSD: bcmgenet.c,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */ +/* $NetBSD: bcmgenet.c,v 1.3 2020/02/27 17:30:07 jmcneill Exp $ */ + +/*- + * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> + * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> + * All rights reserved. + * + * 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. + */ + +/* + * Broadcom GENETv5 + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/timeout.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/bpf.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/mii/miivar.h> + +#include <dev/ic/bcmgenetreg.h> +#include <dev/ic/bcmgenetvar.h> + +CTASSERT(MCLBYTES == 2048); + +#ifdef GENET_DEBUG +#define DPRINTF(...) printf(##__VA_ARGS__) +#else +#define DPRINTF(...) ((void)0) +#endif + +#define TX_SKIP(n, o) (((n) + (o)) & (GENET_DMA_DESC_COUNT - 1)) +#define TX_NEXT(n) TX_SKIP(n, 1) +#define RX_NEXT(n) (((n) + 1) & (GENET_DMA_DESC_COUNT - 1)) + +#define TX_MAX_SEGS 128 +#define TX_DESC_COUNT GENET_DMA_DESC_COUNT +#define RX_DESC_COUNT GENET_DMA_DESC_COUNT +#define MII_BUSY_RETRY 1000 +#define GENET_MAX_MDF_FILTER 17 + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +struct cfdriver bse_cd = { + 0, "bse", DV_IFNET +}; + +int +genet_media_change(struct ifnet *ifp) +{ + struct genet_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) + mii_mediachg(&sc->sc_mii); + + return (0); +} + +void +genet_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct genet_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) { + mii_pollstat(&sc->sc_mii); + ifmr->ifm_active = sc->sc_mii.mii_media_active; + ifmr->ifm_status = sc->sc_mii.mii_media_status; + } +} + +int +genet_mii_readreg(struct device *dev, int phy, int reg) +{ + struct genet_softc *sc = (struct genet_softc *)dev; + int retry; + + WR4(sc, GENET_MDIO_CMD, + GENET_MDIO_READ | GENET_MDIO_START_BUSY | + __SHIFTIN(phy, GENET_MDIO_PMD) | + __SHIFTIN(reg, GENET_MDIO_REG)); + for (retry = MII_BUSY_RETRY; retry > 0; retry--) { + if ((RD4(sc, GENET_MDIO_CMD) & GENET_MDIO_START_BUSY) == 0) + return RD4(sc, GENET_MDIO_CMD) & 0xffff; + delay(10); + } + + printf("%s: phy read timeout, phy=%d reg=%d\n", + sc->sc_dev.dv_xname, phy, reg); + return 0; +} + +void +genet_mii_writereg(struct device *dev, int phy, int reg, int val) +{ + struct genet_softc *sc = (struct genet_softc *)dev; + int retry; + + WR4(sc, GENET_MDIO_CMD, + val | GENET_MDIO_WRITE | GENET_MDIO_START_BUSY | + __SHIFTIN(phy, GENET_MDIO_PMD) | + __SHIFTIN(reg, GENET_MDIO_REG)); + for (retry = MII_BUSY_RETRY; retry > 0; retry--) { + if ((RD4(sc, GENET_MDIO_CMD) & GENET_MDIO_START_BUSY) == 0) + return; + delay(10); + } + + printf("%s: phy write timeout, phy=%d reg=%d\n", + sc->sc_dev.dv_xname, phy, reg); +} + +void +genet_update_link(struct genet_softc *sc) +{ + struct mii_data *mii = &sc->sc_mii; + uint32_t val; + u_int speed; + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T || + IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) + speed = GENET_UMAC_CMD_SPEED_1000; + else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) + speed = GENET_UMAC_CMD_SPEED_100; + else + speed = GENET_UMAC_CMD_SPEED_10; + + val = RD4(sc, GENET_EXT_RGMII_OOB_CTRL); + val &= ~GENET_EXT_RGMII_OOB_OOB_DISABLE; + val |= GENET_EXT_RGMII_OOB_RGMII_LINK; + val |= GENET_EXT_RGMII_OOB_RGMII_MODE_EN; + if (sc->sc_phy_mode == GENET_PHY_MODE_RGMII) + val |= GENET_EXT_RGMII_OOB_ID_MODE_DISABLE; + else + val &= ~GENET_EXT_RGMII_OOB_ID_MODE_DISABLE; + WR4(sc, GENET_EXT_RGMII_OOB_CTRL, val); + + val = RD4(sc, GENET_UMAC_CMD); + val &= ~GENET_UMAC_CMD_SPEED; + val |= __SHIFTIN(speed, GENET_UMAC_CMD_SPEED); + WR4(sc, GENET_UMAC_CMD, val); +} + +void +genet_mii_statchg(struct device *self) +{ + struct genet_softc *sc = (struct genet_softc *)self; + + genet_update_link(sc); +} + +void +genet_setup_txdesc(struct genet_softc *sc, int index, int flags, + bus_addr_t paddr, u_int len) +{ + uint32_t status; + + status = flags | __SHIFTIN(len, GENET_TX_DESC_STATUS_BUFLEN); + ++sc->sc_tx.queued; + + WR4(sc, GENET_TX_DESC_ADDRESS_LO(index), (uint32_t)paddr); + WR4(sc, GENET_TX_DESC_ADDRESS_HI(index), (uint32_t)(paddr >> 32)); + WR4(sc, GENET_TX_DESC_STATUS(index), status); +} + +int +genet_setup_txbuf(struct genet_softc *sc, int index, struct mbuf *m) +{ + bus_dma_segment_t *segs; + int error, nsegs, cur, i; + uint32_t flags; + + /* + * XXX Hardware doesn't seem to like small fragments. For now + * just look at the first fragment and defrag if it is smaller + * than the minimum Ethernet packet size. + */ + if (m->m_len < ETHER_MIN_LEN - ETHER_CRC_LEN) { + if (m_defrag(m, M_DONTWAIT)) + return 0; + } + + error = bus_dmamap_load_mbuf(sc->sc_tx.buf_tag, + sc->sc_tx.buf_map[index].map, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); + if (error == EFBIG) { + if (m_defrag(m, M_DONTWAIT)) + return 0; + error = bus_dmamap_load_mbuf(sc->sc_tx.buf_tag, + sc->sc_tx.buf_map[index].map, m, + BUS_DMA_WRITE | BUS_DMA_NOWAIT); + } + if (error != 0) + return 0; + + segs = sc->sc_tx.buf_map[index].map->dm_segs; + nsegs = sc->sc_tx.buf_map[index].map->dm_nsegs; + + if (sc->sc_tx.queued >= GENET_DMA_DESC_COUNT - nsegs) { + bus_dmamap_unload(sc->sc_tx.buf_tag, + sc->sc_tx.buf_map[index].map); + return -1; + } + + flags = GENET_TX_DESC_STATUS_SOP | + GENET_TX_DESC_STATUS_CRC | + GENET_TX_DESC_STATUS_QTAG; + + for (cur = index, i = 0; i < nsegs; i++) { + sc->sc_tx.buf_map[cur].mbuf = (i == 0 ? m : NULL); + if (i == nsegs - 1) + flags |= GENET_TX_DESC_STATUS_EOP; + + genet_setup_txdesc(sc, cur, flags, segs[i].ds_addr, + segs[i].ds_len); + + if (i == 0) { + flags &= ~GENET_TX_DESC_STATUS_SOP; + flags &= ~GENET_TX_DESC_STATUS_CRC; + } + cur = TX_NEXT(cur); + } + + bus_dmamap_sync(sc->sc_tx.buf_tag, sc->sc_tx.buf_map[index].map, + 0, sc->sc_tx.buf_map[index].map->dm_mapsize, BUS_DMASYNC_PREWRITE); + + return nsegs; +} + +void +genet_setup_rxdesc(struct genet_softc *sc, int index, + bus_addr_t paddr, bus_size_t len) +{ + WR4(sc, GENET_RX_DESC_ADDRESS_LO(index), (uint32_t)paddr); + WR4(sc, GENET_RX_DESC_ADDRESS_HI(index), (uint32_t)(paddr >> 32)); +} + +int +genet_setup_rxbuf(struct genet_softc *sc, int index, struct mbuf *m) +{ + int error; + + error = bus_dmamap_load_mbuf(sc->sc_rx.buf_tag, + sc->sc_rx.buf_map[index].map, m, BUS_DMA_READ | BUS_DMA_NOWAIT); + if (error != 0) + return error; + + bus_dmamap_sync(sc->sc_rx.buf_tag, sc->sc_rx.buf_map[index].map, + 0, sc->sc_rx.buf_map[index].map->dm_mapsize, + BUS_DMASYNC_PREREAD); + + sc->sc_rx.buf_map[index].mbuf = m; + genet_setup_rxdesc(sc, index, + sc->sc_rx.buf_map[index].map->dm_segs[0].ds_addr, + sc->sc_rx.buf_map[index].map->dm_segs[0].ds_len); + + return 0; +} + +struct mbuf * +genet_alloc_mbufcl(struct genet_softc *sc) +{ + struct mbuf *m; + + m = MCLGETI(NULL, M_DONTWAIT, NULL, MCLBYTES); + if (m != NULL) + m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; + + return m; +} + +void +genet_fill_rx_ring(struct genet_softc *sc, int qid) +{ + struct mbuf *m; + uint32_t cidx, index, total; + u_int slots; + int error; + + cidx = sc->sc_rx.cidx; + total = (sc->sc_rx.pidx - cidx) & 0xffff; + KASSERT(total <= RX_DESC_COUNT); + + index = sc->sc_rx.cidx & (RX_DESC_COUNT - 1); + for (slots = if_rxr_get(&sc->sc_rx_ring, total); + slots > 0; slots--) { + if ((m = genet_alloc_mbufcl(sc)) == NULL) { + printf("%s: cannot allocate RX mbuf\n", + sc->sc_dev.dv_xname); + break; + } + error = genet_setup_rxbuf(sc, index, m); + if (error != 0) { + printf("%s: cannot create RX buffer\n", + sc->sc_dev.dv_xname); + m_freem(m); + break; + } + + cidx = (cidx + 1) & 0xffff; + index = RX_NEXT(index); + } + if_rxr_put(&sc->sc_rx_ring, slots); + + if (sc->sc_rx.cidx != cidx) { + sc->sc_rx.cidx = cidx; + WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), sc->sc_rx.cidx); + } + + if (if_rxr_inuse(&sc->sc_rx_ring) == 0) + timeout_add(&sc->sc_rxto, 1); +} + +void +genet_rxtick(void *arg) +{ + genet_fill_rx_ring(arg, GENET_DMA_DEFAULT_QUEUE); +} + +void +genet_enable_intr(struct genet_softc *sc) +{ + WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK, + GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE); +} + +void +genet_disable_intr(struct genet_softc *sc) +{ + /* Disable interrupts */ + WR4(sc, GENET_INTRL2_CPU_SET_MASK, 0xffffffff); + WR4(sc, GENET_INTRL2_CPU_CLEAR, 0xffffffff); +} + +void +genet_tick(void *softc) +{ + struct genet_softc *sc = softc; + struct mii_data *mii = &sc->sc_mii; + int s = splnet(); + + mii_tick(mii); + timeout_add_sec(&sc->sc_stat_ch, 1); + + splx(s); +} + +void +genet_setup_rxfilter_mdf(struct genet_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); +} + +void +genet_setup_rxfilter(struct genet_softc *sc) +{ + struct arpcom *ac = &sc->sc_ac; + struct ifnet *ifp = &ac->ac_if; + struct ether_multistep step; + struct ether_multi *enm; + uint32_t cmd, mdf_ctrl; + u_int n; + + 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. + */ + ETHER_FIRST_MULTI(step, ac, enm); + for (n = 2; enm != NULL; n++) + ETHER_NEXT_MULTI(step, enm); + + if (n > GENET_MAX_MDF_FILTER || ac->ac_multirangecnt > 0) + 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; + genet_setup_rxfilter_mdf(sc, 0, etherbroadcastaddr); + genet_setup_rxfilter_mdf(sc, 1, LLADDR(ifp->if_sadl)); + ETHER_FIRST_MULTI(step, ac, enm); + for (n = 2; enm != NULL; n++) { + genet_setup_rxfilter_mdf(sc, n, enm->enm_addrlo); + ETHER_NEXT_MULTI(step, enm); + } + mdf_ctrl = __BITS(GENET_MAX_MDF_FILTER - 1, + GENET_MAX_MDF_FILTER - n); + } + + WR4(sc, GENET_UMAC_CMD, cmd); + WR4(sc, GENET_UMAC_MDF_CTRL, mdf_ctrl); +} + +int +genet_reset(struct genet_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); + + return 0; +} + +void +genet_init_rings(struct genet_softc *sc, int qid) +{ + uint32_t val; + + /* TX ring */ + + sc->sc_tx.next = 0; + sc->sc_tx.queued = 0; + sc->sc_tx.cidx = sc->sc_tx.pidx = 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), sc->sc_tx.cidx); + WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), sc->sc_tx.pidx); + WR4(sc, GENET_TX_DMA_RING_BUF_SIZE(qid), + __SHIFTIN(TX_DESC_COUNT, GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT) | + __SHIFTIN(MCLBYTES, GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH)); + 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(GENET_DMA_DEFAULT_QUEUE); + WR4(sc, GENET_TX_DMA_CTRL, val); + + /* RX ring */ + + sc->sc_rx.next = 0; + sc->sc_rx.cidx = 0; + sc->sc_rx.pidx = RX_DESC_COUNT; + + 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), sc->sc_rx.pidx); + WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), sc->sc_rx.cidx); + WR4(sc, GENET_RX_DMA_RING_BUF_SIZE(qid), + __SHIFTIN(RX_DESC_COUNT, GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT) | + __SHIFTIN(MCLBYTES, GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH)); + 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), + __SHIFTIN(5, GENET_RX_DMA_XON_XOFF_THRES_LO) | + __SHIFTIN(RX_DESC_COUNT >> 4, GENET_RX_DMA_XON_XOFF_THRES_HI)); + 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 */ + + if_rxr_init(&sc->sc_rx_ring, 2, RX_DESC_COUNT); + genet_fill_rx_ring(sc, qid); + + /* Enable receive DMA */ + 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); +} + +int +genet_init(struct genet_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mii_data *mii = &sc->sc_mii; + uint32_t val; + uint8_t *enaddr = LLADDR(ifp->if_sadl); + + if (ifp->if_flags & IFF_RUNNING) + return 0; + + if (sc->sc_phy_mode == GENET_PHY_MODE_RGMII || + sc->sc_phy_mode == GENET_PHY_MODE_RGMII_ID || + sc->sc_phy_mode == GENET_PHY_MODE_RGMII_RXID || + sc->sc_phy_mode == GENET_PHY_MODE_RGMII_TXID) + WR4(sc, GENET_SYS_PORT_CTRL, + GENET_SYS_PORT_MODE_EXT_GPHY); + + /* 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); + + /* Setup RX filter */ + genet_setup_rxfilter(sc); + + /* Setup TX/RX rings */ + genet_init_rings(sc, GENET_DMA_DEFAULT_QUEUE); + + /* 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 */ + genet_enable_intr(sc); + + ifp->if_flags |= IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + + mii_mediachg(mii); + timeout_add_sec(&sc->sc_stat_ch, 1); + + return 0; +} + +void +genet_stop(struct genet_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct genet_bufmap *bmap; + uint32_t val; + int i; + + timeout_del(&sc->sc_rxto); + timeout_del(&sc->sc_stat_ch); + + mii_down(&sc->sc_mii); + + /* Disable receiver */ + val = RD4(sc, GENET_UMAC_CMD); + val &= ~GENET_UMAC_CMD_RXEN; + WR4(sc, GENET_UMAC_CMD, val); + + /* Stop receive DMA */ + 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); + + /* Stop transmit DMA */ + 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); + + /* Flush data in the TX FIFO */ + WR4(sc, GENET_UMAC_TX_FLUSH, 1); + delay(10); + WR4(sc, GENET_UMAC_TX_FLUSH, 0); + + /* Disable transmitter */ + val = RD4(sc, GENET_UMAC_CMD); + val &= ~GENET_UMAC_CMD_TXEN; + WR4(sc, GENET_UMAC_CMD, val); + + /* Disable interrupts */ + genet_disable_intr(sc); + + ifp->if_flags &= ~IFF_RUNNING; + ifq_clr_oactive(&ifp->if_snd); + ifp->if_timer = 0; + + intr_barrier(sc->sc_ih); + + /* Clean RX ring. */ + for (i = 0; i < RX_DESC_COUNT; i++) { + bmap = &sc->sc_rx.buf_map[i]; + if (bmap->mbuf) { + bus_dmamap_sync(sc->sc_dmat, bmap->map, 0, + bmap->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, bmap->map); + m_freem(bmap->mbuf); + bmap->mbuf = NULL; + } + } + + /* Clean TX ring. */ + for (i = 0; i < TX_DESC_COUNT; i++) { + bmap = &sc->sc_tx.buf_map[i]; + if (bmap->mbuf) { + bus_dmamap_sync(sc->sc_dmat, bmap->map, 0, + bmap->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, bmap->map); + m_freem(bmap->mbuf); + bmap->mbuf = NULL; + } + } +} + +void +genet_rxintr(struct genet_softc *sc, int qid) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); + struct mbuf *m; + int index, len, n; + uint32_t status, pidx, total; + + pidx = RD4(sc, GENET_RX_DMA_PROD_INDEX(qid)) & 0xffff; + total = (pidx - sc->sc_rx.pidx) & 0xffff; + + DPRINTF("RX pidx=%08x total=%d\n", pidx, total); + + index = sc->sc_rx.next; + for (n = 0; n < total; n++) { + status = RD4(sc, GENET_RX_DESC_STATUS(index)); + len = __SHIFTOUT(status, GENET_RX_DESC_STATUS_BUFLEN); + + /* XXX check for errors */ + + bus_dmamap_sync(sc->sc_rx.buf_tag, sc->sc_rx.buf_map[index].map, + 0, sc->sc_rx.buf_map[index].map->dm_mapsize, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_rx.buf_tag, sc->sc_rx.buf_map[index].map); + + DPRINTF("RX [#%d] index=%02x status=%08x len=%d adj_len=%d\n", + n, index, status, len, len - ETHER_ALIGN); + + m = sc->sc_rx.buf_map[index].mbuf; + sc->sc_rx.buf_map[index].mbuf = NULL; + + if (len > ETHER_ALIGN) { + m_adj(m, ETHER_ALIGN); + + m->m_len = m->m_pkthdr.len = len - ETHER_ALIGN; + m->m_nextpkt = NULL; + + ml_enqueue(&ml, m); + } else { + ifp->if_ierrors++; + m_freem(m); + } + + if_rxr_put(&sc->sc_rx_ring, 1); + + index = RX_NEXT(index); + } + + if (sc->sc_rx.pidx != pidx) { + sc->sc_rx.next = index; + sc->sc_rx.pidx = pidx; + + genet_fill_rx_ring(sc, qid); + if_input(ifp, &ml); + } +} + +void +genet_txintr(struct genet_softc *sc, int qid) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct genet_bufmap *bmap; + uint32_t cidx, total; + int i; + + cidx = RD4(sc, GENET_TX_DMA_CONS_INDEX(qid)) & 0xffff; + total = (cidx - sc->sc_tx.cidx) & 0xffff; + + for (i = sc->sc_tx.next; sc->sc_tx.queued > 0 && total > 0; + i = TX_NEXT(i), total--) { + /* XXX check for errors */ + + bmap = &sc->sc_tx.buf_map[i]; + if (bmap->mbuf != NULL) { + bus_dmamap_sync(sc->sc_tx.buf_tag, bmap->map, + 0, bmap->map->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_tx.buf_tag, bmap->map); + m_freem(bmap->mbuf); + bmap->mbuf = NULL; + } + + --sc->sc_tx.queued; + } + + if (sc->sc_tx.queued == 0) + ifp->if_timer = 0; + + if (sc->sc_tx.cidx != cidx) { + sc->sc_tx.next = i; + sc->sc_tx.cidx = cidx; + + if (ifq_is_oactive(&ifp->if_snd)) + ifq_restart(&ifp->if_snd); + } +} + +void +genet_start(struct ifnet *ifp) +{ + struct genet_softc *sc = ifp->if_softc; + struct mbuf *m; + const int qid = GENET_DMA_DEFAULT_QUEUE; + int nsegs, index, cnt; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + if (ifq_is_oactive(&ifp->if_snd)) + return; + + index = sc->sc_tx.pidx & (TX_DESC_COUNT - 1); + cnt = 0; + + for (;;) { + m = ifq_deq_begin(&ifp->if_snd); + if (m == NULL) + break; + + nsegs = genet_setup_txbuf(sc, index, m); + if (nsegs == -1) { + ifq_deq_rollback(&ifp->if_snd, m); + ifq_set_oactive(&ifp->if_snd); + break; + } + if (nsegs == 0) { + ifq_deq_commit(&ifp->if_snd, m); + m_freem(m); + ifp->if_oerrors++; + continue; + } + ifq_deq_commit(&ifp->if_snd, m); + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); + + index = TX_SKIP(index, nsegs); + + sc->sc_tx.pidx = (sc->sc_tx.pidx + nsegs) & 0xffff; + cnt++; + } + + if (cnt != 0) { + WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), sc->sc_tx.pidx); + ifp->if_timer = 5; + } +} + +int +genet_intr(void *arg) +{ + struct genet_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t val; + + val = RD4(sc, GENET_INTRL2_CPU_STAT); + val &= ~RD4(sc, GENET_INTRL2_CPU_STAT_MASK); + WR4(sc, GENET_INTRL2_CPU_CLEAR, val); + + if (val & GENET_IRQ_RXDMA_DONE) + genet_rxintr(sc, GENET_DMA_DEFAULT_QUEUE); + + if (val & GENET_IRQ_TXDMA_DONE) { + genet_txintr(sc, GENET_DMA_DEFAULT_QUEUE); + if (ifq_is_oactive(&ifp->if_snd)) + ifq_restart(&ifp->if_snd); + } + + return 1; +} + +int +genet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) +{ + struct genet_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)addr; + int error = 0, s; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + genet_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + genet_stop(sc); + } + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + + case SIOCGIFRXR: + error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data, + NULL, MCLBYTES, &sc->sc_rx_ring); + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr); + break; + } + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + genet_setup_rxfilter(sc); + error = 0; + } + + splx(s); + return error; +} + +int +genet_setup_dma(struct genet_softc *sc, int qid) +{ + int error, i; + + /* Setup TX ring */ + sc->sc_tx.buf_tag = sc->sc_dmat; + for (i = 0; i < TX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->sc_tx.buf_tag, MCLBYTES, + TX_MAX_SEGS, MCLBYTES, 0, BUS_DMA_WAITOK, + &sc->sc_tx.buf_map[i].map); + if (error != 0) { + printf("%s: cannot create TX buffer map\n", + sc->sc_dev.dv_xname); + return error; + } + } + + /* Setup RX ring */ + sc->sc_rx.buf_tag = sc->sc_dmat; + for (i = 0; i < RX_DESC_COUNT; i++) { + error = bus_dmamap_create(sc->sc_rx.buf_tag, MCLBYTES, + 1, MCLBYTES, 0, BUS_DMA_WAITOK, + &sc->sc_rx.buf_map[i].map); + if (error != 0) { + printf("%s: cannot create RX buffer map\n", + sc->sc_dev.dv_xname); + return error; + } + } + + return 0; +} + +int +genet_attach(struct genet_softc *sc) +{ + struct mii_data *mii = &sc->sc_mii; + struct ifnet *ifp = &sc->sc_ac.ac_if; + int mii_flags = 0; + + switch (sc->sc_phy_mode) { + case GENET_PHY_MODE_RGMII_ID: + mii_flags |= MIIF_RXID | MIIF_TXID; + break; + case GENET_PHY_MODE_RGMII_RXID: + mii_flags |= MIIF_RXID; + break; + case GENET_PHY_MODE_RGMII_TXID: + mii_flags |= MIIF_TXID; + break; + case GENET_PHY_MODE_RGMII: + default: + break; + } + + printf(": address %s\n", ether_sprintf(sc->sc_lladdr)); + + /* Soft reset EMAC core */ + genet_reset(sc); + + /* Setup DMA descriptors */ + if (genet_setup_dma(sc, GENET_DMA_DEFAULT_QUEUE) != 0) { + printf("%s: failed to setup DMA descriptors\n", + sc->sc_dev.dv_xname); + return EINVAL; + } + + timeout_set(&sc->sc_stat_ch, genet_tick, sc); + timeout_set(&sc->sc_rxto, genet_rxtick, sc); + + /* Setup ethernet interface */ + ifp->if_softc = sc; + snprintf(ifp->if_xname, IFNAMSIZ, "%s", sc->sc_dev.dv_xname); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = genet_start; + ifp->if_ioctl = genet_ioctl; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + + /* 802.1Q VLAN-sized frames are supported */ + ifp->if_capabilities = IFCAP_VLAN_MTU; + + /* Attach MII driver */ + ifmedia_init(&mii->mii_media, 0, genet_media_change, genet_media_status); + mii->mii_ifp = ifp; + mii->mii_readreg = genet_mii_readreg; + mii->mii_writereg = genet_mii_writereg; + mii->mii_statchg = genet_mii_statchg; + mii_attach(&sc->sc_dev, mii, 0xffffffff, sc->sc_phy_id, + MII_OFFSET_ANY, mii_flags); + + if (LIST_EMPTY(&mii->mii_phys)) { + printf("%s: no PHY found!\n", sc->sc_dev.dv_xname); + ifmedia_add(&mii->mii_media, IFM_ETHER|IFM_MANUAL, 0, NULL); + ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_MANUAL); + } + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); + + /* Attach interface */ + if_attach(ifp); + + /* Attach ethernet interface */ + ether_ifattach(ifp); + + return 0; +} + +void +genet_lladdr_read(struct genet_softc *sc, uint8_t *lladdr) +{ + uint32_t maclo, machi; + + maclo = RD4(sc, GENET_UMAC_MAC0); + machi = RD4(sc, GENET_UMAC_MAC1); + + lladdr[0] = (maclo >> 24) & 0xff; + lladdr[1] = (maclo >> 16) & 0xff; + lladdr[2] = (maclo >> 8) & 0xff; + lladdr[3] = (maclo >> 0) & 0xff; + lladdr[4] = (machi >> 8) & 0xff; + lladdr[5] = (machi >> 0) & 0xff; +} diff --git a/sys/dev/ic/bcmgenetreg.h b/sys/dev/ic/bcmgenetreg.h new file mode 100644 index 00000000000..2cf24bb6ac0 --- /dev/null +++ b/sys/dev/ic/bcmgenetreg.h @@ -0,0 +1,175 @@ +/* $OpenBSD: bcmgenetreg.h,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */ +/* $NetBSD: bcmgenetreg.h,v 1.2 2020/02/22 13:41:41 jmcneill Exp $ */ + +/*- + * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * 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. + */ + +/* + * Broadcom GENETv5 + */ + +#ifndef _BCMGENETREG_H +#define _BCMGENETREG_H + +#define __BIT(__n) (1U << (__n)) +#define __BITS(__n, __m) ((__BIT((__n) - (__m) + 1) - 1) << (__m)) + +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) +#define __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) + +#define GENET_SYS_REV_CTRL 0x000 +#define GENET_SYS_REV_MAJOR __BITS(27,24) +#define GENET_SYS_REV_MINOR __BITS(19,16) +#define GENET_SYS_PORT_CTRL 0x004 +#define GENET_SYS_PORT_MODE_EXT_GPHY 3 +#define GENET_SYS_RBUF_FLUSH_CTRL 0x008 +#define GENET_SYS_RBUF_FLUSH_RESET __BIT(1) +#define GENET_SYS_TBUF_FLUSH_CTRL 0x00c +#define GENET_EXT_RGMII_OOB_CTRL 0x08c +#define GENET_EXT_RGMII_OOB_ID_MODE_DISABLE __BIT(16) +#define GENET_EXT_RGMII_OOB_RGMII_MODE_EN __BIT(6) +#define GENET_EXT_RGMII_OOB_OOB_DISABLE __BIT(5) +#define GENET_EXT_RGMII_OOB_RGMII_LINK __BIT(4) +#define GENET_INTRL2_CPU_STAT 0x200 +#define GENET_INTRL2_CPU_CLEAR 0x208 +#define GENET_INTRL2_CPU_STAT_MASK 0x20c +#define GENET_INTRL2_CPU_SET_MASK 0x210 +#define GENET_INTRL2_CPU_CLEAR_MASK 0x214 +#define GENET_IRQ_MDIO_ERROR __BIT(24) +#define GENET_IRQ_MDIO_DONE __BIT(23) +#define GENET_IRQ_TXDMA_DONE __BIT(16) +#define GENET_IRQ_RXDMA_DONE __BIT(13) +#define GENET_RBUF_CTRL 0x300 +#define GENET_RBUF_BAD_DIS __BIT(2) +#define GENET_RBUF_ALIGN_2B __BIT(1) +#define GENET_RBUF_64B_EN __BIT(0) +#define GENET_RBUF_TBUF_SIZE_CTRL 0x3b4 +#define GENET_UMAC_CMD 0x808 +#define GENET_UMAC_CMD_LCL_LOOP_EN __BIT(15) +#define GENET_UMAC_CMD_SW_RESET __BIT(13) +#define GENET_UMAC_CMD_PROMISC __BIT(4) +#define GENET_UMAC_CMD_SPEED __BITS(3,2) +#define GENET_UMAC_CMD_SPEED_10 0 +#define GENET_UMAC_CMD_SPEED_100 1 +#define GENET_UMAC_CMD_SPEED_1000 2 +#define GENET_UMAC_CMD_RXEN __BIT(1) +#define GENET_UMAC_CMD_TXEN __BIT(0) +#define GENET_UMAC_MAC0 0x80c +#define GENET_UMAC_MAC1 0x810 +#define GENET_UMAC_MAX_FRAME_LEN 0x814 +#define GENET_UMAC_TX_FLUSH 0xb34 +#define GENET_UMAC_MIB_CTRL 0xd80 +#define GENET_UMAC_MIB_RESET_TX __BIT(2) +#define GENET_UMAC_MIB_RESET_RUNT __BIT(1) +#define GENET_UMAC_MIB_RESET_RX __BIT(0) +#define GENET_MDIO_CMD 0xe14 +#define GENET_MDIO_START_BUSY __BIT(29) +#define GENET_MDIO_READ __BIT(27) +#define GENET_MDIO_WRITE __BIT(26) +#define GENET_MDIO_PMD __BITS(25,21) +#define GENET_MDIO_REG __BITS(20,16) +#define GENET_UMAC_MDF_CTRL 0xe50 +#define GENET_UMAC_MDF_ADDR0(n) (0xe54 + (n) * 0x8) +#define GENET_UMAC_MDF_ADDR1(n) (0xe58 + (n) * 0x8) + +#define GENET_DMA_DESC_COUNT 256 +#define GENET_DMA_DESC_SIZE 12 +#define GENET_DMA_DEFAULT_QUEUE 16 + +#define GENET_DMA_RING_SIZE 0x40 +#define GENET_DMA_RINGS_SIZE (GENET_DMA_RING_SIZE * (GENET_DMA_DEFAULT_QUEUE + 1)) + +#define GENET_RX_BASE 0x2000 +#define GENET_TX_BASE 0x4000 + +#define GENET_RX_DMA_RINGBASE(qid) (GENET_RX_BASE + 0xc00 + GENET_DMA_RING_SIZE * (qid)) +#define GENET_RX_DMA_WRITE_PTR_LO(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x00) +#define GENET_RX_DMA_WRITE_PTR_HI(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x04) +#define GENET_RX_DMA_PROD_INDEX(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x08) +#define GENET_RX_DMA_CONS_INDEX(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x0c) +#define GENET_RX_DMA_RING_BUF_SIZE(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x10) +#define GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT __BITS(31,16) +#define GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH __BITS(15,0) +#define GENET_RX_DMA_START_ADDR_LO(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x14) +#define GENET_RX_DMA_START_ADDR_HI(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x18) +#define GENET_RX_DMA_END_ADDR_LO(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x1c) +#define GENET_RX_DMA_END_ADDR_HI(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x20) +#define GENET_RX_DMA_XON_XOFF_THRES(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x28) +#define GENET_RX_DMA_XON_XOFF_THRES_LO __BITS(31,16) +#define GENET_RX_DMA_XON_XOFF_THRES_HI __BITS(15,0) +#define GENET_RX_DMA_READ_PTR_LO(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x2c) +#define GENET_RX_DMA_READ_PTR_HI(qid) (GENET_RX_DMA_RINGBASE(qid) + 0x30) + +#define GENET_TX_DMA_RINGBASE(qid) (GENET_TX_BASE + 0xc00 + GENET_DMA_RING_SIZE * (qid)) +#define GENET_TX_DMA_READ_PTR_LO(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x00) +#define GENET_TX_DMA_READ_PTR_HI(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x04) +#define GENET_TX_DMA_CONS_INDEX(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x08) +#define GENET_TX_DMA_PROD_INDEX(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x0c) +#define GENET_TX_DMA_RING_BUF_SIZE(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x10) +#define GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT __BITS(31,16) +#define GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH __BITS(15,0) +#define GENET_TX_DMA_START_ADDR_LO(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x14) +#define GENET_TX_DMA_START_ADDR_HI(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x18) +#define GENET_TX_DMA_END_ADDR_LO(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x1c) +#define GENET_TX_DMA_END_ADDR_HI(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x20) +#define GENET_TX_DMA_MBUF_DONE_THRES(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x24) +#define GENET_TX_DMA_FLOW_PERIOD(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x28) +#define GENET_TX_DMA_WRITE_PTR_LO(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x2c) +#define GENET_TX_DMA_WRITE_PTR_HI(qid) (GENET_TX_DMA_RINGBASE(qid) + 0x30) + +#define GENET_RX_DESC_STATUS(idx) (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x00) +#define GENET_RX_DESC_STATUS_BUFLEN __BITS(27,16) +#define GENET_RX_DESC_STATUS_OWN __BIT(15) +#define GENET_RX_DESC_STATUS_EOP __BIT(14) +#define GENET_RX_DESC_STATUS_SOP __BIT(13) +#define GENET_RX_DESC_STATUS_RX_ERROR __BIT(2) +#define GENET_RX_DESC_ADDRESS_LO(idx) (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x04) +#define GENET_RX_DESC_ADDRESS_HI(idx) (GENET_RX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x08) + +#define GENET_TX_DESC_STATUS(idx) (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x00) +#define GENET_TX_DESC_STATUS_BUFLEN __BITS(27,16) +#define GENET_TX_DESC_STATUS_OWN __BIT(15) +#define GENET_TX_DESC_STATUS_EOP __BIT(14) +#define GENET_TX_DESC_STATUS_SOP __BIT(13) +#define GENET_TX_DESC_STATUS_QTAG __BITS(12,7) +#define GENET_TX_DESC_STATUS_CRC __BIT(6) +#define GENET_TX_DESC_ADDRESS_LO(idx) (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x04) +#define GENET_TX_DESC_ADDRESS_HI(idx) (GENET_TX_BASE + GENET_DMA_DESC_SIZE * (idx) + 0x08) + +#define GENET_RX_DMA_RING_CFG (GENET_RX_BASE + 0x1040 + 0x00) +#define GENET_RX_DMA_CTRL (GENET_RX_BASE + 0x1040 + 0x04) +#define GENET_RX_DMA_CTRL_RBUF_EN(qid) __BIT((qid) + 1) +#define GENET_RX_DMA_CTRL_EN __BIT(0) +#define GENET_RX_SCB_BURST_SIZE (GENET_RX_BASE + 0x1040 + 0x0c) + +#define GENET_TX_DMA_RING_CFG (GENET_TX_BASE + 0x1040 + 0x00) +#define GENET_TX_DMA_CTRL (GENET_TX_BASE + 0x1040 + 0x04) +#define GENET_TX_DMA_CTRL_RBUF_EN(qid) __BIT((qid) + 1) +#define GENET_TX_DMA_CTRL_EN __BIT(0) +#define GENET_TX_SCB_BURST_SIZE (GENET_TX_BASE + 0x1040 + 0x0c) + +#endif /* !_BCMGENETREG_H */ diff --git a/sys/dev/ic/bcmgenetvar.h b/sys/dev/ic/bcmgenetvar.h new file mode 100644 index 00000000000..8076b8027d9 --- /dev/null +++ b/sys/dev/ic/bcmgenetvar.h @@ -0,0 +1,83 @@ +/* $OpenBSD: bcmgenetvar.h,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */ +/* $NetBSD: bcmgenetvar.h,v 1.1 2020/02/22 00:28:35 jmcneill Exp $ */ + +/*- + * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> + * All rights reserved. + * + * 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. + */ + +/* + * Broadcom GENETv5 + */ + +#ifndef _BCMGENETVAR_H +#define _BCMGENETVAR_H + +#include <dev/ic/bcmgenetreg.h> + +enum genet_phy_mode { + GENET_PHY_MODE_RGMII, + GENET_PHY_MODE_RGMII_ID, + GENET_PHY_MODE_RGMII_TXID, + GENET_PHY_MODE_RGMII_RXID, +}; + +struct genet_bufmap { + bus_dmamap_t map; + struct mbuf *mbuf; +}; + +struct genet_ring { + bus_dma_tag_t buf_tag; + struct genet_bufmap buf_map[GENET_DMA_DESC_COUNT]; + u_int next, queued; + uint32_t cidx, pidx; +}; + +struct genet_softc { + struct device sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + int sc_phy_id; + enum genet_phy_mode sc_phy_mode; + + void *sc_ih; + + struct arpcom sc_ac; +#define sc_lladdr sc_ac.ac_enaddr + struct mii_data sc_mii; + struct timeout sc_stat_ch; + + struct genet_ring sc_tx; + struct genet_ring sc_rx; + struct if_rxring sc_rx_ring; + struct timeout sc_rxto; +}; + +int genet_attach(struct genet_softc *); +int genet_intr(void *); +void genet_lladdr_read(struct genet_softc *, uint8_t *); + +#endif /* !_BCMGENETVAR_H */ |