Module Name: src Committed By: martin Date: Sun Oct 19 11:45:01 UTC 2014
Modified Files: src/sys/dev/ic: dwc_gmac.c Log Message: Add more MAC filter setup, some DMA burst configuration (from jmcneill), actually enable RX interrupts (spotted by jmcneill), add RX handling code and debug code. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/dev/ic/dwc_gmac.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/dwc_gmac.c diff -u src/sys/dev/ic/dwc_gmac.c:1.10 src/sys/dev/ic/dwc_gmac.c:1.11 --- src/sys/dev/ic/dwc_gmac.c:1.10 Mon Oct 13 09:07:26 2014 +++ src/sys/dev/ic/dwc_gmac.c Sun Oct 19 11:45:01 2014 @@ -39,7 +39,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.10 2014/10/13 09:07:26 martin Exp $"); +__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.11 2014/10/19 11:45:01 martin Exp $"); /* #define DWC_GMAC_DEBUG 1 */ @@ -98,7 +98,7 @@ static void dwc_gmac_rx_intr(struct dwc_ -#define GMAC_DEF_DMA_INT_MASK (GMAC_DMA_INT_TIE| \ +#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) @@ -116,6 +116,7 @@ static void dwc_gmac_rx_intr(struct dwc_ #ifdef DWC_GMAC_DEBUG static void dwc_gmac_dump_dma(struct dwc_gmac_softc *sc); static void dwc_gmac_dump_tx_desc(struct dwc_gmac_softc *sc); +static void dwc_gmac_dump_rx_desc(struct dwc_gmac_softc *sc); static void dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg); static void dwc_dump_status(struct dwc_gmac_softc *sc); #endif @@ -389,7 +390,8 @@ dwc_gmac_alloc_rx_ring(struct dwc_gmac_s desc->ddesc_next = htole32(ring->r_physaddr + next * sizeof(*desc)); desc->ddesc_cntl = htole32( - __SHIFTIN(AWGE_MAX_PACKET,DDESC_CNTL_SIZE1MASK)); + __SHIFTIN(AWGE_MAX_PACKET,DDESC_CNTL_SIZE1MASK) | + DDESC_CNTL_RXCHAIN | DDESC_CNTL_RXINT); desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV); } @@ -425,6 +427,9 @@ dwc_gmac_reset_rx_ring(struct dwc_gmac_s 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); } static int @@ -654,8 +659,10 @@ dwc_gmac_miibus_statchg(struct ifnet *if 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_TXENABLE - | AWIN_GMAC_MAC_CONF_RXENABLE; + conf |= AWIN_GMAC_MAC_CONF_FRAMEBURST + | AWIN_GMAC_MAC_CONF_DISABLERXOWN + | AWIN_GMAC_MAC_CONF_RXENABLE + | AWIN_GMAC_MAC_CONF_TXENABLE; switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: break; @@ -689,6 +696,21 @@ dwc_gmac_init(struct ifnet *ifp) 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 | + __SHIFTIN(GMAC_BUSMODE_PRIORXTX_41, GMAC_BUSMODE_PRIORXTX) | + __SHIFTIN(8, GMCA_BUSMODE_PBL)); + + /* + * Set up address filter (XXX for testing only: promiscous) + */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_FFILT, + AWIN_GMAC_MAC_FFILT_PR); + + /* * Set up dma pointer for RX and TX ring */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_RX_ADDR, @@ -700,7 +722,8 @@ dwc_gmac_init(struct ifnet *ifp) * 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); + AWIN_GMAC_DMA_OPMODE, GMAC_DMA_OP_RXSTART | GMAC_DMA_OP_TXSTART | + GMAC_DMA_OP_STOREFORWARD); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; @@ -846,7 +869,6 @@ dwc_gmac_queue(struct dwc_gmac_softc *sc 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; @@ -865,6 +887,43 @@ dwc_gmac_ioctl(struct ifnet *ifp, u_long default: break; } + break; + + case SIOCSIFFLAGS: + if ((error = ifioctl_common(ifp, cmd, data)) != 0) + break; + + switch (ifp->if_flags & (IFF_UP|IFF_RUNNING)) { + case IFF_RUNNING: + /* + * If interface is marked down and it is running, then + * stop it. + */ + dwc_gmac_stop(ifp, 0); + ifp->if_flags &= ~IFF_RUNNING; + break; + case IFF_UP: + /* + * If interface is marked up and it is stopped, then + * start it. + */ + error = dwc_gmac_init(ifp); + break; + case IFF_UP|IFF_RUNNING: + /* + * If setting debug or promiscuous mode, do not reset + * the chip; for everything else, call dwc_gmac_init() + * which will trigger a reset. + */ + /* XXX - for now allways init */ + error = dwc_gmac_init(ifp); + break; + case 0: + break; + } + + break; + default: if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET) break; @@ -902,8 +961,10 @@ dwc_gmac_tx_intr(struct dwc_gmac_softc * dwc_gmac_txdesc_sync(sc, i, i+1, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); flags = le32toh(desc->ddesc_status); + if (flags & DDESC_STATUS_OWNEDBYDEV) break; + data = &sc->sc_txq.t_data[i]; if (data->td_m == NULL) continue; @@ -932,10 +993,116 @@ dwc_gmac_tx_intr(struct dwc_gmac_softc * static void dwc_gmac_rx_intr(struct dwc_gmac_softc *sc) { + struct ifnet *ifp = &sc->sc_ec.ec_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; + + for (i = sc->sc_rxq.r_cur; ; i = RX_NEXT(i)) { + #ifdef DWC_GMAC_DEBUG - aprint_normal_dev(sc->sc_dev, "rx intr\n"); - /* XXX */ +printf("rx int: checking desc #%d\n", i); #endif + + 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) { +#ifdef DWC_GMAC_DEBUG +printf("status %08x, still owned by device\n", status); +#endif + break; + } + + if (status & (DDESC_STATUS_RXERROR|DDESC_STATUS_RXTRUNCATED)) { +#ifdef DWC_GMAC_DEBUG +printf("status %08x, RX error, skipping\n", status); +#endif + ifp->if_ierrors++; + goto skip; + } + + len = __SHIFTOUT(status, DDESC_STATUS_FRMLENMSK); + +#ifdef DWC_GMAC_DEBUG +printf("rx int: device is done with #%d, len: %d\n", 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", + device_xname(sc->sc_dev)); + } + 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 */ + m->m_pkthdr.len = m->m_len = len; + m->m_pkthdr.rcvif = ifp; + + bpf_mtap(ifp, m); + ifp->if_ipackets++; + (*ifp->if_input)(ifp, m); + +skip: + desc->ddesc_cntl = htole32( + __SHIFTIN(AWGE_MAX_PACKET,DDESC_CNTL_SIZE1MASK)); + 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; + } int @@ -1030,6 +1197,23 @@ dwc_gmac_dump_tx_desc(struct dwc_gmac_so } static void +dwc_gmac_dump_rx_desc(struct dwc_gmac_softc *sc) +{ + int i; + + aprint_normal_dev(sc->sc_dev, "RX queue: cur=%d, next=%d\n", + sc->sc_rxq.r_cur, sc->sc_rxq.r_next); + aprint_normal_dev(sc->sc_dev, "RX DMA descriptors:\n"); + for (i = 0; i < AWGE_RX_RING_COUNT; i++) { + struct dwc_gmac_dev_dmadesc *desc = &sc->sc_rxq.r_desc[i]; + aprint_normal("#%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)); + } +} + +static void dwc_dump_status(struct dwc_gmac_softc *sc) { uint32_t status = bus_space_read_4(sc->sc_bst, sc->sc_bsh, @@ -1066,6 +1250,7 @@ dwc_dump_and_abort(struct dwc_gmac_softc dwc_dump_status(sc); dwc_gmac_dump_dma(sc); dwc_gmac_dump_tx_desc(sc); + dwc_gmac_dump_rx_desc(sc); panic(msg); }