Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/dwc/if_dwc.c
Show First 20 Lines • Show All 621 Lines • ▼ Show 20 Lines | dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) | ||||
if (error != 0) | if (error != 0) | ||||
return; | return; | ||||
*(bus_addr_t *)arg = segs[0].ds_addr; | *(bus_addr_t *)arg = segs[0].ds_addr; | ||||
} | } | ||||
inline static void | inline static void | ||||
dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, | dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, | ||||
uint32_t len, uint32_t flags) | uint32_t len, uint32_t flags, bool first, bool last) | ||||
{ | { | ||||
uint32_t desc0, desc1; | uint32_t desc0, desc1; | ||||
/* Addr/len 0 means we're clearing the descriptor after xmit done. */ | /* Addr/len 0 means we're clearing the descriptor after xmit done. */ | ||||
if (paddr == 0 || len == 0) { | if (paddr == 0 || len == 0) { | ||||
desc0 = 0; | desc0 = 0; | ||||
desc1 = 0; | desc1 = 0; | ||||
--sc->txcount; | --sc->tx_desccount; | ||||
} else { | } else { | ||||
if (sc->mactype != DWC_GMAC_EXT_DESC) { | if (sc->mactype != DWC_GMAC_EXT_DESC) { | ||||
desc0 = 0; | desc0 = 0; | ||||
desc1 = NTDESC1_TCH | NTDESC1_FS | NTDESC1_LS | | desc1 = NTDESC1_TCH | len | flags; | ||||
NTDESC1_IC | len | flags; | if (first) | ||||
desc1 |= NTDESC1_FS; | |||||
if (last) | |||||
desc1 |= NTDESC1_LS | NTDESC1_IC; | |||||
} else { | } else { | ||||
desc0 = ETDESC0_TCH | ETDESC0_FS | ETDESC0_LS | | desc0 = ETDESC0_TCH | flags; | ||||
ETDESC0_IC | flags; | if (first) | ||||
desc0 |= ETDESC0_FS; | |||||
if (last) | |||||
desc0 |= ETDESC0_LS | ETDESC0_IC; | |||||
desc1 = len; | desc1 = len; | ||||
} | } | ||||
++sc->txcount; | ++sc->tx_desccount; | ||||
} | } | ||||
sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); | sc->txdesc_ring[idx].addr1 = (uint32_t)(paddr); | ||||
sc->txdesc_ring[idx].desc0 = desc0; | sc->txdesc_ring[idx].desc0 = desc0; | ||||
sc->txdesc_ring[idx].desc1 = desc1; | sc->txdesc_ring[idx].desc1 = desc1; | ||||
} | |||||
if (paddr && len) { | inline static void | ||||
dwc_set_owner(struct dwc_softc *sc, int idx) | |||||
{ | |||||
wmb(); | wmb(); | ||||
sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; | sc->txdesc_ring[idx].desc0 |= TDESC0_OWN; | ||||
wmb(); | wmb(); | ||||
} | } | ||||
} | |||||
static int | static int | ||||
dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) | dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) | ||||
{ | { | ||||
struct bus_dma_segment seg; | struct bus_dma_segment segs[TX_MAP_MAX_SEGS]; | ||||
int error, nsegs; | int error, nsegs; | ||||
struct mbuf * m; | struct mbuf * m; | ||||
uint32_t flags = 0; | uint32_t flags = 0; | ||||
int i; | |||||
int first, last; | |||||
error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, | |||||
*mp, segs, &nsegs, 0); | |||||
if (error == EFBIG) { | |||||
/* | |||||
* The map may be partially mapped from the first call. | |||||
* Make sure to reset it. | |||||
*/ | |||||
bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); | |||||
if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) | if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
*mp = m; | *mp = m; | ||||
error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, | error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, | ||||
m, &seg, &nsegs, 0); | *mp, segs, &nsegs, 0); | ||||
if (error != 0) { | } | ||||
if (error != 0) | |||||
return (ENOMEM); | return (ENOMEM); | ||||
if (sc->tx_desccount + nsegs > TX_DESC_COUNT) { | |||||
bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map); | |||||
return (ENOMEM); | |||||
} | } | ||||
KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); | m = *mp; | ||||
bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, | |||||
BUS_DMASYNC_PREWRITE); | |||||
sc->txbuf_map[idx].mbuf = m; | |||||
if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { | if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) { | ||||
if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { | if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) { | ||||
if (sc->mactype != DWC_GMAC_EXT_DESC) | if (sc->mactype != DWC_GMAC_EXT_DESC) | ||||
flags = NTDESC1_CIC_FULL; | flags = NTDESC1_CIC_FULL; | ||||
else | else | ||||
flags = ETDESC0_CIC_FULL; | flags = ETDESC0_CIC_FULL; | ||||
} else { | } else { | ||||
if (sc->mactype != DWC_GMAC_EXT_DESC) | if (sc->mactype != DWC_GMAC_EXT_DESC) | ||||
flags = NTDESC1_CIC_HDR; | flags = NTDESC1_CIC_HDR; | ||||
else | else | ||||
flags = ETDESC0_CIC_HDR; | flags = ETDESC0_CIC_HDR; | ||||
} | } | ||||
} | } | ||||
dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len, flags); | bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, | ||||
BUS_DMASYNC_PREWRITE); | |||||
sc->txbuf_map[idx].mbuf = m; | |||||
first = sc->tx_desc_head; | |||||
for (i = 0; i < nsegs; i++) { | |||||
dwc_setup_txdesc(sc, sc->tx_desc_head, | |||||
segs[i].ds_addr, segs[i].ds_len, | |||||
(i == 0) ? flags : 0, /* only first desc needs flags */ | |||||
(i == 0), | |||||
(i == nsegs - 1)); | |||||
if (i > 0) | |||||
dwc_set_owner(sc, sc->tx_desc_head); | |||||
last = sc->tx_desc_head; | |||||
sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head); | |||||
} | |||||
sc->txbuf_map[idx].last_desc_idx = last; | |||||
dwc_set_owner(sc, first); | |||||
return (0); | return (0); | ||||
} | } | ||||
inline static uint32_t | inline static uint32_t | ||||
dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) | dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) | ||||
{ | { | ||||
uint32_t nidx; | uint32_t nidx; | ||||
▲ Show 20 Lines • Show All 183 Lines • ▼ Show 20 Lines | setup_dma(struct dwc_softc *sc) | ||||
} | } | ||||
error = bus_dma_tag_create( | error = bus_dma_tag_create( | ||||
bus_get_dma_tag(sc->dev), /* Parent tag. */ | bus_get_dma_tag(sc->dev), /* Parent tag. */ | ||||
1, 0, /* alignment, boundary */ | 1, 0, /* alignment, boundary */ | ||||
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | ||||
BUS_SPACE_MAXADDR, /* highaddr */ | BUS_SPACE_MAXADDR, /* highaddr */ | ||||
NULL, NULL, /* filter, filterarg */ | NULL, NULL, /* filter, filterarg */ | ||||
MCLBYTES, 1, /* maxsize, nsegments */ | MCLBYTES*TX_MAP_MAX_SEGS, /* maxsize */ | ||||
TX_MAP_MAX_SEGS, /* nsegments */ | |||||
MCLBYTES, /* maxsegsize */ | MCLBYTES, /* maxsegsize */ | ||||
0, /* flags */ | 0, /* flags */ | ||||
NULL, NULL, /* lockfunc, lockarg */ | NULL, NULL, /* lockfunc, lockarg */ | ||||
&sc->txbuf_tag); | &sc->txbuf_tag); | ||||
if (error != 0) { | if (error != 0) { | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"could not create TX ring DMA tag.\n"); | "could not create TX ring DMA tag.\n"); | ||||
goto out; | goto out; | ||||
} | } | ||||
for (idx = 0; idx < TX_DESC_COUNT; idx++) { | for (idx = 0; idx < TX_MAP_COUNT; idx++) { | ||||
error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, | error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, | ||||
&sc->txbuf_map[idx].map); | &sc->txbuf_map[idx].map); | ||||
if (error != 0) { | if (error != 0) { | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"could not create TX buffer DMA map.\n"); | "could not create TX buffer DMA map.\n"); | ||||
goto out; | goto out; | ||||
} | } | ||||
dwc_setup_txdesc(sc, idx, 0, 0, 0); | |||||
} | } | ||||
for (idx = 0; idx < TX_DESC_COUNT; idx++) | |||||
dwc_setup_txdesc(sc, idx, 0, 0, 0, false, false); | |||||
/* | /* | ||||
* Set up RX descriptor ring, descriptors, dma maps, and mbufs. | * Set up RX descriptor ring, descriptors, dma maps, and mbufs. | ||||
*/ | */ | ||||
error = bus_dma_tag_create( | error = bus_dma_tag_create( | ||||
bus_get_dma_tag(sc->dev), /* Parent tag. */ | bus_get_dma_tag(sc->dev), /* Parent tag. */ | ||||
DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ | DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ | ||||
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | ||||
BUS_SPACE_MAXADDR, /* highaddr */ | BUS_SPACE_MAXADDR, /* highaddr */ | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | dwc_txstart_locked(struct dwc_softc *sc) | ||||
if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != | if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != | ||||
IFF_DRV_RUNNING) | IFF_DRV_RUNNING) | ||||
return; | return; | ||||
enqueued = 0; | enqueued = 0; | ||||
for (;;) { | for (;;) { | ||||
if (sc->txcount == (TX_DESC_COUNT - 1)) { | if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) { | ||||
if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); | if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); | ||||
break; | break; | ||||
} | } | ||||
if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) { | |||||
if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); | |||||
break; | |||||
} | |||||
m = if_dequeue(ifp); | m = if_dequeue(ifp); | ||||
if (m == NULL) | if (m == NULL) | ||||
break; | break; | ||||
if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { | if (dwc_setup_txbuf(sc, sc->tx_map_head, &m) != 0) { | ||||
if_sendq_prepend(ifp, m); | if_sendq_prepend(ifp, m); | ||||
if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); | |||||
break; | break; | ||||
} | } | ||||
if_bpfmtap(ifp, m); | if_bpfmtap(ifp, m); | ||||
sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); | sc->tx_map_head = next_txidx(sc, sc->tx_map_head); | ||||
sc->tx_mapcount++; | |||||
++enqueued; | ++enqueued; | ||||
} | } | ||||
if (enqueued != 0) { | if (enqueued != 0) { | ||||
WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); | WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); | ||||
sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; | sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
dwc_txfinish_locked(struct dwc_softc *sc) | dwc_txfinish_locked(struct dwc_softc *sc) | ||||
{ | { | ||||
struct dwc_bufmap *bmap; | struct dwc_bufmap *bmap; | ||||
struct dwc_hwdesc *desc; | struct dwc_hwdesc *desc; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int idx, last_idx; | |||||
bool map_finished; | |||||
DWC_ASSERT_LOCKED(sc); | DWC_ASSERT_LOCKED(sc); | ||||
ifp = sc->ifp; | ifp = sc->ifp; | ||||
while (sc->tx_idx_tail != sc->tx_idx_head) { | /* check if all descriptors of the map are done */ | ||||
desc = &sc->txdesc_ring[sc->tx_idx_tail]; | while (sc->tx_map_tail != sc->tx_map_head) { | ||||
if ((desc->desc0 & TDESC0_OWN) != 0) | map_finished = true; | ||||
bmap = &sc->txbuf_map[sc->tx_map_tail]; | |||||
idx = sc->tx_desc_tail; | |||||
last_idx = next_txidx(sc, bmap->last_desc_idx); | |||||
while (idx != last_idx) { | |||||
desc = &sc->txdesc_ring[idx]; | |||||
if ((desc->desc0 & TDESC0_OWN) != 0) { | |||||
map_finished = false; | |||||
break; | break; | ||||
bmap = &sc->txbuf_map[sc->tx_idx_tail]; | } | ||||
idx = next_txidx(sc, idx); | |||||
} | |||||
if (!map_finished) | |||||
break; | |||||
bus_dmamap_sync(sc->txbuf_tag, bmap->map, | bus_dmamap_sync(sc->txbuf_tag, bmap->map, | ||||
BUS_DMASYNC_POSTWRITE); | BUS_DMASYNC_POSTWRITE); | ||||
bus_dmamap_unload(sc->txbuf_tag, bmap->map); | bus_dmamap_unload(sc->txbuf_tag, bmap->map); | ||||
m_freem(bmap->mbuf); | m_freem(bmap->mbuf); | ||||
bmap->mbuf = NULL; | bmap->mbuf = NULL; | ||||
dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0, 0); | sc->tx_mapcount--; | ||||
sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); | while (sc->tx_desc_tail != last_idx) { | ||||
dwc_setup_txdesc(sc, sc->tx_desc_tail, 0, 0, 0, false, false); | |||||
sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail); | |||||
} | |||||
sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail); | |||||
if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); | if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); | ||||
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); | ||||
} | } | ||||
/* If there are no buffers outstanding, muzzle the watchdog. */ | /* If there are no buffers outstanding, muzzle the watchdog. */ | ||||
if (sc->tx_idx_tail == sc->tx_idx_head) { | if (sc->tx_desc_tail == sc->tx_desc_head) { | ||||
sc->tx_watchdog_count = 0; | sc->tx_watchdog_count = 0; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
dwc_rxfinish_locked(struct dwc_softc *sc) | dwc_rxfinish_locked(struct dwc_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
▲ Show 20 Lines • Show All 272 Lines • ▼ Show 20 Lines | dwc_attach(device_t dev) | ||||
phandle_t node; | phandle_t node; | ||||
uint32_t txpbl, rxpbl, pbl; | uint32_t txpbl, rxpbl, pbl; | ||||
bool nopblx8 = false; | bool nopblx8 = false; | ||||
bool fixed_burst = false; | bool fixed_burst = false; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->dev = dev; | sc->dev = dev; | ||||
sc->rx_idx = 0; | sc->rx_idx = 0; | ||||
sc->txcount = TX_DESC_COUNT; | sc->tx_desccount = TX_DESC_COUNT; | ||||
sc->tx_mapcount = 0; | |||||
sc->mii_clk = IF_DWC_MII_CLK(dev); | sc->mii_clk = IF_DWC_MII_CLK(dev); | ||||
sc->mactype = IF_DWC_MAC_TYPE(dev); | sc->mactype = IF_DWC_MAC_TYPE(dev); | ||||
node = ofw_bus_get_node(dev); | node = ofw_bus_get_node(dev); | ||||
if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_mode)) { | if (OF_getprop_alloc(node, "phy-mode", (void **)&phy_mode)) { | ||||
if (strcmp(phy_mode, "rgmii") == 0) | if (strcmp(phy_mode, "rgmii") == 0) | ||||
sc->phy_mode = PHY_MODE_RGMII; | sc->phy_mode = PHY_MODE_RGMII; | ||||
if (strcmp(phy_mode, "rmii") == 0) | if (strcmp(phy_mode, "rmii") == 0) | ||||
▲ Show 20 Lines • Show All 90 Lines • ▼ Show 20 Lines | #endif | ||||
sc->ifp = ifp = if_alloc(IFT_ETHER); | sc->ifp = ifp = if_alloc(IFT_ETHER); | ||||
ifp->if_softc = sc; | ifp->if_softc = sc; | ||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | ||||
if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); | if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); | ||||
if_setstartfn(ifp, dwc_txstart); | if_setstartfn(ifp, dwc_txstart); | ||||
if_setioctlfn(ifp, dwc_ioctl); | if_setioctlfn(ifp, dwc_ioctl); | ||||
if_setinitfn(ifp, dwc_init); | if_setinitfn(ifp, dwc_init); | ||||
if_setsendqlen(ifp, TX_DESC_COUNT - 1); | if_setsendqlen(ifp, TX_MAP_COUNT - 1); | ||||
if_setsendqready(sc->ifp); | if_setsendqready(sc->ifp); | ||||
if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); | if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP); | ||||
if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); | if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM); | ||||
if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); | if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp)); | ||||
/* Attach the mii driver. */ | /* Attach the mii driver. */ | ||||
error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, | error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, | ||||
dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, | dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, | ||||
Show All 40 Lines |