Module Name: src
Committed By: martin
Date: Wed Oct 8 18:24:21 UTC 2014
Modified Files:
src/sys/dev/ic: dwc_gmac.c
Log Message:
More interrupt handling, more debug code, less magic numbers.
To generate a diff of this commit:
cvs rdiff -u -r1.7 -r1.8 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.7 src/sys/dev/ic/dwc_gmac.c:1.8
--- src/sys/dev/ic/dwc_gmac.c:1.7 Sun Sep 14 18:28:37 2014
+++ src/sys/dev/ic/dwc_gmac.c Wed Oct 8 18:24:21 2014
@@ -39,7 +39,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.7 2014/09/14 18:28:37 martin Exp $");
+__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.8 2014/10/08 18:24:21 martin Exp $");
/* #define DWC_GMAC_DEBUG 1 */
@@ -86,17 +86,37 @@ static void dwc_gmac_stop(struct ifnet *
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 *);
-
+static void dwc_gmac_tx_intr(struct dwc_gmac_softc *sc);
+static void dwc_gmac_rx_intr(struct dwc_gmac_softc *sc);
#define TX_DESC_OFFSET(N) ((AWGE_RX_RING_COUNT+(N)) \
*sizeof(struct dwc_gmac_dev_dmadesc))
+#define TX_NEXT(N) (((N)+1) & (AWGE_TX_RING_COUNT-1))
#define RX_DESC_OFFSET(N) ((N)*sizeof(struct dwc_gmac_dev_dmadesc))
+#define RX_NEXT(N) (((N)+1) & (AWGE_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_ETE| \
+ GMAC_DMA_INT_RWE|GMAC_DMA_INT_RUE| \
+ GMAC_DMA_INT_UNE|GMAC_DMA_INT_OVE| \
+ GMAC_DMA_INT_TJE|GMAC_DMA_INT_TUE)
+
+#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
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_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg);
#endif
void
@@ -127,8 +147,10 @@ dwc_gmac_attach(struct dwc_gmac_softc *s
* 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);
+ 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;
@@ -137,10 +159,6 @@ dwc_gmac_attach(struct dwc_gmac_softc *s
enaddr[5] = (machi >> 8) & 0x0ff;
}
-#ifdef DWC_GMAC_DEBUG
- dwc_gmac_dump_dma(sc);
-#endif
-
/*
* Init chip and do intial setup
*/
@@ -210,8 +228,10 @@ dwc_gmac_attach(struct dwc_gmac_softc *s
/*
* 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);
+ 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;
@@ -364,7 +384,7 @@ dwc_gmac_alloc_rx_ring(struct dwc_gmac_s
desc = &sc->sc_rxq.r_desc[i];
desc->ddesc_data = htole32(physaddr);
- next = (i+1) % AWGE_RX_RING_COUNT;
+ next = RX_NEXT(i);
desc->ddesc_next = htole32(ring->r_physaddr
+ next * sizeof(*desc));
desc->ddesc_cntl = htole32(
@@ -528,7 +548,7 @@ dwc_gmac_alloc_tx_ring(struct dwc_gmac_s
}
ring->t_desc[i].ddesc_next = htole32(
ring->t_physaddr + sizeof(struct dwc_gmac_dev_dmadesc)
- *((i+1)%AWGE_TX_RING_COUNT));
+ *TX_NEXT(i));
}
return 0;
@@ -706,11 +726,6 @@ dwc_gmac_start(struct ifnet *ifp)
bus_space_read_4(sc->sc_bst, sc->sc_bsh,
AWIN_GMAC_DMA_OPMODE) | GMAC_DMA_OP_TXSTART);
}
-
-#ifdef DWC_GMAC_DEBUG
- dwc_gmac_dump_dma(sc);
- dwc_gmac_dump_tx_desc(sc);
-#endif
}
static void
@@ -745,6 +760,11 @@ dwc_gmac_queue(struct dwc_gmac_softc *sc
uint32_t flags, len;
int error, i, first;
+#ifdef DWC_GMAC_DEBUG
+ aprint_normal_dev(sc->sc_dev,
+ "dwc_gmac_queue: adding mbuf chain %p\n", m0);
+#endif
+
first = sc->sc_txq.t_cur;
map = sc->sc_txq.t_data[first].td_map;
flags = 0;
@@ -763,23 +783,24 @@ dwc_gmac_queue(struct dwc_gmac_softc *sc
}
data = NULL;
- flags = DDESC_CNTL_TXFIRST|DDESC_CNTL_TXINT|DDESC_CNTL_TXCHAIN;
+ flags = DDESC_CNTL_TXFIRST|DDESC_CNTL_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 = __SHIFTIN(map->dm_segs[i].ds_len,DDESC_CNTL_SIZE1MASK);
+ if (i == map->dm_nsegs-1)
+ flags |= DDESC_CNTL_TXLAST|DDESC_CNTL_TXINT;
#ifdef DWC_GMAC_DEBUG
aprint_normal_dev(sc->sc_dev, "enqueing desc #%d data %08lx "
- "len %lu\n", sc->sc_txq.t_cur,
+ "len %lu (flags: %08x, len: %08x)\n", sc->sc_txq.t_cur,
(unsigned long)map->dm_segs[i].ds_addr,
- (unsigned long)map->dm_segs[i].ds_len);
+ (unsigned long)map->dm_segs[i].ds_len,
+ flags, len);
#endif
- desc = &sc->sc_txq.t_desc[sc->sc_txq.t_cur];
-
- desc->ddesc_data = htole32(map->dm_segs[i].ds_addr);
- len = __SHIFTIN(map->dm_segs[i].ds_len,DDESC_CNTL_SIZE1MASK);
- if (i == map->dm_nsegs-1)
- flags |= DDESC_CNTL_TXLAST;
desc->ddesc_cntl = htole32(len|flags);
flags &= ~DDESC_CNTL_TXFIRST;
@@ -789,10 +810,9 @@ dwc_gmac_queue(struct dwc_gmac_softc *sc
*/
if (i)
desc->ddesc_status = htole32(DDESC_STATUS_OWNEDBYDEV);
- sc->sc_txq.t_queued++;
- sc->sc_txq.t_cur = (sc->sc_txq.t_cur + 1)
- & (AWGE_TX_RING_COUNT-1);
+ sc->sc_txq.t_queued++;
+ sc->sc_txq.t_cur = TX_NEXT(sc->sc_txq.t_cur);
}
/* Pass first to device */
@@ -846,29 +866,105 @@ dwc_gmac_ioctl(struct ifnet *ifp, u_long
return error;
}
+static void
+dwc_gmac_tx_intr(struct dwc_gmac_softc *sc)
+{
+ struct dwc_gmac_tx_data *data;
+ struct dwc_gmac_dev_dmadesc *desc;
+ uint32_t flags;
+ int i;
+
+ for (i = sc->sc_txq.t_next; sc->sc_txq.t_queued > 0;
+ i = TX_NEXT(i), sc->sc_txq.t_queued--) {
+
+#ifdef DWC_GMAC_DEBUG
+ aprint_normal_dev(sc->sc_dev,
+ "dwc_gmac_tx_intr: checking desc #%d (t_queued: %d)\n",
+ i, sc->sc_txq.t_queued);
+#endif
+
+ desc = &sc->sc_txq.t_desc[i];
+ 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;
+ sc->sc_ec.ec_if.if_opackets++;
+ 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
+ aprint_normal_dev(sc->sc_dev,
+ "dwc_gmac_tx_intr: done with packet at desc #%d, "
+ "freeing mbuf %p\n", i, data->td_m);
+#endif
+
+ m_freem(data->td_m);
+ data->td_m = NULL;
+ }
+
+ sc->sc_txq.t_next = i;
+
+ if (sc->sc_txq.t_queued < AWGE_TX_RING_COUNT) {
+ sc->sc_ec.ec_if.if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+static void
+dwc_gmac_rx_intr(struct dwc_gmac_softc *sc)
+{
+#ifdef DWC_GMAC_DEBUG
+ aprint_normal_dev(sc->sc_dev, "rx intr\n");
+ /* XXX */
+#endif
+}
+
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);
-printf("%s: INTR status: %08x, DMA status: %08x\n", device_xname(sc->sc_dev),
- status, dma_status);
+ if (dma_status & (GMAC_DMA_INT_NIE|GMAC_DMA_INT_AIE))
+ rv = 1;
-static size_t cnt = 0;
-if (++cnt > 20)
- panic("enough now");
+ if (dma_status & GMAC_DMA_INT_TIE)
+ dwc_gmac_tx_intr(sc);
- return 1;
+ if (dma_status & GMAC_DMA_INT_RIE)
+ dwc_gmac_rx_intr(sc);
+
+ /*
+ * Check error conditions
+ */
+ if (dma_status & GMAC_DMA_INT_ERRORS) {
+ sc->sc_ec.ec_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);
+
+ return rv;
}
#ifdef DWC_GMAC_DEBUG
@@ -906,7 +1002,9 @@ dwc_gmac_dump_tx_desc(struct dwc_gmac_so
{
int i;
- aprint_normal_dev(sc->sc_dev, " TX DMA descriptors:\n");
+ aprint_normal_dev(sc->sc_dev, "TX queue: cur=%d, next=%d, queued=%d\n",
+ sc->sc_txq.t_cur, sc->sc_txq.t_next, sc->sc_txq.t_queued);
+ aprint_normal_dev(sc->sc_dev, "TX DMA descriptors:\n");
for (i = 0; i < AWGE_TX_RING_COUNT; i++) {
struct dwc_gmac_dev_dmadesc *desc = &sc->sc_txq.t_desc[i];
aprint_normal("#%d (%08lx): status: %08x cntl: %08x data: %08x next: %08x\n",
@@ -916,4 +1014,41 @@ dwc_gmac_dump_tx_desc(struct dwc_gmac_so
}
}
+
+static void
+dwc_dump_and_abort(struct dwc_gmac_softc *sc, const char *msg)
+{
+ 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""NIE\0"
+ "b\x0f""AIE\0"
+ "b\x0e""ERE\0"
+ "b\x0d""FBE\0"
+ "b\x0a""ETE\0"
+ "b\x09""RWE\0"
+ "b\x08""RSE\0"
+ "b\x07""RUE\0"
+ "b\x06""RIE\0"
+ "b\x05""UNE\0"
+ "b\x04""OVE\0"
+ "b\x03""TJE\0"
+ "b\x02""TUE\0"
+ "b\x01""TSE\0"
+ "b\x00""TIE\0"
+ "\0", dma_status);
+ printf("%s: INTR status: %08x, DMA status: %s\n",
+ device_xname(sc->sc_dev),
+ status, buf);
+
+ dwc_gmac_dump_dma(sc);
+ dwc_gmac_dump_tx_desc(sc);
+
+ panic(msg);
+}
#endif