summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/conf/files7
-rw-r--r--sys/dev/ic/dwc_gmac.c1484
-rw-r--r--sys/dev/ic/dwc_gmac_reg.h227
-rw-r--r--sys/dev/ic/dwc_gmac_var.h95
4 files changed, 1812 insertions, 1 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 4482008ec2f..bc8536727cf 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.623 2016/08/11 09:30:57 natano Exp $
+# $OpenBSD: files,v 1.624 2016/08/13 20:35:57 kettenis Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -317,6 +317,11 @@ file dev/ic/i82596.c ie & (ie_pci | ie_eisa | ie_gsc)
device gem: ether, ifnet, ifmedia, mii
file dev/ic/gem.c gem
+# Synopsis Designware GMAC core, as found on Allwinner A20
+# and other SoCs
+device dwge: ether, ifnet, ifmedia, mii
+file dev/ic/dwc_gmac.c dwge
+
device ti: ether, ifnet, ifmedia, mii, firmload
file dev/ic/ti.c ti
diff --git a/sys/dev/ic/dwc_gmac.c b/sys/dev/ic/dwc_gmac.c
new file mode 100644
index 00000000000..8d66ec7c669
--- /dev/null
+++ b/sys/dev/ic/dwc_gmac.c
@@ -0,0 +1,1484 @@
+/* $OpenBSD: dwc_gmac.c,v 1.1 2016/08/13 20:35:57 kettenis Exp $ */
+/* $NetBSD: dwc_gmac.c,v 1.34 2015/08/21 20:12:29 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry and Martin Husemann.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * This driver supports the Synopsis Designware GMAC core, as found
+ * on Allwinner A20 cores and others.
+ *
+ * Real documentation seems to not be available, the marketing product
+ * documents could be found here:
+ *
+ * http://www.synopsys.com/dw/ipdir.php?ds=dwc_ether_mac10_100_1000_unive
+ */
+
+/*__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.34 2015/08/21 20:12:29 jmcneill Exp $");*/
+
+/* #define DWC_GMAC_DEBUG 1 */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+#include <sys/socket.h>
+#include <sys/timeout.h>
+#include <sys/mbuf.h>
+#include <machine/intr.h>
+#include <machine/bus.h>
+
+#include "bpfilter.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/dwc_gmac_reg.h>
+#include <dev/ic/dwc_gmac_var.h>
+
+int dwc_gmac_ifmedia_upd(struct ifnet *);
+void dwc_gmac_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+int dwc_gmac_miibus_read_reg(struct device *, int, int);
+void dwc_gmac_miibus_write_reg(struct device *, int, int, int);
+void dwc_gmac_miibus_statchg(struct device *);
+
+int dwc_gmac_reset(struct dwc_gmac_softc *);
+void dwc_gmac_write_hwaddr(struct dwc_gmac_softc *, uint8_t *enaddr);
+int dwc_gmac_alloc_dma_rings(struct dwc_gmac_softc *);
+void dwc_gmac_free_dma_rings(struct dwc_gmac_softc *);
+int dwc_gmac_alloc_rx_ring(struct dwc_gmac_softc *, struct dwc_gmac_rx_ring *);
+void dwc_gmac_reset_rx_ring(struct dwc_gmac_softc *, struct dwc_gmac_rx_ring *);
+void dwc_gmac_free_rx_ring(struct dwc_gmac_softc *, struct dwc_gmac_rx_ring *);
+int dwc_gmac_alloc_tx_ring(struct dwc_gmac_softc *, struct dwc_gmac_tx_ring *);
+void dwc_gmac_reset_tx_ring(struct dwc_gmac_softc *, struct dwc_gmac_tx_ring *);
+void dwc_gmac_free_tx_ring(struct dwc_gmac_softc *, struct dwc_gmac_tx_ring *);
+void dwc_gmac_txdesc_sync(struct dwc_gmac_softc *, int, int, int);
+int dwc_gmac_init(struct ifnet *);
+void dwc_gmac_stop(struct ifnet *, int);
+void dwc_gmac_start(struct ifnet *);
+int dwc_gmac_queue(struct dwc_gmac_softc *, struct mbuf *);
+int dwc_gmac_ioctl(struct ifnet *, u_long, caddr_t);
+void dwc_gmac_tx_intr(struct dwc_gmac_softc *);
+void dwc_gmac_rx_intr(struct dwc_gmac_softc *);
+void dwc_gmac_setmulti(struct dwc_gmac_softc *);
+int dwc_gmac_ifflags_cb(struct arpcom *);
+static uint32_t bitrev32(uint32_t);
+
+#define TX_DESC_OFFSET(N) ((DWGE_RX_RING_COUNT+(N)) \
+ *sizeof(struct dwc_gmac_dev_dmadesc))
+#define TX_NEXT(N) (((N)+1) & (DWGE_TX_RING_COUNT-1))
+
+#define RX_DESC_OFFSET(N) ((N)*sizeof(struct dwc_gmac_dev_dmadesc))
+#define RX_NEXT(N) (((N)+1) & (DWGE_RX_RING_COUNT-1))
+
+
+
+#define GMAC_DEF_DMA_INT_MASK (GMAC_DMA_INT_TIE|GMAC_DMA_INT_RIE| \
+ GMAC_DMA_INT_NIE|GMAC_DMA_INT_AIE| \
+ GMAC_DMA_INT_FBE|GMAC_DMA_INT_UNE)
+
+#define GMAC_DMA_INT_ERRORS (GMAC_DMA_INT_AIE|GMAC_DMA_INT_ERE| \
+ GMAC_DMA_INT_FBE| \
+ GMAC_DMA_INT_RWE|GMAC_DMA_INT_RUE| \
+ GMAC_DMA_INT_UNE|GMAC_DMA_INT_OVE| \
+ GMAC_DMA_INT_TJE)
+
+#define AWIN_DEF_MAC_INTRMASK \
+ (AWIN_GMAC_MAC_INT_TSI | AWIN_GMAC_MAC_INT_ANEG | \
+ AWIN_GMAC_MAC_INT_LINKCHG | AWIN_GMAC_MAC_INT_RGSMII)
+
+
+#ifdef DWC_GMAC_DEBUG
+void dwc_gmac_dump_dma(struct dwc_gmac_softc *sc);
+void dwc_gmac_dump_tx_desc(struct dwc_gmac_softc *sc);
+void dwc_gmac_dump_rx_desc(struct dwc_gmac_softc *sc);
+void dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg);
+void dwc_dump_status(struct dwc_gmac_softc *sc);
+void dwc_gmac_dump_ffilt(struct dwc_gmac_softc *sc, uint32_t ffilt);
+#endif
+
+struct cfdriver dwge_cd = {
+ NULL, "dwge", DV_IFNET
+};
+
+void
+dwc_gmac_attach(struct dwc_gmac_softc *sc, uint32_t mii_clk)
+{
+ uint8_t enaddr[ETHER_ADDR_LEN];
+ struct mii_data * const mii = &sc->sc_mii;
+ struct ifnet * const ifp = &sc->sc_ac.ac_if;
+ uint32_t maclo, machi;
+ int s;
+
+ mtx_init(&sc->sc_mdio_lock, IPL_NET);
+ sc->sc_mii_clk = mii_clk & 7;
+
+ /*
+ * If we did not get an externaly configure address,
+ * try to read one from the current filter setup,
+ * before resetting the chip.
+ */
+ maclo = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_ADDR0LO);
+ machi = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_ADDR0HI);
+
+ if (maclo == 0xffffffff && (machi & 0xffff) == 0xffff) {
+ ether_fakeaddr(&sc->sc_ac.ac_if);
+ memcpy(enaddr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
+ } else {
+ enaddr[0] = maclo & 0x0ff;
+ enaddr[1] = (maclo >> 8) & 0x0ff;
+ enaddr[2] = (maclo >> 16) & 0x0ff;
+ enaddr[3] = (maclo >> 24) & 0x0ff;
+ enaddr[4] = machi & 0x0ff;
+ enaddr[5] = (machi >> 8) & 0x0ff;
+ }
+
+ /*
+ * Init chip and do initial setup
+ */
+ if (dwc_gmac_reset(sc) != 0)
+ return; /* not much to cleanup, haven't attached yet */
+ dwc_gmac_write_hwaddr(sc, enaddr);
+ printf("%s: Ethernet address: %s\n", sc->sc_dev.dv_xname,
+ ether_sprintf(enaddr));
+ memcpy(sc->sc_ac.ac_enaddr, enaddr, ETHER_ADDR_LEN);
+
+ /*
+ * Allocate Tx and Rx rings
+ */
+ if (dwc_gmac_alloc_dma_rings(sc) != 0) {
+ printf("%s: could not allocate DMA rings\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ if (dwc_gmac_alloc_tx_ring(sc, &sc->sc_txq) != 0) {
+ printf("%s: could not allocate Tx ring\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ mtx_init(&sc->sc_rxq.r_mtx, IPL_NET);
+ if (dwc_gmac_alloc_rx_ring(sc, &sc->sc_rxq) != 0) {
+ printf("%s: could not allocate Rx ring\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ /*
+ * Prepare interface data
+ */
+ ifp->if_softc = sc;
+ strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = dwc_gmac_ioctl;
+ ifp->if_start = dwc_gmac_start;
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+
+ /*
+ * Attach MII subdevices
+ */
+ mii->mii_ifp = ifp;
+ mii->mii_readreg = dwc_gmac_miibus_read_reg;
+ mii->mii_writereg = dwc_gmac_miibus_write_reg;
+ mii->mii_statchg = dwc_gmac_miibus_statchg;
+
+ ifmedia_init(&mii->mii_media, 0, dwc_gmac_ifmedia_upd,
+ dwc_gmac_ifmedia_sts);
+ mii_attach((void *)sc, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY,
+ MIIF_DOPAUSE);
+
+ 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);
+ } else {
+ ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_AUTO);
+ }
+
+ /*
+ * Ready, attach interface
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ /*
+ * Enable interrupts
+ */
+ s = splnet();
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_INTMASK,
+ AWIN_DEF_MAC_INTRMASK);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_INTENABLE,
+ GMAC_DEF_DMA_INT_MASK);
+ splx(s);
+
+ return;
+
+fail:
+ dwc_gmac_free_rx_ring(sc, &sc->sc_rxq);
+ dwc_gmac_free_tx_ring(sc, &sc->sc_txq);
+}
+
+int
+dwc_gmac_ifmedia_upd(struct ifnet *ifp)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ struct mii_data *mii = &sc->sc_mii;
+ int err;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ err = mii_mediachg(mii);
+ return (err);
+}
+
+void
+dwc_gmac_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ struct mii_data *mii = &sc->sc_mii;
+
+ mii_pollstat(mii);
+
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+int
+dwc_gmac_reset(struct dwc_gmac_softc *sc)
+{
+ size_t cnt;
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE) | GMAC_BUSMODE_RESET);
+ for (cnt = 0; cnt < 3000; cnt++) {
+ if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE)
+ & GMAC_BUSMODE_RESET) == 0)
+ return 0;
+ delay(10);
+ }
+
+ printf("%s: reset timed out\n", sc->sc_dev.dv_xname);
+ return EIO;
+}
+
+void
+dwc_gmac_write_hwaddr(struct dwc_gmac_softc *sc, uint8_t *enaddr)
+{
+ uint32_t lo, hi;
+
+ lo = enaddr[0] | (enaddr[1] << 8) | (enaddr[2] << 16)
+ | (enaddr[3] << 24);
+ hi = enaddr[4] | (enaddr[5] << 8);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0LO, lo);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0HI, hi);
+}
+
+int
+dwc_gmac_miibus_read_reg(struct device *self, int phy, int reg)
+{
+ struct dwc_gmac_softc * const sc = (struct dwc_gmac_softc *)self;
+ uint16_t mii;
+ size_t cnt;
+ int rv = 0;
+
+ mii = ((phy & GMAC_MII_PHY_MASK) << GMAC_MII_PHY_SHIFT)
+ | ((reg & GMAC_MII_REG_MASK) << GMAC_MII_REG_SHIFT)
+ | ((sc->sc_mii_clk & GMAC_MII_CLKMASK_MASK)
+ << GMAC_MII_CLKMASK_SHIFT)
+ | GMAC_MII_BUSY;
+
+ mtx_enter(&sc->sc_mdio_lock);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR, mii);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_MIIADDR) & GMAC_MII_BUSY)) {
+ rv = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_MIIDATA);
+ break;
+ }
+ delay(10);
+ }
+
+ mtx_leave(&sc->sc_mdio_lock);
+
+ return rv;
+}
+
+void
+dwc_gmac_miibus_write_reg(struct device *self, int phy, int reg, int val)
+{
+ struct dwc_gmac_softc * const sc = (struct dwc_gmac_softc *)self;
+ uint16_t mii;
+ size_t cnt;
+
+ mii = ((phy & GMAC_MII_PHY_MASK) << GMAC_MII_PHY_SHIFT)
+ | ((reg & GMAC_MII_REG_MASK) << GMAC_MII_REG_SHIFT)
+ | ((sc->sc_mii_clk & GMAC_MII_CLKMASK_MASK)
+ << GMAC_MII_CLKMASK_SHIFT)
+ | GMAC_MII_BUSY | GMAC_MII_WRITE;
+
+ mtx_enter(&sc->sc_mdio_lock);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIDATA, val);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR, mii);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_MIIADDR) & GMAC_MII_BUSY))
+ break;
+ delay(10);
+ }
+
+ mtx_leave(&sc->sc_mdio_lock);
+}
+
+int
+dwc_gmac_alloc_rx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_rx_ring *ring)
+{
+ struct dwc_gmac_rx_data *data;
+ bus_addr_t physaddr;
+ const size_t descsize = DWGE_RX_RING_COUNT * sizeof(*ring->r_desc);
+ int error, i, next;
+
+ ring->r_cur = ring->r_next = 0;
+ memset(ring->r_desc, 0, descsize);
+
+ /*
+ * Pre-allocate Rx buffers and populate Rx ring.
+ */
+ for (i = 0; i < DWGE_RX_RING_COUNT; i++) {
+ struct dwc_gmac_dev_dmadesc *desc;
+
+ data = &sc->sc_rxq.r_data[i];
+
+ MGETHDR(data->rd_m, M_DONTWAIT, MT_DATA);
+ if (data->rd_m == NULL) {
+ printf("%s: could not allocate rx mbuf #%d\n",
+ sc->sc_dev.dv_xname, i);
+ error = ENOMEM;
+ goto fail;
+ }
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
+ MCLBYTES, 0, BUS_DMA_NOWAIT, &data->rd_map);
+ if (error != 0) {
+ printf("%s: could not create DMA map\n",
+ sc->sc_dev.dv_xname);
+ data->rd_map = NULL;
+ goto fail;
+ }
+ MCLGET(data->rd_m, M_DONTWAIT);
+ if (!(data->rd_m->m_flags & M_EXT)) {
+ printf("%s: could not allocate mbuf cluster #%d\n",
+ sc->sc_dev.dv_xname, i);
+ error = ENOMEM;
+ goto fail;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmat, data->rd_map,
+ mtod(data->rd_m, void *), MCLBYTES, NULL,
+ BUS_DMA_READ | BUS_DMA_NOWAIT);
+ if (error != 0) {
+ printf("%s: could not load rx buf DMA map #%d",
+ sc->sc_dev.dv_xname, i);
+ goto fail;
+ }
+ physaddr = data->rd_map->dm_segs[0].ds_addr;
+
+ desc = &sc->sc_rxq.r_desc[i];
+ desc->ddesc_data = htole32(physaddr);
+ next = RX_NEXT(i);
+ desc->ddesc_next = htole32(ring->r_physaddr
+ + next * sizeof(*desc));
+ desc->ddesc_cntl = htole32(
+ ((DWGE_MAX_PACKET & DDESC_CNTL_SIZE1MASK)
+ << DDESC_CNTL_SIZE1SHIFT) | DDESC_CNTL_RXCHAIN);
+ desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, 0,
+ DWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc),
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR,
+ ring->r_physaddr);
+
+ return 0;
+
+fail:
+ dwc_gmac_free_rx_ring(sc, ring);
+ return error;
+}
+
+void
+dwc_gmac_reset_rx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_rx_ring *ring)
+{
+ struct dwc_gmac_dev_dmadesc *desc;
+ int i;
+
+ for (i = 0; i < DWGE_RX_RING_COUNT; i++) {
+ desc = &sc->sc_rxq.r_desc[i];
+ desc->ddesc_cntl = htole32(
+ ((DWGE_MAX_PACKET & DDESC_CNTL_SIZE1MASK)
+ << DDESC_CNTL_SIZE1SHIFT) | DDESC_CNTL_RXCHAIN);
+ desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, 0,
+ DWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc),
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ ring->r_cur = ring->r_next = 0;
+ /* reset DMA address to start of ring */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR,
+ sc->sc_rxq.r_physaddr);
+}
+
+int
+dwc_gmac_alloc_dma_rings(struct dwc_gmac_softc *sc)
+{
+ const size_t descsize = DWGE_TOTAL_RING_COUNT *
+ sizeof(struct dwc_gmac_dev_dmadesc);
+ int error, nsegs;
+ caddr_t rings;
+
+ error = bus_dmamap_create(sc->sc_dmat, descsize, 1, descsize, 0,
+ BUS_DMA_NOWAIT, &sc->sc_dma_ring_map);
+ if (error != 0) {
+ printf("%s: could not create desc DMA map\n", sc->sc_dev.dv_xname);
+ sc->sc_dma_ring_map = NULL;
+ goto fail;
+ }
+
+ error = bus_dmamem_alloc(sc->sc_dmat, descsize, PAGE_SIZE, 0,
+ &sc->sc_dma_ring_seg, 1, &nsegs, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
+ if (error != 0) {
+ printf("%s: could not map DMA memory\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ error = bus_dmamem_map(sc->sc_dmat, &sc->sc_dma_ring_seg, nsegs,
+ descsize, &rings, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
+ if (error != 0) {
+ printf("%s: could not allocate DMA memory\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_ring_map, rings,
+ descsize, NULL, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
+ if (error != 0) {
+ printf("%s: could not load desc DMA map\n", sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ /* give first DWGE_RX_RING_COUNT to the RX side */
+ sc->sc_rxq.r_desc = (struct dwc_gmac_dev_dmadesc *)rings;
+ sc->sc_rxq.r_physaddr = sc->sc_dma_ring_map->dm_segs[0].ds_addr;
+
+ /* and next rings to the TX side */
+ sc->sc_txq.t_desc = sc->sc_rxq.r_desc + DWGE_RX_RING_COUNT;
+ sc->sc_txq.t_physaddr = sc->sc_rxq.r_physaddr +
+ DWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc);
+
+ return 0;
+
+fail:
+ dwc_gmac_free_dma_rings(sc);
+ return error;
+}
+
+void
+dwc_gmac_free_dma_rings(struct dwc_gmac_softc *sc)
+{
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, 0,
+ sc->sc_dma_ring_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_dma_ring_map);
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_rxq.r_desc,
+ DWGE_TOTAL_RING_COUNT * sizeof(struct dwc_gmac_dev_dmadesc));
+ bus_dmamem_free(sc->sc_dmat, &sc->sc_dma_ring_seg, 1);
+}
+
+void
+dwc_gmac_free_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *ring)
+{
+ struct dwc_gmac_rx_data *data;
+ int i;
+
+ if (ring->r_desc == NULL)
+ return;
+
+
+ for (i = 0; i < DWGE_RX_RING_COUNT; i++) {
+ data = &ring->r_data[i];
+
+ if (data->rd_map != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, data->rd_map, 0,
+ DWGE_RX_RING_COUNT
+ *sizeof(struct dwc_gmac_dev_dmadesc),
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_dmat, data->rd_map);
+ bus_dmamap_destroy(sc->sc_dmat, data->rd_map);
+ }
+ if (data->rd_m != NULL)
+ m_freem(data->rd_m);
+ }
+}
+
+int
+dwc_gmac_alloc_tx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_tx_ring *ring)
+{
+ int i, error = 0;
+
+ ring->t_queued = 0;
+ ring->t_cur = ring->t_next = 0;
+
+ memset(ring->t_desc, 0, DWGE_TX_RING_COUNT*sizeof(*ring->t_desc));
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ TX_DESC_OFFSET(0),
+ DWGE_TX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc),
+ BUS_DMASYNC_POSTWRITE);
+
+ for (i = 0; i < DWGE_TX_RING_COUNT; i++) {
+ error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
+ DWGE_TX_RING_COUNT, MCLBYTES, 0,
+ BUS_DMA_NOWAIT|BUS_DMA_COHERENT,
+ &ring->t_data[i].td_map);
+ if (error != 0) {
+ printf("%s: could not create TX DMA map #%d\n",
+ sc->sc_dev.dv_xname, i);
+ ring->t_data[i].td_map = NULL;
+ goto fail;
+ }
+ ring->t_desc[i].ddesc_next = htole32(
+ ring->t_physaddr + sizeof(struct dwc_gmac_dev_dmadesc)
+ *TX_NEXT(i));
+ }
+
+ return 0;
+
+fail:
+ dwc_gmac_free_tx_ring(sc, ring);
+ return error;
+}
+
+void
+dwc_gmac_txdesc_sync(struct dwc_gmac_softc *sc, int start, int end, int ops)
+{
+ /* 'end' is pointing one descriptor beyound the last we want to sync */
+ if (end > start) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ TX_DESC_OFFSET(start),
+ TX_DESC_OFFSET(end)-TX_DESC_OFFSET(start),
+ ops);
+ return;
+ }
+ /* sync from 'start' to end of ring */
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ TX_DESC_OFFSET(start),
+ TX_DESC_OFFSET(DWGE_TX_RING_COUNT)-TX_DESC_OFFSET(start),
+ ops);
+ /* sync from start of ring to 'end' */
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ TX_DESC_OFFSET(0),
+ TX_DESC_OFFSET(end)-TX_DESC_OFFSET(0),
+ ops);
+}
+
+void
+dwc_gmac_reset_tx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_tx_ring *ring)
+{
+ int i;
+
+ for (i = 0; i < DWGE_TX_RING_COUNT; i++) {
+ struct dwc_gmac_tx_data *data = &ring->t_data[i];
+
+ if (data->td_m != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, data->td_active,
+ 0, data->td_active->dm_mapsize,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, data->td_active);
+ m_freem(data->td_m);
+ data->td_m = NULL;
+ }
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ TX_DESC_OFFSET(0),
+ DWGE_TX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc),
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_TX_ADDR,
+ sc->sc_txq.t_physaddr);
+
+ ring->t_queued = 0;
+ ring->t_cur = ring->t_next = 0;
+}
+
+void
+dwc_gmac_free_tx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_tx_ring *ring)
+{
+ int i;
+
+ /* unload the maps */
+ for (i = 0; i < DWGE_TX_RING_COUNT; i++) {
+ struct dwc_gmac_tx_data *data = &ring->t_data[i];
+
+ if (data->td_m != NULL) {
+ bus_dmamap_sync(sc->sc_dmat, data->td_active,
+ 0, data->td_map->dm_mapsize,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, data->td_active);
+ m_freem(data->td_m);
+ data->td_m = NULL;
+ }
+ }
+
+ /* and actually free them */
+ for (i = 0; i < DWGE_TX_RING_COUNT; i++) {
+ struct dwc_gmac_tx_data *data = &ring->t_data[i];
+
+ bus_dmamap_destroy(sc->sc_dmat, data->td_map);
+ }
+}
+
+void
+dwc_gmac_miibus_statchg(struct device *dev)
+{
+ struct dwc_gmac_softc * const sc = (struct dwc_gmac_softc *)dev;
+ struct mii_data * const mii = &sc->sc_mii;
+ uint32_t conf, flow;
+
+ /*
+ * Set MII or GMII interface based on the speed
+ * negotiated by the PHY.
+ */
+ conf = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_CONF);
+ conf &= ~(AWIN_GMAC_MAC_CONF_FES100|AWIN_GMAC_MAC_CONF_MIISEL
+ |AWIN_GMAC_MAC_CONF_FULLDPLX);
+ conf |= AWIN_GMAC_MAC_CONF_FRAMEBURST
+ | AWIN_GMAC_MAC_CONF_DISABLERXOWN
+ | AWIN_GMAC_MAC_CONF_DISABLEJABBER
+ | AWIN_GMAC_MAC_CONF_ACS
+ | AWIN_GMAC_MAC_CONF_RXENABLE
+ | AWIN_GMAC_MAC_CONF_TXENABLE;
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_10_T:
+ conf |= AWIN_GMAC_MAC_CONF_MIISEL;
+ break;
+ case IFM_100_TX:
+ conf |= AWIN_GMAC_MAC_CONF_FES100 |
+ AWIN_GMAC_MAC_CONF_MIISEL;
+ break;
+ case IFM_1000_T:
+ break;
+ }
+
+ flow = 0;
+ if (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) {
+ conf |= AWIN_GMAC_MAC_CONF_FULLDPLX;
+ flow |= ((0x200 & AWIN_GMAC_MAC_FLOWCTRL_PAUSE_MASK)
+ << AWIN_GMAC_MAC_FLOWCTRL_PAUSE_SHIFT);
+ }
+ if (mii->mii_media_active & IFM_ETH_TXPAUSE) {
+ flow |= AWIN_GMAC_MAC_FLOWCTRL_TFE;
+ }
+ if (mii->mii_media_active & IFM_ETH_RXPAUSE) {
+ flow |= AWIN_GMAC_MAC_FLOWCTRL_RFE;
+ }
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_FLOWCTRL, flow);
+
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: setting MAC conf register: %08x\n",
+ sc->sc_dev.dv_xname, conf);
+#endif
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_CONF, conf);
+}
+
+int
+dwc_gmac_init(struct ifnet *ifp)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ uint32_t ffilt;
+
+ if (ifp->if_flags & IFF_RUNNING)
+ return 0;
+
+ dwc_gmac_stop(ifp, 0);
+
+ /*
+ * Configure DMA burst/transfer mode and RX/TX priorities.
+ * XXX - the GMAC_BUSMODE_PRIORXTX bits are undocumented.
+ */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE,
+ GMAC_BUSMODE_FIXEDBURST | GMAC_BUSMODE_4PBL |
+ 2 << GMAC_BUSMODE_RPBL_SHIFT |
+ 2 << GMAC_BUSMODE_PBL_SHIFT);
+
+ /*
+ * Set up address filter
+ */
+ ffilt = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT);
+ if (ifp->if_flags & IFF_PROMISC) {
+ ffilt |= AWIN_GMAC_MAC_FFILT_PR;
+ } else {
+ ffilt &= ~AWIN_GMAC_MAC_FFILT_PR;
+ }
+ if (ifp->if_flags & IFF_BROADCAST) {
+ ffilt &= ~AWIN_GMAC_MAC_FFILT_DBF;
+ } else {
+ ffilt |= AWIN_GMAC_MAC_FFILT_DBF;
+ }
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT, ffilt);
+
+ /*
+ * Set up multicast filter
+ */
+ dwc_gmac_setmulti(sc);
+
+ /*
+ * Set up dma pointer for RX and TX ring
+ */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR,
+ sc->sc_rxq.r_physaddr);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_TX_ADDR,
+ sc->sc_txq.t_physaddr);
+
+ /*
+ * Start RX/TX part
+ */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_OPMODE, GMAC_DMA_OP_RXSTART | GMAC_DMA_OP_TXSTART |
+ GMAC_DMA_OP_RXSTOREFORWARD | GMAC_DMA_OP_TXSTOREFORWARD);
+
+ ifq_clr_oactive(&ifp->if_snd);
+ ifp->if_flags |= IFF_RUNNING;
+
+ return 0;
+}
+
+void
+dwc_gmac_start(struct ifnet *ifp)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ int old = sc->sc_txq.t_queued;
+ int start = sc->sc_txq.t_cur;
+ struct mbuf *m_head = NULL;
+
+ if (ifq_is_oactive(&ifp->if_snd) || !(ifp->if_flags & IFF_RUNNING))
+ return;
+
+ for (;;) {
+ m_head = ifq_deq_begin(&ifp->if_snd);
+ if (m_head == NULL)
+ break;
+ if (dwc_gmac_queue(sc, m_head) != 0) {
+ ifq_deq_rollback(&ifp->if_snd, m_head);
+ ifq_set_oactive(&ifp->if_snd);
+ break;
+ }
+
+ ifq_deq_commit(&ifp->if_snd, m_head);
+
+ ifp->if_opackets++;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
+#endif
+
+ if (sc->sc_txq.t_queued == DWGE_TX_RING_COUNT) {
+ ifq_set_oactive(&ifp->if_snd);
+ break;
+ }
+ }
+
+ if (sc->sc_txq.t_queued != old) {
+ /* packets have been queued, kick it off */
+ dwc_gmac_txdesc_sync(sc, start, sc->sc_txq.t_cur,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_TXPOLL, ~0U);
+#ifdef DWC_GMAC_DEBUG
+ dwc_dump_status(sc);
+#endif
+ }
+}
+
+void
+dwc_gmac_stop(struct ifnet *ifp, int disable)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+ ifq_clr_oactive(&ifp->if_snd);
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_OPMODE,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_OPMODE)
+ & ~(GMAC_DMA_OP_TXSTART|GMAC_DMA_OP_RXSTART));
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_OPMODE,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_OPMODE) | GMAC_DMA_OP_FLUSHTX);
+
+ mii_down(&sc->sc_mii);
+ dwc_gmac_reset_tx_ring(sc, &sc->sc_txq);
+ dwc_gmac_reset_rx_ring(sc, &sc->sc_rxq);
+}
+
+/*
+ * Add m0 to the TX ring
+ */
+int
+dwc_gmac_queue(struct dwc_gmac_softc *sc, struct mbuf *m0)
+{
+ struct dwc_gmac_dev_dmadesc *desc = NULL;
+ struct dwc_gmac_tx_data *data = NULL;
+ bus_dmamap_t map;
+ uint32_t flags, len, status;
+ int error, i, first;
+
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: dwc_gmac_queue: adding mbuf chain %p\n",
+ sc->sc_dev.dv_xname, m0);
+#endif
+
+ first = sc->sc_txq.t_cur;
+ map = sc->sc_txq.t_data[first].td_map;
+
+ error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m0,
+ BUS_DMA_WRITE|BUS_DMA_NOWAIT);
+ if (error != 0) {
+ printf("%s: could not map mbuf (len: %d, error %d)\n",
+ sc->sc_dev.dv_xname, m0->m_pkthdr.len, error);
+ return error;
+ }
+
+ if (sc->sc_txq.t_queued + map->dm_nsegs > DWGE_TX_RING_COUNT) {
+ bus_dmamap_unload(sc->sc_dmat, map);
+ return ENOBUFS;
+ }
+
+ flags = DDESC_CNTL_TXFIRST|DDESC_CNTL_TXCHAIN;
+ status = 0;
+ for (i = 0; i < map->dm_nsegs; i++) {
+ data = &sc->sc_txq.t_data[sc->sc_txq.t_cur];
+ desc = &sc->sc_txq.t_desc[sc->sc_txq.t_cur];
+
+ desc->ddesc_data = htole32(map->dm_segs[i].ds_addr);
+ len = (map->dm_segs[i].ds_len & DDESC_CNTL_SIZE1MASK)
+ << DDESC_CNTL_SIZE1SHIFT;
+
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: enqueing desc #%d data %08lx "
+ "len %lu (flags: %08x, len: %08x)\n",
+ sc->sc_dev.dv_xname, sc->sc_txq.t_cur,
+ (unsigned long)map->dm_segs[i].ds_addr,
+ (unsigned long)map->dm_segs[i].ds_len,
+ flags, len);
+#endif
+
+ desc->ddesc_cntl = htole32(len|flags);
+ flags &= ~DDESC_CNTL_TXFIRST;
+
+ /*
+ * Defer passing ownership of the first descriptor
+ * until we are done.
+ */
+ desc->ddesc_status = htole32(status);
+ status |= DDESC_STATUS_OWNEDBYDEV;
+
+ sc->sc_txq.t_queued++;
+ sc->sc_txq.t_cur = TX_NEXT(sc->sc_txq.t_cur);
+ }
+
+ desc->ddesc_cntl |= htole32(DDESC_CNTL_TXLAST|DDESC_CNTL_TXINT);
+
+ data->td_m = m0;
+ data->td_active = map;
+
+ bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
+ BUS_DMASYNC_PREWRITE);
+
+ /* Pass first to device */
+ sc->sc_txq.t_desc[first].ddesc_status =
+ htole32(DDESC_STATUS_OWNEDBYDEV);
+
+ return 0;
+}
+
+/*
+ * If the interface is up and running, only modify the receive
+ * filter when setting promiscuous or debug mode. Otherwise fall
+ * through to ether_ioctl, which will reset the chip.
+ */
+int
+dwc_gmac_ifflags_cb(struct arpcom *ac)
+{
+ struct ifnet *ifp = &ac->ac_if;
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ int change = ifp->if_flags ^ sc->sc_if_flags;
+
+ if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0)
+ return ENETRESET;
+ if ((change & IFF_PROMISC) != 0)
+ dwc_gmac_setmulti(sc);
+ return 0;
+}
+
+int
+dwc_gmac_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct dwc_gmac_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch(cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ if (!(ifp->if_flags & IFF_RUNNING))
+ dwc_gmac_init(ifp);
+ break;
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING)
+ error = ENETRESET;
+ else
+ dwc_gmac_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ dwc_gmac_stop(ifp, 0);
+ }
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
+ }
+
+ if (error == ENETRESET) {
+ if (ifp->if_flags & IFF_RUNNING)
+ dwc_gmac_ifflags_cb(&sc->sc_ac);
+ error = 0;
+ }
+
+ sc->sc_if_flags = sc->sc_ac.ac_if.if_flags;
+ splx(s);
+ return error;
+}
+
+void
+dwc_gmac_tx_intr(struct dwc_gmac_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct dwc_gmac_tx_data *data;
+ struct dwc_gmac_dev_dmadesc *desc;
+ uint32_t status;
+ int i, nsegs;
+
+ for (i = sc->sc_txq.t_next; sc->sc_txq.t_queued > 0; i = TX_NEXT(i)) {
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: dwc_gmac_tx_intr: checking desc #%d (t_queued: %d)\n",
+ sc->sc_dev.dv_xname, i, sc->sc_txq.t_queued);
+#endif
+
+ /*
+ * i+1 does not need to be a valid descriptor,
+ * this is just a special notion to just sync
+ * a single tx descriptor (i)
+ */
+ dwc_gmac_txdesc_sync(sc, i, i+1,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+
+ desc = &sc->sc_txq.t_desc[i];
+ status = le32toh(desc->ddesc_status);
+ if (status & DDESC_STATUS_OWNEDBYDEV)
+ break;
+
+ data = &sc->sc_txq.t_data[i];
+ if (data->td_m == NULL)
+ continue;
+
+ ifp->if_opackets++;
+ nsegs = data->td_active->dm_nsegs;
+ bus_dmamap_sync(sc->sc_dmat, data->td_active, 0,
+ data->td_active->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, data->td_active);
+
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: dwc_gmac_tx_intr: done with packet at desc #%d, "
+ "freeing mbuf %p\n", sc->sc_dev.dv_xname, i, data->td_m);
+#endif
+
+ m_freem(data->td_m);
+ data->td_m = NULL;
+
+ sc->sc_txq.t_queued -= nsegs;
+ }
+
+ sc->sc_txq.t_next = i;
+
+ if (sc->sc_txq.t_queued < DWGE_TX_RING_COUNT) {
+ ifq_clr_oactive(&ifp->if_snd);
+ }
+}
+
+void
+dwc_gmac_rx_intr(struct dwc_gmac_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct dwc_gmac_dev_dmadesc *desc;
+ struct dwc_gmac_rx_data *data;
+ bus_addr_t physaddr;
+ uint32_t status;
+ struct mbuf *m, *mnew;
+ int i, len, error;
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+
+ for (i = sc->sc_rxq.r_cur; ; i = RX_NEXT(i)) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ RX_DESC_OFFSET(i), sizeof(*desc),
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ desc = &sc->sc_rxq.r_desc[i];
+ data = &sc->sc_rxq.r_data[i];
+
+ status = le32toh(desc->ddesc_status);
+ if (status & DDESC_STATUS_OWNEDBYDEV)
+ break;
+
+ if (status & (DDESC_STATUS_RXERROR|DDESC_STATUS_RXTRUNCATED)) {
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: RX error: descriptor status %08x, skipping\n",
+ sc->sc_dev.dv_xname, status);
+#endif
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ len = (status >> DDESC_STATUS_FRMLENSHIFT)
+ & DDESC_STATUS_FRMLENMSK;
+
+#ifdef DWC_GMAC_DEBUG
+ printf("%s: rx int: device is done with descriptor #%d, len: %d\n",
+ sc->sc_dev.dv_xname, i, len);
+#endif
+
+ /*
+ * Try to get a new mbuf before passing this one
+ * up, if that fails, drop the packet and reuse
+ * the existing one.
+ */
+ MGETHDR(mnew, M_DONTWAIT, MT_DATA);
+ if (mnew == NULL) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+ MCLGET(mnew, M_DONTWAIT);
+ if ((mnew->m_flags & M_EXT) == 0) {
+ m_freem(mnew);
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ /* unload old DMA map */
+ bus_dmamap_sync(sc->sc_dmat, data->rd_map, 0,
+ data->rd_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->sc_dmat, data->rd_map);
+
+ /* and reload with new mbuf */
+ error = bus_dmamap_load(sc->sc_dmat, data->rd_map,
+ mtod(mnew, void*), MCLBYTES, NULL,
+ BUS_DMA_READ | BUS_DMA_NOWAIT);
+ if (error != 0) {
+ m_freem(mnew);
+ /* try to reload old mbuf */
+ error = bus_dmamap_load(sc->sc_dmat, data->rd_map,
+ mtod(data->rd_m, void*), MCLBYTES, NULL,
+ BUS_DMA_READ | BUS_DMA_NOWAIT);
+ if (error != 0) {
+ panic("%s: could not load old rx mbuf",
+ sc->sc_dev.dv_xname);
+ }
+ ifp->if_ierrors++;
+ goto skip;
+ }
+ physaddr = data->rd_map->dm_segs[0].ds_addr;
+
+ /*
+ * New mbuf loaded, update RX ring and continue
+ */
+ m = data->rd_m;
+ data->rd_m = mnew;
+ desc->ddesc_data = htole32(physaddr);
+
+ /* finalize mbuf */
+#ifdef __STRICT_ALIGNMENT
+ {
+ struct mbuf *m0;
+ m0 = m_devget(mtod(m, caddr_t), len, ETHER_ALIGN);
+ m_freem(m);
+ if (m0 == NULL) {
+ ifp->if_ierrors++;
+ goto skip;
+ }
+ m = m0;
+ }
+#else
+ m->m_pkthdr.len = m->m_len = len;
+#endif
+
+ ml_enqueue(&ml, m);
+
+skip:
+ bus_dmamap_sync(sc->sc_dmat, data->rd_map, 0,
+ data->rd_map->dm_mapsize, BUS_DMASYNC_PREREAD);
+ desc->ddesc_cntl = htole32(
+ ((DWGE_MAX_PACKET & DDESC_CNTL_SIZE1MASK)
+ << DDESC_CNTL_SIZE1SHIFT) | DDESC_CNTL_RXCHAIN);
+ desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV);
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map,
+ RX_DESC_OFFSET(i), sizeof(*desc),
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+
+ /* update RX pointer */
+ sc->sc_rxq.r_cur = i;
+
+ if_input(ifp, &ml);
+}
+
+/*
+ * Reverse order of bits - http://aggregate.org/MAGIC/#Bit%20Reversal
+ */
+static uint32_t
+bitrev32(uint32_t x)
+{
+ x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
+ x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
+ x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
+ x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
+
+ return (x >> 16) | (x << 16);
+}
+
+void
+dwc_gmac_setmulti(struct dwc_gmac_softc *sc)
+{
+ struct ifnet * const ifp = &sc->sc_ac.ac_if;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ uint32_t hashes[2] = { 0, 0 };
+ uint32_t ffilt, h;
+ int mcnt, s;
+
+ s = splnet();
+
+ ffilt = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ ffilt |= AWIN_GMAC_MAC_FFILT_PR;
+ goto special_filter;
+ }
+
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ ffilt &= ~(AWIN_GMAC_MAC_FFILT_PM|AWIN_GMAC_MAC_FFILT_PR);
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW, 0);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH, 0);
+
+ ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
+ mcnt = 0;
+ while (enm != NULL) {
+ if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+ ETHER_ADDR_LEN) != 0) {
+ ffilt |= AWIN_GMAC_MAC_FFILT_PM;
+ ifp->if_flags |= IFF_ALLMULTI;
+ goto special_filter;
+ }
+
+ h = bitrev32(
+ ~ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN)
+ ) >> 26;
+ hashes[h >> 5] |= (1 << (h & 0x1f));
+
+ mcnt++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (mcnt)
+ ffilt |= AWIN_GMAC_MAC_FFILT_HMC;
+ else
+ ffilt &= ~AWIN_GMAC_MAC_FFILT_HMC;
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT, ffilt);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW,
+ hashes[0]);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH,
+ hashes[1]);
+ sc->sc_if_flags = sc->sc_ac.ac_if.if_flags;
+
+ splx(s);
+
+#ifdef DWC_GMAC_DEBUG
+ dwc_gmac_dump_ffilt(sc, ffilt);
+#endif
+ return;
+
+special_filter:
+#ifdef DWC_GMAC_DEBUG
+ dwc_gmac_dump_ffilt(sc, ffilt);
+#endif
+ /* no MAC hashes, ALLMULTI or PROMISC */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT,
+ ffilt);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTLOW,
+ 0xffffffff);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_HTHIGH,
+ 0xffffffff);
+ sc->sc_if_flags = sc->sc_ac.ac_if.if_flags;
+ splx(s);
+}
+
+int
+dwc_gmac_intr(struct dwc_gmac_softc *sc)
+{
+ uint32_t status, dma_status;
+ int rv = 0;
+
+ status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_INTR);
+ if (status & AWIN_GMAC_MII_IRQ) {
+ (void)bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MII_STATUS);
+ rv = 1;
+ mii_pollstat(&sc->sc_mii);
+ }
+
+ dma_status = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_STATUS);
+
+ if (dma_status & (GMAC_DMA_INT_NIE|GMAC_DMA_INT_AIE))
+ rv = 1;
+
+ if (dma_status & GMAC_DMA_INT_TIE)
+ dwc_gmac_tx_intr(sc);
+
+ if (dma_status & GMAC_DMA_INT_RIE)
+ dwc_gmac_rx_intr(sc);
+
+ /*
+ * Check error conditions
+ */
+ if (dma_status & GMAC_DMA_INT_ERRORS) {
+ sc->sc_ac.ac_if.if_oerrors++;
+#ifdef DWC_GMAC_DEBUG
+ dwc_dump_and_abort(sc, "interrupt error condition");
+#endif
+ }
+
+ /* ack interrupt */
+ if (dma_status)
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_STATUS, dma_status & GMAC_DMA_INT_MASK);
+
+ /*
+ * Get more packets
+ */
+ if (rv)
+ sc->sc_ac.ac_if.if_start(&sc->sc_ac.ac_if);
+
+ return rv;
+}
+
+#ifdef DWC_GMAC_DEBUG
+void
+dwc_gmac_dump_dma(struct dwc_gmac_softc *sc)
+{
+ printf("%s: busmode: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE));
+ printf("%s: tx poll: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_TXPOLL));
+ printf("%s: rx poll: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RXPOLL));
+ printf("%s: rx descriptors: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR));
+ printf("%s: tx descriptors: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_TX_ADDR));
+ printf("%s: status: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_STATUS));
+ printf("%s: op mode: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_OPMODE));
+ printf("%s: int enable: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_INTENABLE));
+ printf("%s: cur tx: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_CUR_TX_DESC));
+ printf("%s: cur rx: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_CUR_RX_DESC));
+ printf("%s: cur tx buffer: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_CUR_TX_BUFADDR));
+ printf("%s: cur rx buffer: %08x\n", sc->sc_dev.dv_xname,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_CUR_RX_BUFADDR));
+}
+
+void
+dwc_gmac_dump_tx_desc(struct dwc_gmac_softc *sc)
+{
+ int i;
+
+ printf("%s: TX queue: cur=%d, next=%d, queued=%d\n",
+ sc->sc_dev.dv_xname, sc->sc_txq.t_cur,
+ sc->sc_txq.t_next, sc->sc_txq.t_queued);
+ printf("%s: TX DMA descriptors:\n", sc->sc_dev.dv_xname);
+ for (i = 0; i < DWGE_TX_RING_COUNT; i++) {
+ struct dwc_gmac_dev_dmadesc *desc = &sc->sc_txq.t_desc[i];
+ printf("#%d (%08lx): status: %08x cntl: %08x "
+ "data: %08x next: %08x\n",
+ i, sc->sc_txq.t_physaddr +
+ i*sizeof(struct dwc_gmac_dev_dmadesc),
+ le32toh(desc->ddesc_status), le32toh(desc->ddesc_cntl),
+ le32toh(desc->ddesc_data), le32toh(desc->ddesc_next));
+ }
+}
+
+void
+dwc_gmac_dump_rx_desc(struct dwc_gmac_softc *sc)
+{
+ int i;
+
+ printf("%s: RX queue: cur=%d, next=%d\n", sc->sc_dev.dv_xname,
+ sc->sc_rxq.r_cur, sc->sc_rxq.r_next);
+ printf("%s: RX DMA descriptors:\n", sc->sc_dev.dv_xname);
+ for (i = 0; i < DWGE_RX_RING_COUNT; i++) {
+ struct dwc_gmac_dev_dmadesc *desc = &sc->sc_rxq.r_desc[i];
+ printf("#%d (%08lx): status: %08x cntl: %08x "
+ "data: %08x next: %08x\n",
+ i, sc->sc_rxq.r_physaddr +
+ i*sizeof(struct dwc_gmac_dev_dmadesc),
+ le32toh(desc->ddesc_status), le32toh(desc->ddesc_cntl),
+ le32toh(desc->ddesc_data), le32toh(desc->ddesc_next));
+ }
+}
+
+void
+dwc_dump_status(struct dwc_gmac_softc *sc)
+{
+ uint32_t status = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_MAC_INTR);
+ uint32_t dma_status = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+ AWIN_GMAC_DMA_STATUS);
+ char buf[200];
+
+ /* print interrupt state */
+ snprintb(buf, sizeof(buf), "\177\20"
+ "b\x10""NI\0"
+ "b\x0f""AI\0"
+ "b\x0e""ER\0"
+ "b\x0d""FB\0"
+ "b\x0a""ET\0"
+ "b\x09""RW\0"
+ "b\x08""RS\0"
+ "b\x07""RU\0"
+ "b\x06""RI\0"
+ "b\x05""UN\0"
+ "b\x04""OV\0"
+ "b\x03""TJ\0"
+ "b\x02""TU\0"
+ "b\x01""TS\0"
+ "b\x00""TI\0"
+ "\0", dma_status);
+ printf("%s: INTR status: %08x, DMA status: %s\n",
+ sc->sc_dev.dv_xname, status, buf);
+}
+
+void
+dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg)
+{
+ dwc_dump_status(sc);
+ dwc_gmac_dump_ffilt(sc,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT));
+ dwc_gmac_dump_dma(sc);
+ dwc_gmac_dump_tx_desc(sc);
+ dwc_gmac_dump_rx_desc(sc);
+
+ panic("%s", msg);
+}
+
+void dwc_gmac_dump_ffilt(struct dwc_gmac_softc *sc, uint32_t ffilt)
+{
+ char buf[200];
+
+ /* print filter setup */
+ snprintb(buf, sizeof(buf), "\177\20"
+ "b\x1f""RA\0"
+ "b\x0a""HPF\0"
+ "b\x09""SAF\0"
+ "b\x08""SAIF\0"
+ "b\x05""DBF\0"
+ "b\x04""PM\0"
+ "b\x03""DAIF\0"
+ "b\x02""HMC\0"
+ "b\x01""HUC\0"
+ "b\x00""PR\0"
+ "\0", ffilt);
+ printf("%s: FFILT: %s\n", sc->sc_dev.dv_xname, buf);
+}
+#endif
diff --git a/sys/dev/ic/dwc_gmac_reg.h b/sys/dev/ic/dwc_gmac_reg.h
new file mode 100644
index 00000000000..ed8c139bc1a
--- /dev/null
+++ b/sys/dev/ic/dwc_gmac_reg.h
@@ -0,0 +1,227 @@
+/* $OpenBSD: dwc_gmac_reg.h,v 1.1 2016/08/13 20:35:57 kettenis Exp $ */
+/* $NetBSD: dwc_gmac_reg.h,v 1.15 2015/11/21 16:04:11 martin Exp $ */
+
+/*-
+ * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry and Martin Husemann.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#define AWIN_GMAC_MAC_CONF 0x0000
+#define AWIN_GMAC_MAC_FFILT 0x0004
+#define AWIN_GMAC_MAC_HTHIGH 0x0008
+#define AWIN_GMAC_MAC_HTLOW 0x000c
+#define AWIN_GMAC_MAC_MIIADDR 0x0010
+#define AWIN_GMAC_MAC_MIIDATA 0x0014
+#define AWIN_GMAC_MAC_FLOWCTRL 0x0018
+#define AWIN_GMAC_MAC_VLANTAG 0x001c
+#define AWIN_GMAC_MAC_VERSION 0x0020 /* not always implemented? */
+#define AWIN_GMAC_MAC_INTR 0x0038
+#define AWIN_GMAC_MAC_INTMASK 0x003c
+#define AWIN_GMAC_MAC_ADDR0HI 0x0040
+#define AWIN_GMAC_MAC_ADDR0LO 0x0044
+#define AWIN_GMAC_MII_STATUS 0x00D8
+
+#define AWIN_GMAC_MAC_CONF_DISABLEJABBER (1 << 22) /* jabber disable */
+#define AWIN_GMAC_MAC_CONF_FRAMEBURST (1 << 21) /* allow TX frameburst when
+ in half duplex mode */
+#define AWIN_GMAC_MAC_CONF_MIISEL (1 << 15) /* select MII phy */
+#define AWIN_GMAC_MAC_CONF_FES100 (1 << 14) /* 100 mbit mode */
+#define AWIN_GMAC_MAC_CONF_DISABLERXOWN (1 << 13) /* do not receive our own
+ TX frames in half duplex
+ mode */
+#define AWIN_GMAC_MAC_CONF_FULLDPLX (1 << 11) /* select full duplex */
+#define AWIN_GMAC_MAC_CONF_ACS (1 << 7) /* auto pad/CRC stripping */
+#define AWIN_GMAC_MAC_CONF_TXENABLE (1 << 3) /* enable TX dma engine */
+#define AWIN_GMAC_MAC_CONF_RXENABLE (1 << 2) /* enable RX dma engine */
+
+#define AWIN_GMAC_MAC_FFILT_RA (1U << 31) /* receive all mode */
+#define AWIN_GMAC_MAC_FFILT_HPF (1 << 10) /* hash or perfect filter */
+#define AWIN_GMAC_MAC_FFILT_SAF (1 << 9) /* source address filter */
+#define AWIN_GMAC_MAC_FFILT_SAIF (1 << 8) /* inverse filtering */
+#define AWIN_GMAC_MAC_FFILT_DBF (1 << 5) /* disable broadcast frames */
+#define AWIN_GMAC_MAC_FFILT_PM (1 << 4) /* promiscious multicast */
+#define AWIN_GMAC_MAC_FFILT_DAIF (1 << 3) /* DA inverse filtering */
+#define AWIN_GMAC_MAC_FFILT_HMC (1 << 2) /* multicast hash compare */
+#define AWIN_GMAC_MAC_FFILT_HUC (1 << 1) /* unicast hash compare */
+#define AWIN_GMAC_MAC_FFILT_PR (1 << 0) /* promiscious mode */
+
+#define AWIN_GMAC_MAC_INT_LPI (1 << 10)
+#define AWIN_GMAC_MAC_INT_TSI (1 << 9)
+#define AWIN_GMAC_MAC_INT_ANEG (1 << 2)
+#define AWIN_GMAC_MAC_INT_LINKCHG (1 << 1)
+#define AWIN_GMAC_MAC_INT_RGSMII (1 << 0)
+
+#define AWIN_GMAC_MAC_FLOWCTRL_PAUSE_SHIFT 16
+#define AWIN_GMAC_MAC_FLOWCTRL_PAUSE_MASK 0xffff
+#define AWIN_GMAC_MAC_FLOWCTRL_RFE (1 << 2)
+#define AWIN_GMAC_MAC_FLOWCTRL_TFE (1 << 1)
+#define AWIN_GMAC_MAC_FLOWCTRL_BUSY (1 << 0)
+
+#define AWIN_GMAC_DMA_BUSMODE 0x1000
+#define AWIN_GMAC_DMA_TXPOLL 0x1004
+#define AWIN_GMAC_DMA_RXPOLL 0x1008
+#define AWIN_GMAC_DMA_RX_ADDR 0x100c
+#define AWIN_GMAC_DMA_TX_ADDR 0x1010
+#define AWIN_GMAC_DMA_STATUS 0x1014
+#define AWIN_GMAC_DMA_OPMODE 0x1018
+#define AWIN_GMAC_DMA_INTENABLE 0x101c
+#define AWIN_GMAC_DMA_CUR_TX_DESC 0x1048
+#define AWIN_GMAC_DMA_CUR_RX_DESC 0x104c
+#define AWIN_GMAC_DMA_CUR_TX_BUFADDR 0x1050
+#define AWIN_GMAC_DMA_CUR_RX_BUFADDR 0x1054
+#define AWIN_GMAC_DMA_HWFEATURES 0x1058 /* not always implemented? */
+
+#define GMAC_MII_PHY_SHIFT 11
+#define GMAC_MII_PHY_MASK 0x1f
+#define GMAC_MII_REG_SHIFT 6
+#define GMAC_MII_REG_MASK 0x1f
+
+#define GMAC_MII_BUSY (1 << 0)
+#define GMAC_MII_WRITE (1 << 1)
+#define GMAC_MII_CLK_60_100M_DIV42 0x0
+#define GMAC_MII_CLK_100_150M_DIV62 0x1
+#define GMAC_MII_CLK_25_35M_DIV16 0x2
+#define GMAC_MII_CLK_35_60M_DIV26 0x3
+#define GMAC_MII_CLK_150_250M_DIV102 0x4
+#define GMAC_MII_CLK_250_300M_DIV124 0x5
+#define GMAC_MII_CLK_DIV4 0x8
+#define GMAC_MII_CLK_DIV6 0x9
+#define GMAC_MII_CLK_DIV8 0xa
+#define GMAC_MII_CLK_DIV10 0xb
+#define GMAC_MII_CLK_DIV12 0xc
+#define GMAC_MII_CLK_DIV14 0xd
+#define GMAC_MII_CLK_DIV16 0xe
+#define GMAC_MII_CLK_DIV18 0xf
+#define GMAC_MII_CLKMASK_SHIFT 2
+#define GMAC_MII_CLKMASK_MASK 0xf
+
+#define GMAC_BUSMODE_4PBL (1 << 24)
+#define GMAC_BUSMODE_RPBL_SHIFT 17
+#define GMAC_BUSMODE_RPBL_MASK 0x3f
+#define GMAC_BUSMODE_FIXEDBURST (1 << 16)
+#define GMAC_BUSMODE_PRIORXTX_SHIFT 14
+#define GMAC_BUSMODE_PRIORXTX_MASK 0x3
+#define GMAC_BUSMODE_PRIORXTX_41 3
+#define GMAC_BUSMODE_PRIORXTX_31 2
+#define GMAC_BUSMODE_PRIORXTX_21 1
+#define GMAC_BUSMODE_PRIORXTX_11 0
+#define GMAC_BUSMODE_PBL_SHIFT 8
+#define GMAC_BUSMODE_PBL_MASK 0x3f /* possible DMA
+ burst len */
+#define GMAC_BUSMODE_RESET (1 << 0)
+
+#define AWIN_GMAC_MII_IRQ (1 << 0)
+
+
+#define GMAC_DMA_OP_DISABLECSDROP (1 << 26) /* disable dropping of
+ frames with TCP/IP
+ checksum errors */
+#define GMAC_DMA_OP_RXSTOREFORWARD (1 << 25) /* start RX when a
+ full frame is available */
+#define GMAC_DMA_OP_DISABLERXFLUSH (1 << 24) /* Do not drop frames
+ when out of RX descr. */
+#define GMAC_DMA_OP_TXSTOREFORWARD (1 << 21) /* start TX when a
+ full frame is available */
+#define GMAC_DMA_OP_FLUSHTX (1 << 20) /* flush TX fifo */
+#define GMAC_DMA_OP_TXSTART (1 << 13) /* start TX DMA engine */
+#define GMAC_DMA_OP_RXSTART (1 << 1) /* start RX DMA engine */
+
+#define GMAC_DMA_INT_NIE (1 << 16) /* Normal/Summary */
+#define GMAC_DMA_INT_AIE (1 << 15) /* Abnormal/Summary */
+#define GMAC_DMA_INT_ERE (1 << 14) /* Early receive */
+#define GMAC_DMA_INT_FBE (1 << 13) /* Fatal bus error */
+#define GMAC_DMA_INT_ETE (1 << 10) /* Early transmit */
+#define GMAC_DMA_INT_RWE (1 << 9) /* Receive watchdog */
+#define GMAC_DMA_INT_RSE (1 << 8) /* Receive stopped */
+#define GMAC_DMA_INT_RUE (1 << 7) /* Receive buffer unavail. */
+#define GMAC_DMA_INT_RIE (1 << 6) /* Receive interrupt */
+#define GMAC_DMA_INT_UNE (1 << 5) /* Tx underflow */
+#define GMAC_DMA_INT_OVE (1 << 4) /* Receive overflow */
+#define GMAC_DMA_INT_TJE (1 << 3) /* Transmit jabber */
+#define GMAC_DMA_INT_TUE (1 << 2) /* Transmit buffer unavail. */
+#define GMAC_DMA_INT_TSE (1 << 1) /* Transmit stopped */
+#define GMAC_DMA_INT_TIE (1 << 0) /* Transmit interrupt */
+
+#define GMAC_DMA_INT_MASK 0x1ffff /* all possible intr bits */
+
+struct dwc_gmac_dev_dmadesc {
+ uint32_t ddesc_status;
+/* both: */
+#define DDESC_STATUS_OWNEDBYDEV (1U << 31)
+
+/* for RX descriptors */
+#define DDESC_STATUS_DAFILTERFAIL (1 << 30)
+#define DDESC_STATUS_FRMLENMSK 0x3fff
+#define DDESC_STATUS_FRMLENSHIFT 16
+#define DDESC_STATUS_RXERROR (1 << 15)
+#define DDESC_STATUS_RXTRUNCATED (1 << 14)
+#define DDESC_STATUS_SAFILTERFAIL (1 << 13)
+#define DDESC_STATUS_RXIPC_GIANTFRAME (1 << 12)
+#define DDESC_STATUS_RXDAMAGED (1 << 11)
+#define DDESC_STATUS_RXVLANTAG (1 << 10)
+#define DDESC_STATUS_RXFIRST (1 << 9)
+#define DDESC_STATUS_RXLAST (1 << 8)
+#define DDESC_STATUS_RXIPC_GIANT (1 << 7)
+#define DDESC_STATUS_RXCOLLISION (1 << 6)
+#define DDESC_STATUS_RXFRAMEETHER (1 << 5)
+#define DDESC_STATUS_RXWATCHDOG (1 << 4)
+#define DDESC_STATUS_RXMIIERROR (1 << 3)
+#define DDESC_STATUS_RXDRIBBLING (1 << 2)
+#define DDESC_STATUS_RXCRC (1 << 1)
+
+ uint32_t ddesc_cntl;
+
+/* for TX descriptors */
+#define DDESC_CNTL_TXINT (1U << 31)
+#define DDESC_CNTL_TXLAST (1 << 30)
+#define DDESC_CNTL_TXFIRST (1 << 29)
+#define DDESC_CNTL_TXCHECKINSCTRL __BITS(27,28)
+
+#define DDESC_TXCHECK_DISABLED 0
+#define DDESC_TXCHECK_IP 1
+#define DDESC_TXCHECK_IP_NO_PSE 2
+#define DDESC_TXCHECK_FULL 3
+
+#define DDESC_CNTL_TXCRCDIS (1 << 26)
+#define DDESC_CNTL_TXRINGEND (1 << 25)
+#define DDESC_CNTL_TXCHAIN (1 << 24)
+#define DDESC_CNTL_TXDISPAD (1 << 23)
+
+/* for RX descriptors */
+#define DDESC_CNTL_RXINTDIS (1U << 31)
+#define DDESC_CNTL_RXRINGEND (1 << 25)
+#define DDESC_CNTL_RXCHAIN (1 << 24)
+
+/* both */
+#define DDESC_CNTL_SIZE1MASK 0x7ff
+#define DDESC_CNTL_SIZE1SHIFT 0
+#define DDESC_CNTL_SIZE2MASK 0x7ff
+#define DDESC_CNTL_SIZE2SHIFT 11
+
+ uint32_t ddesc_data; /* pointer to buffer data */
+ uint32_t ddesc_next; /* link to next descriptor */
+};
diff --git a/sys/dev/ic/dwc_gmac_var.h b/sys/dev/ic/dwc_gmac_var.h
new file mode 100644
index 00000000000..e2b2d2887ca
--- /dev/null
+++ b/sys/dev/ic/dwc_gmac_var.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: dwc_gmac_var.h,v 1.1 2016/08/13 20:35:57 kettenis Exp $ */
+/* $NetBSD: dwc_gmac_var.h,v 1.6 2014/11/22 18:31:03 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry and Martin Husemann.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+
+/*
+ * We could use 1024 DMA descriptors to fill up an 8k page (each is 16 byte).
+ * However, on TX we probably will not need that many, and on RX we allocate
+ * a full mbuf cluster for each, so secondary memory consumption will grow
+ * rapidly.
+ * So currently we waste half a page of dma memory and consume 512k Byte of
+ * RAM for mbuf clusters.
+ * XXX Maybe fine-tune later, or reconsider unsharing of RX/TX dmamap.
+ */
+#define DWGE_RX_RING_COUNT 256
+#define DWGE_TX_RING_COUNT 256
+#define DWGE_TOTAL_RING_COUNT \
+ (DWGE_RX_RING_COUNT + DWGE_TX_RING_COUNT)
+
+#define DWGE_MAX_PACKET 0x7ff
+
+
+
+struct dwc_gmac_rx_data {
+ bus_dmamap_t rd_map;
+ struct mbuf *rd_m;
+};
+
+struct dwc_gmac_tx_data {
+ bus_dmamap_t td_map;
+ bus_dmamap_t td_active;
+ struct mbuf *td_m;
+};
+
+struct dwc_gmac_tx_ring {
+ bus_addr_t t_physaddr; /* PA of TX ring start */
+ struct dwc_gmac_dev_dmadesc *t_desc; /* VA of TX ring start */
+ struct dwc_gmac_tx_data t_data[DWGE_TX_RING_COUNT];
+ int t_cur, t_next, t_queued;
+};
+
+struct dwc_gmac_rx_ring {
+ bus_addr_t r_physaddr; /* PA of RX ring start */
+ struct dwc_gmac_dev_dmadesc *r_desc; /* VA of RX ring start */
+ struct dwc_gmac_rx_data r_data[DWGE_RX_RING_COUNT];
+ int r_cur, r_next;
+ struct mutex r_mtx;
+};
+
+struct dwc_gmac_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ bus_dma_tag_t sc_dmat;
+ struct arpcom sc_ac;
+ struct mii_data sc_mii;
+ struct mutex sc_mdio_lock;
+ bus_dmamap_t sc_dma_ring_map; /* common dma memory for RX */
+ bus_dma_segment_t sc_dma_ring_seg; /* and TX ring */
+ struct dwc_gmac_rx_ring sc_rxq;
+ struct dwc_gmac_tx_ring sc_txq;
+ short sc_if_flags; /* shadow of ether flags */
+ uint16_t sc_mii_clk;
+};
+
+void dwc_gmac_attach(struct dwc_gmac_softc*, uint32_t /*mii_clk*/);
+int dwc_gmac_intr(struct dwc_gmac_softc*);