summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/dwc/if_dwc.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/dev/dwc/if_dwc.c')
-rw-r--r--freebsd/sys/dev/dwc/if_dwc.c736
1 files changed, 303 insertions, 433 deletions
diff --git a/freebsd/sys/dev/dwc/if_dwc.c b/freebsd/sys/dev/dwc/if_dwc.c
index a3716a15..c7284ee1 100644
--- a/freebsd/sys/dev/dwc/if_dwc.c
+++ b/freebsd/sys/dev/dwc/if_dwc.c
@@ -44,24 +44,16 @@ __FBSDID("$FreeBSD$");
#include <rtems/bsd/sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/gpio.h>
#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/malloc.h>
-#include <sys/rman.h>
-#include <sys/endian.h>
#include <rtems/bsd/sys/lock.h>
+#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/rman.h>
#include <sys/socket.h>
#include <sys/sockio.h>
-#include <sys/sysctl.h>
-
-#ifndef __rtems__
-#include <dev/fdt/fdt_common.h>
-#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-#endif /* __rtems__ */
#include <net/bpf.h>
#include <net/if.h>
@@ -70,21 +62,27 @@ __FBSDID("$FreeBSD$");
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
-#include <net/if_vlan_var.h>
#include <machine/bus.h>
-#ifndef __rtems__
-#include <machine/fdt.h>
-#endif /* __rtems__ */
+#include <dev/dwc/if_dwc.h>
+#include <dev/dwc/if_dwcvar.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
-#include <rtems/bsd/local/miibus_if.h>
-#ifdef __rtems__
-#pragma GCC diagnostic ignored "-Wpointer-sign"
-#include <rtems/bsd/bsd.h>
+#ifndef __rtems__
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
#endif /* __rtems__ */
+#ifdef EXT_RESOURCES
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#endif
+
+#include <rtems/bsd/local/if_dwc_if.h>
+#include <rtems/bsd/local/gpio_if.h>
+#include <rtems/bsd/local/miibus_if.h>
+
#define READ4(_sc, _reg) \
bus_read_4((_sc)->res[0], _reg)
#define WRITE4(_sc, _reg, _val) \
@@ -93,128 +91,53 @@ __FBSDID("$FreeBSD$");
#define MAC_RESET_TIMEOUT 100
#define WATCHDOG_TIMEOUT_SECS 5
#define STATS_HARVEST_INTERVAL 2
-#define MII_CLK_VAL 2
-
-#include <dev/dwc/if_dwc.h>
#define DWC_LOCK(sc) mtx_lock(&(sc)->mtx)
#define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
-#define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED);
-#define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED);
-
-#define DDESC_TDES0_OWN (1 << 31)
-#define DDESC_TDES0_TXINT (1 << 30)
-#define DDESC_TDES0_TXLAST (1 << 29)
-#define DDESC_TDES0_TXFIRST (1 << 28)
-#define DDESC_TDES0_TXCRCDIS (1 << 27)
-#define DDESC_TDES0_CIC_IP_HDR (0x1 << 22)
-#define DDESC_TDES0_CIC_IP_HDR_PYL (0x2 << 22)
-#define DDESC_TDES0_CIC_IP_HDR_PYL_PHDR (0x3 << 22)
-#define DDESC_TDES0_TXRINGEND (1 << 21)
-#define DDESC_TDES0_TXCHAIN (1 << 20)
-
-#define DDESC_RDES0_OWN (1 << 31)
+#define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
+#define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
+
+#define DDESC_TDES0_OWN (1U << 31)
+#define DDESC_TDES0_TXINT (1U << 30)
+#define DDESC_TDES0_TXLAST (1U << 29)
+#define DDESC_TDES0_TXFIRST (1U << 28)
+#define DDESC_TDES0_TXCRCDIS (1U << 27)
+#define DDESC_TDES0_TXRINGEND (1U << 21)
+#define DDESC_TDES0_TXCHAIN (1U << 20)
+
+#define DDESC_RDES0_OWN (1U << 31)
#define DDESC_RDES0_FL_MASK 0x3fff
#define DDESC_RDES0_FL_SHIFT 16 /* Frame Length */
-#define DDESC_RDES0_ESA (1 << 0)
-#define DDESC_RDES1_CHAINED (1 << 14)
-#define DDESC_RDES4_IP_PYL_ERR (1 << 4)
-#define DDESC_RDES4_IP_HDR_ERR (1 << 3)
-#define DDESC_RDES4_IP_PYL_TYPE_MSK 0x7
-#define DDESC_RDES4_IP_PYL_UDP 1
-#define DDESC_RDES4_IP_PYL_TCP 2
-
-struct dwc_bufmap {
-#ifndef __rtems__
- bus_dmamap_t map;
-#endif /* __rtems__ */
- struct mbuf *mbuf;
-};
+#define DDESC_RDES1_CHAINED (1U << 14)
+
+/* Alt descriptor bits. */
+#define DDESC_CNTL_TXINT (1U << 31)
+#define DDESC_CNTL_TXLAST (1U << 30)
+#define DDESC_CNTL_TXFIRST (1U << 29)
+#define DDESC_CNTL_TXCRCDIS (1U << 26)
+#define DDESC_CNTL_TXRINGEND (1U << 25)
+#define DDESC_CNTL_TXCHAIN (1U << 24)
+
+#define DDESC_CNTL_CHAINED (1U << 24)
/*
* A hardware buffer descriptor. Rx and Tx buffers have the same descriptor
- * layout, but the bits in the flags field have different meanings.
+ * layout, but the bits in the fields have different meanings.
*/
struct dwc_hwdesc
{
- uint32_t tdes0;
- uint32_t tdes1;
+ uint32_t tdes0; /* status for alt layout */
+ uint32_t tdes1; /* cntl for alt layout */
uint32_t addr; /* pointer to buffer data */
uint32_t addr_next; /* link to next descriptor */
- uint32_t tdes4;
- uint32_t tdes5;
- uint32_t timestamp_low;
- uint32_t timestamp_high;
};
/*
- * Driver data and defines.
- */
-#ifndef __rtems__
-#define RX_DESC_COUNT 1024
-#else /* __rtems__ */
-#define RX_DESC_COUNT 256
-#endif /* __rtems__ */
-#define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT)
-#define TX_DESC_COUNT 1024
-#define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT)
-#define TX_MAX_DMA_SEGS 8 /* maximum segs in a tx mbuf dma */
-
-/*
* The hardware imposes alignment restrictions on various objects involved in
* DMA transfers. These values are expressed in bytes (not bits).
*/
#define DWC_DESC_RING_ALIGN 2048
-#define DWC_CKSUM_ASSIST (CSUM_IP | CSUM_TCP | CSUM_UDP | \
- CSUM_TCP_IPV6 | CSUM_UDP_IPV6)
-
-struct dwc_softc {
- struct resource *res[2];
- bus_space_tag_t bst;
- bus_space_handle_t bsh;
- device_t dev;
- int mii_clk;
- device_t miibus;
- struct mii_data * mii_softc;
- struct ifnet *ifp;
- int if_flags;
- struct mtx mtx;
- void * intr_cookie;
- struct callout dwc_callout;
- uint8_t phy_conn_type;
- uint8_t mactype;
- boolean_t link_is_up;
- boolean_t is_attached;
- boolean_t is_detaching;
- int tx_watchdog_count;
- int stats_harvest_count;
-
- /* RX */
- bus_dma_tag_t rxdesc_tag;
- bus_dmamap_t rxdesc_map;
- struct dwc_hwdesc *rxdesc_ring;
-#ifndef __rtems__
- bus_addr_t rxdesc_ring_paddr;
- bus_dma_tag_t rxbuf_tag;
-#endif /* __rtems__ */
- struct dwc_bufmap rxbuf_map[RX_DESC_COUNT];
- uint32_t rx_idx;
-
- /* TX */
- bus_dma_tag_t txdesc_tag;
- bus_dmamap_t txdesc_map;
- struct dwc_hwdesc *txdesc_ring;
-#ifndef __rtems__
- bus_addr_t txdesc_ring_paddr;
- bus_dma_tag_t txbuf_tag;
-#endif /* __rtems__ */
- struct dwc_bufmap txbuf_map[TX_DESC_COUNT];
- uint32_t tx_idx_head;
- uint32_t tx_idx_tail;
- int txcount;
-};
-
static struct resource_spec dwc_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
@@ -234,13 +157,12 @@ next_rxidx(struct dwc_softc *sc, uint32_t curidx)
}
static inline uint32_t
-next_txidx(struct dwc_softc *sc, uint32_t curidx, int inc)
+next_txidx(struct dwc_softc *sc, uint32_t curidx)
{
- return ((curidx + (uint32_t)inc) % TX_DESC_COUNT);
+ return ((curidx + 1) % TX_DESC_COUNT);
}
-#ifndef __rtems__
static void
dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
{
@@ -249,130 +171,75 @@ dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
return;
*(bus_addr_t *)arg = segs[0].ds_addr;
}
-#endif /* __rtems__ */
-static void
-dwc_setup_txdesc(struct dwc_softc *sc, int csum_flags, int idx,
- bus_dma_segment_t segs[TX_MAX_DMA_SEGS], int nsegs)
+inline static uint32_t
+dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr,
+ uint32_t len)
{
- int i;
-
- sc->txcount += nsegs;
+ uint32_t flags;
+ uint32_t nidx;
- idx = next_txidx(sc, idx, nsegs);
- sc->tx_idx_head = idx;
+ nidx = next_txidx(sc, idx);
- /*
- * Fill in the TX descriptors back to front so that OWN bit in first
- * descriptor is set last.
- */
- for (i = nsegs - 1; i >= 0; i--) {
- uint32_t tdes0;
+ /* Addr/len 0 means we're clearing the descriptor after xmit done. */
+ if (paddr == 0 || len == 0) {
+ flags = 0;
+ --sc->txcount;
+ } else {
+ if (sc->mactype == DWC_GMAC_ALT_DESC)
+ flags = DDESC_CNTL_TXCHAIN | DDESC_CNTL_TXFIRST
+ | DDESC_CNTL_TXLAST | DDESC_CNTL_TXINT;
+ else
+ flags = DDESC_TDES0_TXCHAIN | DDESC_TDES0_TXFIRST
+ | DDESC_TDES0_TXLAST | DDESC_TDES0_TXINT;
+ ++sc->txcount;
+ }
- idx = next_txidx(sc, idx, -1);
+ sc->txdesc_ring[idx].addr = (uint32_t)(paddr);
+ if (sc->mactype == DWC_GMAC_ALT_DESC) {
+ sc->txdesc_ring[idx].tdes0 = 0;
+ sc->txdesc_ring[idx].tdes1 = flags | len;
+ } else {
+ sc->txdesc_ring[idx].tdes0 = flags;
+ sc->txdesc_ring[idx].tdes1 = len;
+ }
- sc->txdesc_ring[idx].addr = segs[i].ds_addr;
- sc->txdesc_ring[idx].tdes1 = segs[i].ds_len;
+ if (paddr && len) {
wmb();
-
- tdes0 = DDESC_TDES0_TXCHAIN | DDESC_TDES0_TXINT |
- DDESC_TDES0_OWN;
-
- if (i == 0) {
- tdes0 |= DDESC_TDES0_TXFIRST;
-
- if ((csum_flags & (CSUM_TCP | CSUM_UDP |
- CSUM_TCP_IPV6 | CSUM_UDP_IPV6)) != 0)
- tdes0 |= DDESC_TDES0_CIC_IP_HDR_PYL_PHDR;
- else if ((csum_flags & CSUM_IP) != 0)
- tdes0 |= DDESC_TDES0_CIC_IP_HDR;
- }
-
- if (i == nsegs - 1)
- tdes0 |= DDESC_TDES0_TXLAST;
-
- sc->txdesc_ring[idx].tdes0 = tdes0;
+ sc->txdesc_ring[idx].tdes0 |= DDESC_TDES0_OWN;
wmb();
-
- if (i != 0)
- sc->txbuf_map[idx].mbuf = NULL;
}
+
+ return (nidx);
}
-#ifdef __rtems__
static int
-dwc_get_segs_for_tx(struct mbuf *m, bus_dma_segment_t segs[TX_MAX_DMA_SEGS],
- int *nsegs)
+dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp)
{
- int i = 0;
-
- do {
- if (m->m_len > 0) {
- segs[i].ds_addr = mtod(m, bus_addr_t);
- segs[i].ds_len = m->m_len;
- rtems_cache_flush_multiple_data_lines(m->m_data, m->m_len);
- ++i;
- }
-
- m = m->m_next;
-
- if (m == NULL) {
- *nsegs = i;
-
- return (0);
- }
- } while (i < TX_MAX_DMA_SEGS);
+ struct bus_dma_segment seg;
+ int error, nsegs;
+ struct mbuf * m;
- return (EFBIG);
-}
-#endif /* __rtems__ */
-static void
-dwc_setup_txbuf(struct dwc_softc *sc, struct mbuf *m, int *start_tx)
-{
- bus_dma_segment_t segs[TX_MAX_DMA_SEGS];
- int error, nsegs, idx;
+ if ((m = m_defrag(*mp, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ *mp = m;
- idx = sc->tx_idx_head;
-#ifndef __rtems__
error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map,
- m, &seg, &nsegs, BUS_DMA_NOWAIT);
-
-#else /* __rtems__ */
- error = dwc_get_segs_for_tx(m, segs, &nsegs);
-#endif /* __rtems__ */
- if (error == EFBIG) {
- /* Too many segments! Defrag and try again. */
- struct mbuf *m2 = m_defrag(m, M_NOWAIT);
-
- if (m2 == NULL) {
- m_freem(m);
- return;
- }
- m = m2;
-#ifndef __rtems__
- error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag,
- sc->txbuf_map[idx].map, m, &seg, &nsegs, BUS_DMA_NOWAIT);
-#else /* __rtems__ */
- error = dwc_get_segs_for_tx(m, segs, &nsegs);
-#endif /* __rtems__ */
- }
+ m, &seg, &nsegs, 0);
if (error != 0) {
- /* Give up. */
- m_freem(m);
- return;
+ return (ENOMEM);
}
- sc->txbuf_map[idx].mbuf = m;
+ KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
-#ifndef __rtems__
bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map,
BUS_DMASYNC_PREWRITE);
-#endif /* __rtems__ */
- dwc_setup_txdesc(sc, m->m_pkthdr.csum_flags, idx, segs, nsegs);
+ sc->txbuf_map[idx].mbuf = m;
+
+ dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len);
- ETHER_BPF_MTAP(sc->ifp, m);
- *start_tx = 1;
+ return (0);
}
static void
@@ -380,7 +247,7 @@ dwc_txstart_locked(struct dwc_softc *sc)
{
struct ifnet *ifp;
struct mbuf *m;
- int start_tx;
+ int enqueued;
DWC_ASSERT_LOCKED(sc);
@@ -389,10 +256,14 @@ dwc_txstart_locked(struct dwc_softc *sc)
ifp = sc->ifp;
- start_tx = 0;
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
+ return;
+ }
+
+ enqueued = 0;
for (;;) {
- if (sc->txcount >= (TX_DESC_COUNT - 1 - TX_MAX_DMA_SEGS)) {
+ if (sc->txcount == (TX_DESC_COUNT-1)) {
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
break;
}
@@ -400,11 +271,16 @@ dwc_txstart_locked(struct dwc_softc *sc)
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
if (m == NULL)
break;
-
- dwc_setup_txbuf(sc, m, &start_tx);
+ if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ break;
+ }
+ BPF_MTAP(ifp, m);
+ sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head);
+ ++enqueued;
}
- if (start_tx != 0) {
+ if (enqueued != 0) {
WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1);
sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS;
}
@@ -416,8 +292,7 @@ dwc_txstart(struct ifnet *ifp)
struct dwc_softc *sc = ifp->if_softc;
DWC_LOCK(sc);
- if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0)
- dwc_txstart_locked(sc);
+ dwc_txstart_locked(sc);
DWC_UNLOCK(sc);
}
@@ -425,7 +300,7 @@ static void
dwc_stop_locked(struct dwc_softc *sc)
{
struct ifnet *ifp;
- int reg;
+ uint32_t reg;
DWC_ASSERT_LOCKED(sc);
@@ -459,7 +334,7 @@ dwc_stop_locked(struct dwc_softc *sc)
static void dwc_clear_stats(struct dwc_softc *sc)
{
- int reg;
+ uint32_t reg;
reg = READ4(sc, MMC_CONTROL);
reg |= (MMC_CONTROL_CNTRST);
@@ -478,7 +353,6 @@ dwc_harvest_stats(struct dwc_softc *sc)
sc->stats_harvest_count = 0;
ifp = sc->ifp;
-#ifndef __rtems__
if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB));
if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G));
if_inc_counter(ifp, IFCOUNTER_IERRORS,
@@ -495,24 +369,6 @@ dwc_harvest_stats(struct dwc_softc *sc)
if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL));
-#else /* __rtems__ */
- ifp->if_ipackets += READ4(sc, RXFRAMECOUNT_GB);
- ifp->if_imcasts += READ4(sc, RXMULTICASTFRAMES_G);
- ifp->if_ierrors +=
- READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) +
- READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) +
- READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) +
- READ4(sc, RXLENGTHERROR);
-
- ifp->if_opackets += READ4(sc, TXFRAMECOUNT_G);
- ifp->if_omcasts += READ4(sc, TXMULTICASTFRAMES_G);
- ifp->if_oerrors +=
- READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) +
- READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR);
-
- ifp->if_collisions +=
- READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL);
-#endif /* __rtems__ */
dwc_clear_stats(sc);
}
@@ -561,7 +417,7 @@ static void
dwc_init_locked(struct dwc_softc *sc)
{
struct ifnet *ifp = sc->ifp;
- int reg;
+ uint32_t reg;
DWC_ASSERT_LOCKED(sc);
@@ -588,7 +444,6 @@ dwc_init_locked(struct dwc_softc *sc)
/* Enable transmitters */
reg = READ4(sc, MAC_CONFIGURATION);
- reg |= (CONF_IPC);
reg |= (CONF_JD | CONF_ACS | CONF_BE);
reg |= (CONF_TE | CONF_RE);
WRITE4(sc, MAC_CONFIGURATION, reg);
@@ -618,13 +473,12 @@ dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr)
sc->rxdesc_ring[idx].addr = (uint32_t)paddr;
nidx = next_rxidx(sc, idx);
-#ifndef __rtems__
sc->rxdesc_ring[idx].addr_next = sc->rxdesc_ring_paddr + \
(nidx * sizeof(struct dwc_hwdesc));
-#else /* __rtems__ */
- sc->rxdesc_ring[idx].addr_next = (uint32_t)&sc->rxdesc_ring[nidx];
-#endif /* __rtems__ */
- sc->rxdesc_ring[idx].tdes1 = DDESC_RDES1_CHAINED | MCLBYTES;
+ if (sc->mactype == DWC_GMAC_ALT_DESC)
+ sc->rxdesc_ring[idx].tdes1 = DDESC_CNTL_CHAINED | RX_MAX_PACKET;
+ else
+ sc->rxdesc_ring[idx].tdes1 = DDESC_RDES1_CHAINED | MCLBYTES;
wmb();
sc->rxdesc_ring[idx].tdes0 = DDESC_RDES0_OWN;
@@ -636,14 +490,11 @@ dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr)
static int
dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m)
{
- bus_dma_segment_t seg;
-#ifndef __rtems__
+ struct bus_dma_segment seg;
int error, nsegs;
-#endif /* __rtems__ */
m_adj(m, ETHER_ALIGN);
-#ifndef __rtems__
error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
m, &seg, &nsegs, 0);
if (error != 0) {
@@ -654,10 +505,6 @@ dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m)
bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
BUS_DMASYNC_PREREAD);
-#else /* __rtems__ */
- rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
- seg.ds_addr = mtod(m, bus_addr_t);
-#endif /* __rtems__ */
sc->rxbuf_map[idx].mbuf = m;
dwc_setup_rxdesc(sc, idx, seg.ds_addr);
@@ -743,27 +590,26 @@ dwc_setup_rxfilter(struct dwc_softc *sc)
{
struct ifmultiaddr *ifma;
struct ifnet *ifp;
- uint8_t *eaddr;
- uint32_t crc;
- uint8_t val;
- int hashbit;
- int hashreg;
- int ffval;
- int reg;
- int lo;
- int hi;
+ uint8_t *eaddr, val;
+ uint32_t crc, ffval, hashbit, hashreg, hi, lo, hash[8];
+ int nhash, i;
DWC_ASSERT_LOCKED(sc);
ifp = sc->ifp;
+ nhash = sc->mactype == DWC_GMAC_ALT_DESC ? 2 : 8;
/*
* Set the multicast (group) filter hash.
*/
- if ((ifp->if_flags & IFF_ALLMULTI))
+ if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
ffval = (FRAME_FILTER_PM);
- else {
+ for (i = 0; i < nhash; i++)
+ hash[i] = ~0;
+ } else {
ffval = (FRAME_FILTER_HMC);
+ for (i = 0; i < nhash; i++)
+ hash[i] = 0;
if_maddr_rlock(ifp);
TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
if (ifma->ifma_addr->sa_family != AF_LINK)
@@ -773,12 +619,11 @@ dwc_setup_rxfilter(struct dwc_softc *sc)
/* Take lower 8 bits and reverse it */
val = bitreverse(~crc & 0xff);
+ if (sc->mactype == DWC_GMAC_ALT_DESC)
+ val >>= nhash; /* Only need lower 6 bits */
hashreg = (val >> 5);
hashbit = (val & 31);
-
- reg = READ4(sc, HASH_TABLE_REG(hashreg));
- reg |= (1 << hashbit);
- WRITE4(sc, HASH_TABLE_REG(hashreg), reg);
+ hash[hashreg] |= (1 << hashbit);
}
if_maddr_runlock(ifp);
}
@@ -799,6 +644,13 @@ dwc_setup_rxfilter(struct dwc_softc *sc)
WRITE4(sc, MAC_ADDRESS_LOW(0), lo);
WRITE4(sc, MAC_ADDRESS_HIGH(0), hi);
WRITE4(sc, MAC_FRAME_FILTER, ffval);
+ if (sc->mactype == DWC_GMAC_ALT_DESC) {
+ WRITE4(sc, GMAC_MAC_HTLOW, hash[0]);
+ WRITE4(sc, GMAC_MAC_HTHIGH, hash[1]);
+ } else {
+ for (i = 0; i < nhash; i++)
+ WRITE4(sc, HASH_TABLE_REG(i), hash[i]);
+ }
}
static int
@@ -866,28 +718,27 @@ dwc_txfinish_locked(struct dwc_softc *sc)
{
struct dwc_bufmap *bmap;
struct dwc_hwdesc *desc;
+ struct ifnet *ifp;
DWC_ASSERT_LOCKED(sc);
+ ifp = sc->ifp;
while (sc->tx_idx_tail != sc->tx_idx_head) {
desc = &sc->txdesc_ring[sc->tx_idx_tail];
if ((desc->tdes0 & DDESC_TDES0_OWN) != 0)
break;
bmap = &sc->txbuf_map[sc->tx_idx_tail];
-#ifndef __rtems__
bus_dmamap_sync(sc->txbuf_tag, bmap->map,
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->txbuf_tag, bmap->map);
-#endif /* __rtems__ */
m_freem(bmap->mbuf);
bmap->mbuf = NULL;
- --sc->txcount;
- sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail, 1);
+ dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0);
+ sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail);
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
}
- sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
- dwc_txstart_locked(sc);
-
/* If there are no buffers outstanding, muzzle the watchdog. */
if (sc->tx_idx_tail == sc->tx_idx_head) {
sc->tx_watchdog_count = 0;
@@ -900,11 +751,8 @@ dwc_rxfinish_locked(struct dwc_softc *sc)
struct ifnet *ifp;
struct mbuf *m0;
struct mbuf *m;
- int error;
+ int error, idx, len;
uint32_t rdes0;
- uint32_t rdes4;
- int idx;
- int len;
ifp = sc->ifp;
@@ -915,78 +763,20 @@ dwc_rxfinish_locked(struct dwc_softc *sc)
if ((rdes0 & DDESC_RDES0_OWN) != 0)
break;
- sc->rx_idx = next_rxidx(sc, idx);
-
- m = sc->rxbuf_map[idx].mbuf;
-
- m0 = dwc_alloc_mbufcl(sc);
- if (m0 == NULL) {
- m0 = m;
-
- /* Account for m_adj() in dwc_setup_rxbuf() */
- m0->m_data = m0->m_ext.ext_buf;
- }
-
- if ((error = dwc_setup_rxbuf(sc, idx, m0)) != 0) {
- /*
- * XXX Now what?
- * We've got a hole in the rx ring.
- */
- }
-
- if (m0 == m) {
- /* Discard frame and continue */
-#ifndef __rtems__
- if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1);
-#else /* __rtems__ */
- ++ifp->if_iqdrops;
-#endif /* __rtems__ */
- continue;
- }
-
-#ifndef __rtems__
bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->rxbuf_tag, sc->rxbuf_map[idx].map);
-#endif /* __rtems__ */
len = (rdes0 >> DDESC_RDES0_FL_SHIFT) & DDESC_RDES0_FL_MASK;
if (len != 0) {
+ m = sc->rxbuf_map[idx].mbuf;
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = len;
m->m_len = len;
-
- /* Check checksum offload flags. */
- if ((rdes0 & DDESC_RDES0_ESA) != 0) {
- rdes4 = sc->rxdesc_ring[idx].tdes4;
-
- /* TCP or UDP checks out, IP checks out too. */
- if ((rdes4 & DDESC_RDES4_IP_PYL_TYPE_MSK) ==
- DDESC_RDES4_IP_PYL_UDP ||
- (rdes4 & DDESC_RDES4_IP_PYL_TYPE_MSK) ==
- DDESC_RDES4_IP_PYL_TCP) {
- m->m_pkthdr.csum_flags |=
- CSUM_IP_CHECKED |
- CSUM_IP_VALID |
- CSUM_DATA_VALID |
- CSUM_PSEUDO_HDR;
- m->m_pkthdr.csum_data = 0xffff;
- } else if ((rdes4 & (DDESC_RDES4_IP_PYL_ERR |
- DDESC_RDES4_IP_HDR_ERR)) == 0) {
- /* Only IP checks out. */
- m->m_pkthdr.csum_flags |=
- CSUM_IP_CHECKED |
- CSUM_IP_VALID;
- m->m_pkthdr.csum_data = 0xffff;
- }
- }
-
-#ifndef __rtems__
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
-#else /* __rtems__ */
- ++ifp->if_ipackets;
- rtems_cache_invalidate_multiple_data_lines(m->m_data, m->m_len);
-#endif /* __rtems__ */
+
+ /* Remove trailing FCS */
+ m_adj(m, -ETHER_CRC_LEN);
DWC_UNLOCK(sc);
(*ifp->if_input)(ifp, m);
@@ -994,6 +784,18 @@ dwc_rxfinish_locked(struct dwc_softc *sc)
} else {
/* XXX Zero-length packet ? */
}
+
+ if ((m0 = dwc_alloc_mbufcl(sc)) != NULL) {
+ if ((error = dwc_setup_rxbuf(sc, idx, m0)) != 0) {
+ /*
+ * XXX Now what?
+ * We've got a hole in the rx ring.
+ */
+ }
+ } else
+ if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1);
+
+ sc->rx_idx = next_rxidx(sc, sc->rx_idx);
}
}
@@ -1008,28 +810,31 @@ dwc_intr(void *arg)
DWC_LOCK(sc);
reg = READ4(sc, INTERRUPT_STATUS);
- if (reg) {
- mii_mediachg(sc->mii_softc);
+ if (reg)
READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS);
- }
reg = READ4(sc, DMA_STATUS);
- WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK);
-
- if (reg & (DMA_STATUS_RI | DMA_STATUS_RU))
- dwc_rxfinish_locked(sc);
+ if (reg & DMA_STATUS_NIS) {
+ if (reg & DMA_STATUS_RI)
+ dwc_rxfinish_locked(sc);
- if (reg & DMA_STATUS_TI)
- dwc_txfinish_locked(sc);
+ if (reg & DMA_STATUS_TI) {
+ dwc_txfinish_locked(sc);
+ dwc_txstart_locked(sc);
+ }
+ }
- if (reg & DMA_STATUS_FBI) {
- /* Fatal bus error */
- device_printf(sc->dev,
- "Ethernet DMA error, restarting controller.\n");
- dwc_stop_locked(sc);
- dwc_init_locked(sc);
+ if (reg & DMA_STATUS_AIS) {
+ if (reg & DMA_STATUS_FBI) {
+ /* Fatal bus error */
+ device_printf(sc->dev,
+ "Ethernet DMA error, restarting controller.\n");
+ dwc_stop_locked(sc);
+ dwc_init_locked(sc);
+ }
}
+ WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK);
DWC_UNLOCK(sc);
}
@@ -1070,7 +875,6 @@ setup_dma(struct dwc_softc *sc)
goto out;
}
-#ifndef __rtems__
error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map,
sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr,
&sc->txdesc_ring_paddr, 0);
@@ -1079,30 +883,20 @@ setup_dma(struct dwc_softc *sc)
"could not load TX descriptor ring map.\n");
goto out;
}
-#endif /* __rtems__ */
for (idx = 0; idx < TX_DESC_COUNT; idx++) {
- sc->txdesc_ring[idx].addr = 0;
- sc->txdesc_ring[idx].tdes0 = DDESC_TDES0_TXCHAIN;
- sc->txdesc_ring[idx].tdes1 = 0;
- nidx = next_txidx(sc, idx, 1);
-#ifndef __rtems__
- sc->txdesc_ring[idx].addr_next = sc->txdesc_ring_paddr + \
+ nidx = next_txidx(sc, idx);
+ sc->txdesc_ring[idx].addr_next = sc->txdesc_ring_paddr +
(nidx * sizeof(struct dwc_hwdesc));
-#else /* __rtems__ */
- sc->txdesc_ring[idx].addr_next =
- (uint32_t)&sc->txdesc_ring[nidx];
-#endif /* __rtems__ */
}
-#ifndef __rtems__
error = bus_dma_tag_create(
bus_get_dma_tag(sc->dev), /* Parent tag. */
1, 0, /* alignment, boundary */
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
- MCLBYTES, TX_MAX_DMA_SEGS, /* maxsize, nsegments */
+ MCLBYTES, 1, /* maxsize, nsegments */
MCLBYTES, /* maxsegsize */
0, /* flags */
NULL, NULL, /* lockfunc, lockarg */
@@ -1112,10 +906,8 @@ setup_dma(struct dwc_softc *sc)
"could not create TX ring DMA tag.\n");
goto out;
}
-#endif /* __rtems__ */
for (idx = 0; idx < TX_DESC_COUNT; idx++) {
-#ifndef __rtems__
error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT,
&sc->txbuf_map[idx].map);
if (error != 0) {
@@ -1123,7 +915,7 @@ setup_dma(struct dwc_softc *sc)
"could not create TX buffer DMA map.\n");
goto out;
}
-#endif /* __rtems__ */
+ dwc_setup_txdesc(sc, idx, 0, 0);
}
/*
@@ -1155,7 +947,6 @@ setup_dma(struct dwc_softc *sc)
goto out;
}
-#ifndef __rtems__
error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map,
sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr,
&sc->rxdesc_ring_paddr, 0);
@@ -1181,10 +972,8 @@ setup_dma(struct dwc_softc *sc)
"could not create RX buf DMA tag.\n");
goto out;
}
-#endif /* __rtems__ */
for (idx = 0; idx < RX_DESC_COUNT; idx++) {
-#ifndef __rtems__
error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT,
&sc->rxbuf_map[idx].map);
if (error != 0) {
@@ -1192,7 +981,6 @@ setup_dma(struct dwc_softc *sc)
"could not create RX buffer DMA map.\n");
goto out;
}
-#endif /* __rtems__ */
if ((m = dwc_alloc_mbufcl(sc)) == NULL) {
device_printf(sc->dev, "Could not alloc mbuf\n");
error = ENOMEM;
@@ -1203,7 +991,6 @@ setup_dma(struct dwc_softc *sc)
"could not create new RX buffer.\n");
goto out;
}
- sc->rxdesc_ring[idx].tdes4 = 0;
}
out:
@@ -1216,11 +1003,7 @@ out:
static int
dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
{
-#ifndef __rtems__
- int rnd;
-#endif /* __rtems__ */
- int lo;
- int hi;
+ uint32_t hi, lo, rnd;
/*
* Try to recover a MAC address from the running hardware. If there's
@@ -1241,7 +1024,6 @@ dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
hwaddr[4] = (hi >> 0) & 0xff;
hwaddr[5] = (hi >> 8) & 0xff;
} else {
-#ifndef __rtems__
rnd = arc4random() & 0x00ffffff;
hwaddr[0] = 'b';
hwaddr[1] = 's';
@@ -1249,14 +1031,98 @@ dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
hwaddr[3] = rnd >> 16;
hwaddr[4] = rnd >> 8;
hwaddr[5] = rnd >> 0;
-#else /* __rtems__ */
- rtems_bsd_get_mac_address(device_get_name(sc->dev),
- device_get_unit(sc->dev), hwaddr);
+ }
+
+ return (0);
+}
+
+#define GPIO_ACTIVE_LOW 1
+
+static int
+dwc_reset(device_t dev)
+{
+#ifndef __rtems__
+ pcell_t gpio_prop[4];
+ pcell_t delay_prop[3];
+ phandle_t node, gpio_node;
+ device_t gpio;
+ uint32_t pin, flags;
+ uint32_t pin_value;
+
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "snps,reset-gpio",
+ gpio_prop, sizeof(gpio_prop)) <= 0)
+ return (0);
+
+ if (OF_getencprop(node, "snps,reset-delays-us",
+ delay_prop, sizeof(delay_prop)) <= 0) {
+ device_printf(dev,
+ "Wrong property for snps,reset-delays-us");
+ return (ENXIO);
+ }
+
+ gpio_node = OF_node_from_xref(gpio_prop[0]);
+ if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) {
+ device_printf(dev,
+ "Can't find gpio controller for phy reset\n");
+ return (ENXIO);
+ }
+
+ if (GPIO_MAP_GPIOS(gpio, node, gpio_node,
+ nitems(gpio_prop) - 1,
+ gpio_prop + 1, &pin, &flags) != 0) {
+ device_printf(dev, "Can't map gpio for phy reset\n");
+ return (ENXIO);
+ }
+
+ pin_value = GPIO_PIN_LOW;
+ if (OF_hasprop(node, "snps,reset-active-low"))
+ pin_value = GPIO_PIN_HIGH;
+
+ if (flags & GPIO_ACTIVE_LOW)
+ pin_value = !pin_value;
+
+ GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT);
+ GPIO_PIN_SET(gpio, pin, pin_value);
+ DELAY(delay_prop[0]);
+ GPIO_PIN_SET(gpio, pin, !pin_value);
+ DELAY(delay_prop[1]);
+ GPIO_PIN_SET(gpio, pin, pin_value);
+ DELAY(delay_prop[2]);
#endif /* __rtems__ */
+
+ return (0);
+}
+
+#ifdef EXT_RESOURCES
+static int
+dwc_clock_init(device_t dev)
+{
+ hwreset_t rst;
+ clk_t clk;
+ int error;
+
+ /* Enable clock */
+ if (clk_get_by_ofw_name(dev, 0, "stmmaceth", &clk) == 0) {
+ error = clk_enable(clk);
+ if (error != 0) {
+ device_printf(dev, "could not enable main clock\n");
+ return (error);
+ }
+ }
+
+ /* De-assert reset */
+ if (hwreset_get_by_ofw_name(dev, 0, "stmmaceth", &rst) == 0) {
+ error = hwreset_deassert(rst);
+ if (error != 0) {
+ device_printf(dev, "could not de-assert reset\n");
+ return (error);
+ }
}
return (0);
}
+#endif
static int
dwc_probe(device_t dev)
@@ -1280,15 +1146,23 @@ dwc_attach(device_t dev)
uint8_t macaddr[ETHER_ADDR_LEN];
struct dwc_softc *sc;
struct ifnet *ifp;
- int error;
- int reg;
- int i;
+ int error, i;
+ uint32_t reg;
sc = device_get_softc(dev);
sc->dev = dev;
- sc->mii_clk = MII_CLK_VAL;
sc->rx_idx = 0;
- sc->txcount = 0;
+ sc->txcount = TX_DESC_COUNT;
+ sc->mii_clk = IF_DWC_MII_CLK(dev);
+ sc->mactype = IF_DWC_MAC_TYPE(dev);
+
+ if (IF_DWC_INIT(dev) != 0)
+ return (ENXIO);
+
+#ifdef EXT_RESOURCES
+ if (dwc_clock_init(dev) != 0)
+ return (ENXIO);
+#endif
if (bus_alloc_resources(dev, dwc_spec, sc->res)) {
device_printf(dev, "could not allocate resources\n");
@@ -1305,6 +1179,12 @@ dwc_attach(device_t dev)
return (ENXIO);
}
+ /* Reset the PHY if needed */
+ if (dwc_reset(dev) != 0) {
+ device_printf(dev, "Can't reset the PHY\n");
+ return (ENXIO);
+ }
+
/* Reset */
reg = READ4(sc, BUS_MODE);
reg |= (BUS_MODE_SWR);
@@ -1320,9 +1200,11 @@ dwc_attach(device_t dev)
return (ENXIO);
}
- reg = READ4(sc, BUS_MODE);
- reg |= (BUS_MODE_ATDS);
- reg |= (BUS_MODE_EIGHTXPBL);
+ if (sc->mactype == DWC_GMAC_ALT_DESC) {
+ reg = BUS_MODE_FIXEDBURST;
+ reg |= (BUS_MODE_PRIORXTX_41 << BUS_MODE_PRIORXTX_SHIFT);
+ } else
+ reg = (BUS_MODE_EIGHTXPBL);
reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT);
WRITE4(sc, BUS_MODE, reg);
@@ -1337,36 +1219,36 @@ dwc_attach(device_t dev)
return (ENXIO);
/* Setup addresses */
-#ifndef __rtems__
WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr);
WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr);
-#else /* __rtems__ */
- WRITE4(sc, RX_DESCR_LIST_ADDR, (uint32_t)&sc->rxdesc_ring[0]);
- WRITE4(sc, TX_DESCR_LIST_ADDR, (uint32_t)&sc->txdesc_ring[0]);
-#endif /* __rtems__ */
mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
MTX_NETWORK_LOCK, MTX_DEF);
callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0);
+ /* Setup interrupt handler. */
+ error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
+ NULL, dwc_intr, sc, &sc->intr_cookie);
+ if (error != 0) {
+ device_printf(dev, "could not setup interrupt handler.\n");
+ return (ENXIO);
+ }
+
/* Set up the ethernet interface. */
sc->ifp = ifp = if_alloc(IFT_ETHER);
ifp->if_softc = sc;
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
- ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6 |
- IFCAP_VLAN_MTU | IFCAP_VLAN_HWCSUM;
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
ifp->if_capenable = ifp->if_capabilities;
- ifp->if_hwassist = DWC_CKSUM_ASSIST;
ifp->if_start = dwc_txstart;
ifp->if_ioctl = dwc_ioctl;
ifp->if_init = dwc_init;
IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
IFQ_SET_READY(&ifp->if_snd);
- ifp->if_hdrlen = sizeof(struct ether_vlan_header);
/* Attach the mii driver. */
error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change,
@@ -1379,14 +1261,6 @@ dwc_attach(device_t dev)
}
sc->mii_softc = device_get_softc(sc->miibus);
- /* Setup interrupt handler. */
- error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
- NULL, dwc_intr, sc, &sc->intr_cookie);
- if (error != 0) {
- device_printf(dev, "could not setup interrupt handler.\n");
- return (ENXIO);
- }
-
/* All ready to run, attach the ethernet interface. */
ether_ifattach(ifp, macaddr);
sc->is_attached = true;
@@ -1454,7 +1328,7 @@ dwc_miibus_statchg(device_t dev)
{
struct dwc_softc *sc;
struct mii_data *mii;
- int reg;
+ uint32_t reg;
/*
* Called by the MII bus driver when the PHY establishes
@@ -1513,7 +1387,7 @@ static device_method_t dwc_methods[] = {
{ 0, 0 }
};
-static driver_t dwc_driver = {
+driver_t dwc_driver = {
"dwc",
dwc_methods,
sizeof(struct dwc_softc),
@@ -1521,11 +1395,7 @@ static driver_t dwc_driver = {
static devclass_t dwc_devclass;
-#ifndef __rtems__
DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0);
-#else /* __rtems__ */
-DRIVER_MODULE(dwc, nexus, dwc_driver, dwc_devclass, 0, 0);
-#endif /* __rtems__ */
DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0);
MODULE_DEPEND(dwc, ether, 1, 1, 1);