Module Name: src Committed By: matt Date: Fri Oct 12 23:25:16 UTC 2012
Modified Files: src/sys/arch/arm/broadcom: bcm53xx_eth.c Log Message: Add some defensive code to deal with "issues" of this interface. Seems it can't do DMA updates of the rxsts for mbufs with addresses >= 256MB. It can't also seem to properly read from the descriptor rings if they are < 256MB. And I have no idea why either should matter. To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/sys/arch/arm/broadcom/bcm53xx_eth.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/arch/arm/broadcom/bcm53xx_eth.c diff -u src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.9 src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.10 --- src/sys/arch/arm/broadcom/bcm53xx_eth.c:1.9 Mon Oct 8 20:54:10 2012 +++ src/sys/arch/arm/broadcom/bcm53xx_eth.c Fri Oct 12 23:25:15 2012 @@ -27,13 +27,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _ARM32_BUS_DMA_PRIVATE #define GMAC_PRIVATE #include "locators.h" #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.9 2012/10/08 20:54:10 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth.c,v 1.10 2012/10/12 23:25:15 matt Exp $"); #include <sys/param.h> #include <sys/atomic.h> @@ -61,13 +62,15 @@ __KERNEL_RCSID(1, "$NetBSD: bcm53xx_eth. #include <arm/broadcom/bcm53xx_var.h> #define BCMETH_RCVOFFSET 6 -#define BCMETH_MAXTXMBUFS 32 +#define BCMETH_MAXTXMBUFS 128 #define BCMETH_NTXSEGS 30 #define BCMETH_MAXRXMBUFS 255 #define BCMETH_MINRXMBUFS 64 #define BCMETH_NRXSEGS 1 #define BCMETH_RINGSIZE PAGE_SIZE +#define BCMETH_RCVMAGIC 0xfeedface + static int bcmeth_ccb_match(device_t, cfdata_t, void *); static void bcmeth_ccb_attach(device_t, device_t, void *); @@ -86,6 +89,7 @@ struct bcmeth_txqueue { bus_size_t txq_reg_xmtptr; bus_size_t txq_reg_xmtctl; bus_size_t txq_reg_xmtsts0; + bus_size_t txq_reg_xmtsts1; bus_dma_segment_t txq_descmap_seg; }; @@ -104,6 +108,7 @@ struct bcmeth_rxqueue { bus_size_t rxq_reg_rcvptr; bus_size_t rxq_reg_rcvctl; bus_size_t rxq_reg_rcvsts0; + bus_size_t rxq_reg_rcvsts1; bus_dma_segment_t rxq_descmap_seg; }; @@ -141,8 +146,10 @@ struct bcmeth_softc { struct evcnt sc_ev_intr; struct evcnt sc_ev_soft_intr; - struct evcnt sc_ev_work;; + struct evcnt sc_ev_work; struct evcnt sc_ev_tx_stall; + struct evcnt sc_ev_rx_badmagic_lo; + struct evcnt sc_ev_rx_badmagic_hi; struct ifqueue sc_rx_bufcache; struct bcmeth_mapcache *sc_rx_mapcache; @@ -203,6 +210,13 @@ static void bcmeth_worker(struct work *, static int bcmeth_mediachange(struct ifnet *); static void bcmeth_mediastatus(struct ifnet *, struct ifmediareq *); +static struct arm32_dma_range bcmeth_dma_ranges[2]; +static struct arm32_bus_dma_tag bcmeth_dma_tag = { + _BUS_DMAMAP_FUNCS, + _BUS_DMAMEM_FUNCS, + _BUS_DMATAG_FUNCS, +}; + static inline uint32_t bcmeth_read_4(struct bcmeth_softc *sc, bus_size_t o) { @@ -252,6 +266,25 @@ bcmeth_ccb_attach(device_t parent, devic bus_space_subregion(sc->sc_bst, ccbaa->ccbaa_ccb_bsh, loc->loc_offset, loc->loc_size, &sc->sc_bsh); + /* + * Initialize a bus_dma_tag to prefer memory over 256MB. + */ + if (bcmeth_dma_tag._nranges != sc->sc_dmat->_nranges) { + KASSERT(sc->sc_dmat->_nranges == 2); + bcmeth_dma_ranges[0] = sc->sc_dmat->_ranges[1]; + bcmeth_dma_ranges[1] = sc->sc_dmat->_ranges[0]; + bcmeth_dma_tag._ranges = bcmeth_dma_ranges; + bcmeth_dma_tag._nranges = sc->sc_dmat->_nranges; + } + + /* + * If we initialized our dma tag, then use it. + */ + if (bcmeth_dma_tag._nranges > 0) { + sc->sc_dmat = &bcmeth_dma_tag; + KASSERT(sc->sc_dmat->_ranges[0].dr_busbase - sc->sc_dmat->_ranges[1].dr_busbase == 0x10000000); + } + prop_data_t eaprop = prop_dictionary_get(dict, "mac-address"); if (eaprop == NULL) { uint32_t mac0 = bcmeth_read_4(sc, UNIMAC_MAC_0); @@ -372,6 +405,10 @@ bcmeth_ccb_attach(device_t parent, devic NULL, xname, "work items"); evcnt_attach_dynamic(&sc->sc_ev_tx_stall, EVCNT_TYPE_MISC, NULL, xname, "tx stalls"); + evcnt_attach_dynamic(&sc->sc_ev_rx_badmagic_lo, EVCNT_TYPE_MISC, + NULL, xname, "rx badmagic lo"); + evcnt_attach_dynamic(&sc->sc_ev_rx_badmagic_hi, EVCNT_TYPE_MISC, + NULL, xname, "rx badmagic hi"); } static int @@ -763,15 +800,14 @@ bcmeth_dmamem_alloc( *kvap = NULL; *map = NULL; - error = bus_dmamem_alloc(dmat, map_size, PAGE_SIZE, 0, + error = bus_dmamem_alloc(dmat, map_size, 2*PAGE_SIZE, 0, seg, 1, &nseg, 0); if (error) return error; KASSERT(nseg == 1); - error = bus_dmamem_map(dmat, seg, nseg, map_size, (void **)kvap, - BUS_DMA_COHERENT); + error = bus_dmamem_map(dmat, seg, nseg, map_size, (void **)kvap, 0); if (error == 0) { error = bus_dmamap_create(dmat, map_size, 1, map_size, 0, 0, map); @@ -826,8 +862,12 @@ bcmeth_rx_buf_alloc( return NULL; } KASSERT(map->dm_mapsize == MCLBYTES); - bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, - BUS_DMASYNC_PREREAD); + *mtod(m, uint32_t *) = BCMETH_RCVMAGIC; + bus_dmamap_sync(sc->sc_dmat, map, 0, sizeof(uint32_t), + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + bus_dmamap_sync(sc->sc_dmat, map, sizeof(uint32_t), + map->dm_mapsize - sizeof(uint32_t), BUS_DMASYNC_PREREAD); return m; } @@ -977,19 +1017,26 @@ bcmeth_rxq_consume( bus_dmamap_sync(sc->sc_dmat, map, 0, arm_dcache_align, BUS_DMASYNC_POSTREAD); memcpy(&rxsts, rxq->rxq_mhead->m_data, 4); +#if 0 + KASSERTMSG(rxsts != BCMETH_RCVMAGIC, "currdscr=%u consumer=%zd", + currdscr, consumer - rxq->rxq_first); +#endif /* * Get the count of descriptors. Fetch the correct number * of mbufs. */ - size_t desc_count = __SHIFTOUT(rxsts, RXSTS_DESC_COUNT) + 1; + size_t desc_count = rxsts != BCMETH_RCVMAGIC ? __SHIFTOUT(rxsts, RXSTS_DESC_COUNT) + 1 : 1; struct mbuf *m = rxq->rxq_mhead; struct mbuf *m_last = m; for (size_t i = 1; i < desc_count; i++) { if (++consumer == rxq->rxq_last) { consumer = rxq->rxq_first; } - KASSERT(consumer != rxq->rxq_first + currdscr); + KASSERTMSG(consumer != rxq->rxq_first + currdscr, + "i=%zu rxsts=%#x desc_count=%zu currdscr=%u consumer=%zd", + i, rxsts, desc_count, currdscr, + consumer - rxq->rxq_first); m_last = m_last->m_next; } @@ -1000,7 +1047,15 @@ bcmeth_rxq_consume( rxq->rxq_mtail = &rxq->rxq_mhead; m_last->m_next = NULL; - if (rxsts & (RXSTS_CRC_ERROR|RXSTS_OVERSIZED|RXSTS_PKT_OVERFLOW)) { + if (rxsts == BCMETH_RCVMAGIC) { + ifp->if_ierrors++; + if ((m->m_ext.ext_paddr >> 28) == 8) { + sc->sc_ev_rx_badmagic_lo.ev_count++; + } else { + sc->sc_ev_rx_badmagic_hi.ev_count++; + } + IF_ENQUEUE(&sc->sc_rx_bufcache, m); + } else if (rxsts & (RXSTS_CRC_ERROR|RXSTS_OVERSIZED|RXSTS_PKT_OVERFLOW)) { aprint_error_dev(sc->sc_dev, "[%zu]: count=%zu rxsts=%#x\n", consumer - rxq->rxq_first, desc_count, rxsts); /* @@ -1137,6 +1192,7 @@ bcmeth_rxq_attach( rxq->rxq_reg_rcvctl = GMAC_RCVCONTROL; rxq->rxq_reg_rcvptr = GMAC_RCVPTR; rxq->rxq_reg_rcvsts0 = GMAC_RCVSTATUS0; + rxq->rxq_reg_rcvsts1 = GMAC_RCVSTATUS1; return 0; } @@ -1186,6 +1242,7 @@ bcmeth_txq_attach( txq->txq_reg_xmtctl = GMAC_XMTCONTROL; txq->txq_reg_xmtptr = GMAC_XMTPTR; txq->txq_reg_xmtsts0 = GMAC_XMTSTATUS0; + txq->txq_reg_xmtsts1 = GMAC_XMTSTATUS1; bcmeth_txq_reset(sc, txq); @@ -1292,7 +1349,8 @@ bcmeth_txq_produce( producer->txdb_flags, producer->txdb_buflen, producer->txdb_addrlo, producer->txdb_addrhi); #endif - bcmeth_txq_desc_presync(sc, txq, start, count); + if (count) + bcmeth_txq_desc_presync(sc, txq, start, count); /* * Reduce free count by the number of segments we consumed. @@ -1308,12 +1366,11 @@ bcmeth_txq_produce( txq->txq_producer - txq->txq_first, producer - txq->txq_first); #endif - if (++producer == txq->txq_last) + if (producer + 1 == txq->txq_last) txq->txq_producer = txq->txq_first; else - txq->txq_producer = producer; + txq->txq_producer = producer + 1; IF_ENQUEUE(&txq->txq_mbufs, m); - bpf_mtap(&sc->sc_if, m); /* * Let the transmitter know there's more to do @@ -1415,6 +1472,7 @@ bcmeth_txq_consume( printf("%s: mbuf %p: consumed a %u byte packet\n", __func__, m, m->m_pkthdr.len); #endif + bpf_mtap(ifp, m); ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; if (m->m_flags & M_MCAST) @@ -1584,8 +1642,22 @@ bcmeth_intr(void *arg) } if (intstatus) { - aprint_error_dev(sc->sc_dev, "intr: intstatus=%#x\n", - intstatus); + aprint_error_dev(sc->sc_dev, + "intr: intstatus=%#x\n", intstatus); + aprint_error_dev(sc->sc_dev, + "rcvbase=%p/%#lx rcvptr=%#x rcvsts=%#x/%#x\n", + sc->sc_rxq.rxq_first, + sc->sc_rxq.rxq_descmap->dm_segs[0].ds_addr, + bcmeth_read_4(sc, sc->sc_rxq.rxq_reg_rcvptr), + bcmeth_read_4(sc, sc->sc_rxq.rxq_reg_rcvsts0), + bcmeth_read_4(sc, sc->sc_rxq.rxq_reg_rcvsts1)); + aprint_error_dev(sc->sc_dev, + "xmtbase=%p/%#lx xmtptr=%#x xmtsts=%#x/%#x\n", + sc->sc_txq.txq_first, + sc->sc_txq.txq_descmap->dm_segs[0].ds_addr, + bcmeth_read_4(sc, sc->sc_txq.txq_reg_xmtptr), + bcmeth_read_4(sc, sc->sc_txq.txq_reg_xmtsts0), + bcmeth_read_4(sc, sc->sc_txq.txq_reg_xmtsts1)); Debugger(); sc->sc_intmask &= ~intstatus; work_flags |= WORK_REINIT;