Module Name: src Committed By: martin Date: Mon Sep 8 14:24:32 UTC 2014
Added Files: src/sys/dev/ic: dwc_gmac.c dwc_gmac_reg.h dwc_gmac_var.h Log Message: Add work-in-progress driver for the Designware GMAC core, found on some allwinner chips. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/dwc_gmac.c \ src/sys/dev/ic/dwc_gmac_reg.h src/sys/dev/ic/dwc_gmac_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/sys/dev/ic/dwc_gmac.c diff -u /dev/null src/sys/dev/ic/dwc_gmac.c:1.1 --- /dev/null Mon Sep 8 14:24:32 2014 +++ src/sys/dev/ic/dwc_gmac.c Mon Sep 8 14:24:32 2014 @@ -0,0 +1,811 @@ +/*- + * 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 + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.1 2014/09/08 14:24:32 martin Exp $"); + +#include "opt_inet.h" + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/sockio.h> + +#include <net/if.h> +#include <net/if_ether.h> +#include <net/if_media.h> +#include <net/bpf.h> +#ifdef INET +#include <netinet/if_inarp.h> +#endif + +#include <dev/mii/miivar.h> + +#include <dev/ic/dwc_gmac_reg.h> +#include <dev/ic/dwc_gmac_var.h> + +static int dwc_gmac_miibus_read_reg(device_t, int, int); +static void dwc_gmac_miibus_write_reg(device_t, int, int, int); +static void dwc_gmac_miibus_statchg(struct ifnet *); + +static int dwc_gmac_reset(struct dwc_gmac_softc *sc); +static void dwc_gmac_write_hwaddr(struct dwc_gmac_softc *sc, + uint8_t enaddr[ETHER_ADDR_LEN]); +static int dwc_gmac_alloc_dma_rings(struct dwc_gmac_softc *sc); +static void dwc_gmac_free_dma_rings(struct dwc_gmac_softc *sc); +static int dwc_gmac_alloc_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *); +static void dwc_gmac_reset_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *); +static void dwc_gmac_free_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *); +static int dwc_gmac_alloc_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *); +static void dwc_gmac_reset_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *); +static void dwc_gmac_free_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *); +static void dwc_gmac_txdesc_sync(struct dwc_gmac_softc *sc, int start, int end, int ops); +static int dwc_gmac_init(struct ifnet *ifp); +static void dwc_gmac_stop(struct ifnet *ifp, int disable); +static void dwc_gmac_start(struct ifnet *ifp); +static int dwc_gmac_queue(struct dwc_gmac_softc *sc, struct mbuf *m0); +static int dwc_gmac_ioctl(struct ifnet *, u_long, void *); + + +#define TX_DESC_OFFSET(N) ((AWGE_RX_RING_COUNT+(N)) \ + *sizeof(struct dwc_gmac_dev_dmadesc)) + +#define RX_DESC_OFFSET(N) ((N)*sizeof(struct dwc_gmac_dev_dmadesc)) + +void +dwc_gmac_attach(struct dwc_gmac_softc *sc, uint8_t *ep) +{ + uint8_t enaddr[ETHER_ADDR_LEN]; + uint32_t maclo, machi; + struct mii_data * const mii = &sc->sc_mii; + struct ifnet * const ifp = &sc->sc_ec.ec_if; + + mutex_init(&sc->sc_mdio_lock, MUTEX_DEFAULT, IPL_NET); + + /* + * If the frontend did not pass in a pre-configured ethernet mac + * address, try to read on from the current filter setup, + * before resetting the chip. + */ + if (ep == NULL) { + 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); + 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; + ep = enaddr; + } + + /* + * Init chip and do intial setup + */ + if (dwc_gmac_reset(sc) != 0) + return; /* not much to cleanup, haven't attached yet */ + dwc_gmac_write_hwaddr(sc, ep); + aprint_normal_dev(sc->sc_dev, "Ethernet address: %s\n", + ether_sprintf(enaddr)); + + /* + * Allocate Tx and Rx rings + */ + if (dwc_gmac_alloc_dma_rings(sc) != 0) { + aprint_error_dev(sc->sc_dev, "could not allocate DMA rings\n"); + goto fail; + } + + if (dwc_gmac_alloc_tx_ring(sc, &sc->sc_txq) != 0) { + aprint_error_dev(sc->sc_dev, "could not allocate Tx ring\n"); + goto fail; + } + + mutex_init(&sc->sc_rxq.r_mtx, MUTEX_DEFAULT, IPL_NET); + if (dwc_gmac_alloc_rx_ring(sc, &sc->sc_rxq) != 0) { + aprint_error_dev(sc->sc_dev, "could not allocate Rx ring\n"); + goto fail; + } + + /* + * Prepare interface data + */ + ifp->if_softc = sc; + strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = dwc_gmac_ioctl; + ifp->if_start = dwc_gmac_start; + ifp->if_init = dwc_gmac_init; + ifp->if_stop = dwc_gmac_stop; + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + IFQ_SET_READY(&ifp->if_snd); + + /* + * Attach MII subdevices + */ + ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus); + 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; + mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); + + if (LIST_EMPTY(&mii->mii_phys)) { + aprint_error_dev(sc->sc_dev, "no PHY found!\n"); + 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, enaddr); + + /* + * Enable interrupts + */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_INTR, AWIN_DEF_MAC_INTRMASK); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_INTENABLE, GMAC_DEF_DMA_INT_MASK); + + return; + +fail: + dwc_gmac_free_rx_ring(sc, &sc->sc_rxq); + dwc_gmac_free_tx_ring(sc, &sc->sc_txq); +} + + + +static 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); + } + + aprint_error_dev(sc->sc_dev, "reset timed out\n"); + return EIO; +} + +static void +dwc_gmac_write_hwaddr(struct dwc_gmac_softc *sc, + uint8_t enaddr[ETHER_ADDR_LEN]) +{ + 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); +} + +static int +dwc_gmac_miibus_read_reg(device_t self, int phy, int reg) +{ + struct dwc_gmac_softc * const sc = device_private(self); + uint16_t miiaddr; + size_t cnt; + int rv = 0; + + miiaddr = ((phy << GMAC_MII_PHY_SHIFT) & GMAC_MII_PHY_MASK) + | ((reg << GMAC_MII_REG_SHIFT) & GMAC_MII_REG_MASK); + + mutex_enter(&sc->sc_mdio_lock); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR, miiaddr + | GMAC_MII_CLK_150_250M | GMAC_MII_BUSY); + + 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); + } + + mutex_exit(&sc->sc_mdio_lock); + + return rv; +} + +static void +dwc_gmac_miibus_write_reg(device_t self, int phy, int reg, int val) +{ + struct dwc_gmac_softc * const sc = device_private(self); + uint16_t miiaddr; + size_t cnt; + + miiaddr = ((phy << GMAC_MII_PHY_SHIFT) & GMAC_MII_PHY_MASK) + | ((reg << GMAC_MII_REG_SHIFT) & GMAC_MII_REG_MASK) + | GMAC_MII_BUSY | GMAC_MII_WRITE; + + mutex_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, miiaddr); + + 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); + } + + mutex_exit(&sc->sc_mdio_lock); +} + +static 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 = + AWGE_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 < AWGE_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) { + aprint_error_dev(sc->sc_dev, + "could not allocate rx mbuf #%d\n", 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) { + aprint_error_dev(sc->sc_dev, + "could not create DMA map\n"); + data->rd_map = NULL; + goto fail; + } + MCLGET(data->rd_m, M_DONTWAIT); + if (!(data->rd_m->m_flags & M_EXT)) { + aprint_error_dev(sc->sc_dev, + "could not allocate mbuf cluster #%d\n", 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) { + aprint_error_dev(sc->sc_dev, + "could not load rx buf DMA map #%d", 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 = i < (AWGE_RX_RING_COUNT-1) ? i+1 : 0; + desc->ddesc_next = htole32(ring->r_physaddr + + next * sizeof(*desc)); + desc->ddesc_cntl = htole32( + (AWGE_MAX_PACKET & DDESC_CNTL_SIZE1MASK) + << DDESC_CNTL_SIZE1SHIFT); + desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV); + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, 0, + AWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc), + BUS_DMASYNC_PREREAD); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR, + htole32(ring->r_physaddr)); + + return 0; + +fail: + dwc_gmac_free_rx_ring(sc, ring); + return error; +} + +static 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 < AWGE_RX_RING_COUNT; i++) { + desc = &sc->sc_rxq.r_desc[i]; + desc->ddesc_cntl = htole32( + (AWGE_MAX_PACKET & DDESC_CNTL_SIZE1MASK) + << DDESC_CNTL_SIZE1SHIFT); + desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV); + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, 0, + AWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc), + BUS_DMASYNC_PREWRITE); + + ring->r_cur = ring->r_next = 0; +} + +static int +dwc_gmac_alloc_dma_rings(struct dwc_gmac_softc *sc) +{ + const size_t descsize = AWGE_TOTAL_RING_COUNT * + sizeof(struct dwc_gmac_dev_dmadesc); + int error, nsegs; + void *rings; + + error = bus_dmamap_create(sc->sc_dmat, descsize, 1, descsize, 0, + BUS_DMA_NOWAIT, &sc->sc_dma_ring_map); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not create desc DMA map\n"); + 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) { + aprint_error_dev(sc->sc_dev, + "could not map DMA memory\n"); + 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) { + aprint_error_dev(sc->sc_dev, + "could not allocate DMA memory\n"); + 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) { + aprint_error_dev(sc->sc_dev, + "could not load desc DMA map\n"); + goto fail; + } + + /* give first AWGE_RX_RING_COUNT to the RX side */ + sc->sc_rxq.r_desc = 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 + AWGE_RX_RING_COUNT; + sc->sc_txq.t_physaddr = sc->sc_rxq.r_physaddr + + AWGE_RX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc); + + return 0; + +fail: + dwc_gmac_free_dma_rings(sc); + return error; +} + +static 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, sc->sc_rxq.r_desc, + AWGE_TOTAL_RING_COUNT * sizeof(struct dwc_gmac_dev_dmadesc)); + bus_dmamem_free(sc->sc_dmat, &sc->sc_dma_ring_seg, 1); +} + +static 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 < AWGE_RX_RING_COUNT; i++) { + data = &ring->r_data[i]; + + if (data->rd_map != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->rd_map, 0, + AWGE_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); + } +} + +static 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, AWGE_TX_RING_COUNT*sizeof(*ring->t_desc)); + bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_ring_map, + TX_DESC_OFFSET(0), + AWGE_TX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc), + BUS_DMASYNC_POSTWRITE); + + for (i = 0; i < AWGE_TX_RING_COUNT; i++) { + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + AWGE_TX_RING_COUNT, MCLBYTES, 0, + BUS_DMA_NOWAIT|BUS_DMA_COHERENT, + &ring->t_data[i].td_map); + if (error != 0) { + aprint_error_dev(sc->sc_dev, + "could not create TX DMA map #%d\n", 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) + *((i+1)&AWGE_TX_RING_COUNT)); + } + + return 0; + +fail: + dwc_gmac_free_tx_ring(sc, ring); + return error; +} + +static 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(AWGE_TX_RING_COUNT+1)-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); +} + +static void +dwc_gmac_reset_tx_ring(struct dwc_gmac_softc *sc, + struct dwc_gmac_tx_ring *ring) +{ + int i; + + for (i = 0; i < AWGE_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), + AWGE_TX_RING_COUNT*sizeof(struct dwc_gmac_dev_dmadesc), + BUS_DMASYNC_PREWRITE); + + ring->t_queued = 0; + ring->t_cur = ring->t_next = 0; +} + +static 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 < AWGE_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 < AWGE_TX_RING_COUNT; i++) { + struct dwc_gmac_tx_data *data = &ring->t_data[i]; + + bus_dmamap_destroy(sc->sc_dmat, data->td_map); + } +} + +static void +dwc_gmac_miibus_statchg(struct ifnet *ifp) +{ + struct dwc_gmac_softc * const sc = ifp->if_softc; + struct mii_data * const mii = &sc->sc_mii; + +printf("dwc_gmac_miibus_statchg called\n"); + + /* + * Set MII or GMII interface based on the speed + * negotiated by the PHY. + */ + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + /* XXX */ + break; + case IFM_1000_T: + /* XXX */ + break; + } +} + +static int +dwc_gmac_init(struct ifnet *ifp) +{ + struct dwc_gmac_softc *sc = ifp->if_softc; + + if (ifp->if_flags & IFF_RUNNING) + return 0; + + dwc_gmac_stop(ifp, 0); + + /* + * Set up dma pointer for RX ring + */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR, sc->sc_rxq.r_physaddr); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + return 0; +} + +static void +dwc_gmac_start(struct ifnet *ifp) +{ + struct dwc_gmac_softc *sc = ifp->if_softc; + int old = sc->sc_txq.t_queued; + struct mbuf *m0; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (dwc_gmac_queue(sc, m0) != 0) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m0); + bpf_mtap(ifp, m0); + } + + if (sc->sc_txq.t_queued != old) { + /* packets have been queued, kick it off */ + dwc_gmac_txdesc_sync(sc, old, sc->sc_txq.t_cur, + 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 + + old*sizeof(struct dwc_gmac_dev_dmadesc)); + } +} + +static void +dwc_gmac_stop(struct ifnet *ifp, int disable) +{ + struct dwc_gmac_softc *sc = ifp->if_softc; + + 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 + */ +static 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 status, flags, len; + int error, i, first; + + first = sc->sc_txq.t_cur; + map = sc->sc_txq.t_data[first].td_map; + flags = 0; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m0, + BUS_DMA_WRITE|BUS_DMA_NOWAIT); + if (error != 0) { + aprint_error_dev(sc->sc_dev, "could not map mbuf " + "(len: %d, error %d)\n", m0->m_pkthdr.len, error); + return error; + } + + if (sc->sc_txq.t_queued + map->dm_nsegs >= AWGE_TX_RING_COUNT - 1) { + bus_dmamap_unload(sc->sc_dmat, map); + return ENOBUFS; + } + + data = NULL; + flags = DDESC_STATUS_TXINT|DDESC_STATUS_TXCHAIN; + 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; + desc->ddesc_cntl = htole32(len); + status = flags; + desc->ddesc_status = htole32(status); + sc->sc_txq.t_queued++; + + /* + * Defer passing ownership of the first descriptor + * untill we are done. + */ + flags |= DDESC_STATUS_OWNEDBYDEV; + + sc->sc_txq.t_cur = (sc->sc_txq.t_cur + 1) + & (AWGE_TX_RING_COUNT-1); + } + + /* Fixup last */ + status = flags|DDESC_STATUS_TXLAST; + desc->ddesc_status = htole32(status); + + /* Finalize first */ + status = flags|DDESC_STATUS_TXFIRST; + sc->sc_txq.t_desc[first].ddesc_status = htole32(status); + + data->td_m = m0; + data->td_active = map; + + bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + return 0; +} + +static int +dwc_gmac_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + // struct dwc_gmac_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCINITIFADDR: + ifp->if_flags |= IFF_UP; + dwc_gmac_init(ifp); + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(ifp, ifa); + break; +#endif + default: + break; + } + default: + if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) + break; + error = 0; + if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI) + ; + else if (ifp->if_flags & IFF_RUNNING) + /* setmulti */; + break; + } + + splx(s); + + return error; +} + +int +dwc_gmac_intr(struct dwc_gmac_softc *sc) +{ + uint32_t status, dma_status; + + 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); + + dma_status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, + AWIN_GMAC_DMA_STATUS); + +printf("%s: INTR status: %08x, DMA status: %08x\n", device_xname(sc->sc_dev), + status, dma_status); + +static size_t cnt = 0; +if (++cnt > 20) + panic("enough now"); + + return 1; +} Index: src/sys/dev/ic/dwc_gmac_reg.h diff -u /dev/null src/sys/dev/ic/dwc_gmac_reg.h:1.1 --- /dev/null Mon Sep 8 14:24:32 2014 +++ src/sys/dev/ic/dwc_gmac_reg.h Mon Sep 8 14:24:32 2014 @@ -0,0 +1,136 @@ +/*- + * 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 +#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_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 GMAC_MII_PHY_SHIFT 11 +#define GMAC_MII_PHY_MASK (0x1F << GMAC_MII_PHY_SHIFT) +#define GMAC_MII_REG_SHIFT 6 +#define GMAC_MII_REG_MASK (0x1F << GMAC_MII_REG_SHIFT) + +#define GMAC_MII_BUSY 1 +#define GMAC_MII_WRITE 2 +#define GMAC_MII_CLK_150_250M 0x10 + +#define GMAC_BUSMODE_RESET 1 + +#define AWIN_GMAC_MII_IRQ 1 + +#define GMAC_DMA_INT_NIE 0x10000 /* Normal/Summary */ +#define GMAC_DMA_INT_AIE 0x08000 /* Abnormal/Summary */ +#define GMAC_DMA_INT_ERE 0x04000 /* Early receive */ +#define GMAC_DMA_INT_FBE 0x02000 /* Fatal bus error */ +#define GMAC_DMA_INT_ETE 0x00400 /* Early transmit */ +#define GMAC_DMA_INT_RWE 0x00200 /* Receive watchdog */ +#define GMAC_DMA_INT_RSE 0x00100 /* Receive stopped */ +#define GMAC_DMA_INT_RUE 0x00080 /* Receive buffer unavailable */ +#define GMAC_DMA_INT_RIE 0x00040 /* Receive interrupt */ +#define GMAC_DMA_INT_UNE 0x00020 /* Tx underflow */ +#define GMAC_DMA_INT_OVE 0x00010 /* Receive overflow */ +#define GMAC_DMA_INT_TJE 0x00008 /* Transmit jabber */ +#define GMAC_DMA_INT_TUE 0x00004 /* Transmit buffer unavailable */ +#define GMAC_DMA_INT_TSE 0x00002 /* Transmit stopped */ +#define GMAC_DMA_INT_TIE 0x00001 /* Transmit interrupt */ + +#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 AWIN_DEF_MAC_INTRMASK 0x207 /* XXX ??? */ + +struct dwc_gmac_dev_dmadesc { + uint32_t ddesc_status; +/* both: */ +#define DDESC_STATUS_OWNEDBYDEV (1<<31) +/* for TX descriptors */ +#define DDESC_STATUS_TXINT (1<<30) +#define DDESC_STATUS_TXLAST (1<<29) +#define DDESC_STATUS_TXFIRST (1<<28) +#define DDESC_STATUS_TXCRCDIS (1<<27) +#define DDESC_STATUS_TXPADDIS (1<<26) +#define DDESC_STATUS_TXCHECKINSCTRL (1<<22) +#define DDESC_STATUS_TXRINGEND (1<<21) +#define DDESC_STATUS_TXCHAIN (1<<20) +#define DDESC_STATUS_MASK 0x1ffff +/* for RX descriptors */ +#define DDESC_STATUS_DAFILTERFAIL (1<<30) +#define DDESC_STATUS_FRMLENMSK (0x3fff << 16) +#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 + + uint32_t ddesc_cntl; +#define DDESC_CNTL_SIZE1MASK 0x1fff +#define DDESC_CNTL_SIZE1SHIFT 0 +#define DDESC_CNTL_SIZE2MASK (0x1fff<<16) +#define DDESC_CNTL_SIZE2SHIFT 16 + + uint32_t ddesc_data; /* pointer to buffer data */ + uint32_t ddesc_next; /* link to next descriptor */ +}; + Index: src/sys/dev/ic/dwc_gmac_var.h diff -u /dev/null src/sys/dev/ic/dwc_gmac_var.h:1.1 --- /dev/null Mon Sep 8 14:24:32 2014 +++ src/sys/dev/ic/dwc_gmac_var.h Mon Sep 8 14:24:32 2014 @@ -0,0 +1,90 @@ +/*- + * 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 AWGE_RX_RING_COUNT 256 +#define AWGE_TX_RING_COUNT 256 +#define AWGE_TOTAL_RING_COUNT \ + (AWGE_RX_RING_COUNT + AWGE_TX_RING_COUNT) + +#define AWGE_MAX_PACKET (ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) + + + +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[AWGE_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[AWGE_RX_RING_COUNT]; + int r_cur, r_next; + kmutex_t r_mtx; +}; + +struct dwc_gmac_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + struct ethercom sc_ec; + struct mii_data sc_mii; + kmutex_t 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; +}; + +void dwc_gmac_attach(struct dwc_gmac_softc*, uint8_t*); +int dwc_gmac_intr(struct dwc_gmac_softc*);