Date: Sat, 6 Sep 2008 19:27:15 GMT From: Rafal Jaworowski <raj@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 149350 for review Message-ID: <200809061927.m86JRFKG033027@repoman.freebsd.org>
index | next in thread | raw e-mail
http://perforce.freebsd.org/chv.cgi?CH=149350 Change 149350 by raj@raj_mimi on 2008/09/06 19:26:50 mge(4): Import updated version of the Ethernet controller driver. This brings the following advanced features: - multicast - VLAN tagging - IP/TCP/UDP checksum calculation offloading - polling - interrupt coalescing Obtained from: Marvell, Semihalf Affected files ... .. //depot/projects/arm/src/sys/dev/mge/if_mge.c#4 edit .. //depot/projects/arm/src/sys/dev/mge/if_mgevar.h#2 edit Differences ... ==== //depot/projects/arm/src/sys/dev/mge/if_mge.c#4 (text+ko) ==== @@ -29,6 +29,10 @@ * SUCH DAMAGE. */ +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -50,6 +54,11 @@ #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <netinet/in_systm.h> +#include <netinet/in.h> +#include <netinet/ip.h> #include <sys/sockio.h> #include <sys/bus.h> @@ -60,11 +69,20 @@ #include <dev/mii/mii.h> #include <dev/mii/miivar.h> +#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) +#define MGE_VER2 1 +#endif + +#define MV_PHY_ADDR_BASE 8 + #include <dev/mge/if_mgevar.h> #include <arm/mv/mvreg.h> #include "miibus_if.h" +/* PHY registers are in the address space of the first mge unit */ +static struct mge_softc *sc_mge0 = NULL; + static int mge_probe(device_t dev); static int mge_attach(device_t dev); static int mge_detach(device_t dev); @@ -85,8 +103,11 @@ static void mge_watchdog(struct mge_softc *sc); static int mge_ioctl(struct ifnet *ifp, u_long command, caddr_t data); +static void mge_intrs_ctrl(struct mge_softc *sc, int enable); static void mge_intr_rx(void *arg); +static void mge_intr_rx_locked(struct mge_softc *sc, int count); static void mge_intr_tx(void *arg); +static void mge_intr_tx_locked(struct mge_softc *sc); static void mge_intr_misc(void *arg); static void mge_intr_sum(void *arg); static void mge_intr_err(void *arg); @@ -107,7 +128,16 @@ static void mge_free_dma(struct mge_softc *sc); static void mge_free_desc(struct mge_softc *sc, struct mge_desc_wrapper* tab, uint32_t size, bus_dma_tag_t buffer_tag, uint8_t free_mbufs); - +static void mge_offload_process_frame(struct ifnet *ifp, struct mbuf *frame, + uint32_t status, uint16_t bufsize); +static void mge_offload_setup_descriptor(struct mge_softc *sc, + struct mge_desc_wrapper *dw); +static uint8_t mge_crc8(uint8_t *data, int size); +static void mge_setup_multicast(struct mge_softc *sc); +static void mge_set_rxic(struct mge_softc *sc); +static void mge_set_txic(struct mge_softc *sc); +static void mge_add_sysctls(struct mge_softc *sc); +static int mge_sysctl_ic(SYSCTL_HANDLER_ARGS); static device_method_t mge_methods[] = { /* Device interface */ @@ -182,8 +212,7 @@ char *if_mac; uint32_t mac_l, mac_h; - mtx_assert(&sc->transmit_lock, MA_OWNED); - mtx_assert(&sc->receive_lock, MA_OWNED); + MGE_GLOBAL_LOCK_ASSERT(sc); if_mac = (char *)IF_LLADDR(sc->ifp); @@ -203,15 +232,15 @@ uint32_t reg_idx, reg_off, reg_val, i; last_byte &= 0xf; - reg_idx = last_byte / 4; - reg_off = (last_byte % 4) * 8; + reg_idx = last_byte / MGE_UCAST_REG_NUMBER; + reg_off = (last_byte % MGE_UCAST_REG_NUMBER) * 8; reg_val = (1 | (queue << 1)) << reg_off; - for (i = 0; i < 4; i++) { + for (i = 0; i < MGE_UCAST_REG_NUMBER; i++) { if ( i == reg_idx) - MGE_WRITE(sc, MGE_DA_FILTER_UCAST + (i * 4), reg_val); + MGE_WRITE(sc, MGE_DA_FILTER_UCAST(i), reg_val); else - MGE_WRITE(sc, MGE_DA_FILTER_UCAST + (i * 4), 0); + MGE_WRITE(sc, MGE_DA_FILTER_UCAST(i), 0); } } @@ -235,8 +264,8 @@ MGE_WRITE(sc, MGE_DA_FILTER_OTH_MCAST(i), reg_val); } - for (i = 0; i < 4; i++) - MGE_WRITE(sc, MGE_DA_FILTER_UCAST + (i * 4), reg_val); + for (i = 0; i < MGE_UCAST_REG_NUMBER; i++) + MGE_WRITE(sc, MGE_DA_FILTER_UCAST(i), reg_val); } else { port_config = MGE_READ(sc, MGE_PORT_CONFIG); @@ -459,7 +488,8 @@ struct mge_desc_wrapper *dw; int i; - mtx_lock(&sc->receive_lock); + MGE_RECEIVE_LOCK(sc); + mge_free_desc(sc, sc->mge_rx_desc, MGE_RX_DESC_NUM, sc->mge_rx_dtag, 1); mge_alloc_desc_dma(sc, sc->mge_rx_desc, MGE_RX_DESC_NUM, @@ -480,8 +510,45 @@ /* Enable RX queue */ MGE_WRITE(sc, MGE_RX_QUEUE_CMD, MGE_ENABLE_RXQ(MGE_RX_DEFAULT_QUEUE)); - mtx_unlock(&sc->receive_lock); + MGE_RECEIVE_UNLOCK(sc); +} + +#ifdef DEVICE_POLLING +static poll_handler_t mge_poll; + +static void +mge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + struct mge_softc *sc = ifp->if_softc; + uint32_t int_cause, int_cause_ext; + + MGE_GLOBAL_LOCK(sc); + + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + MGE_GLOBAL_UNLOCK(sc); + return; + } + + if (cmd == POLL_AND_CHECK_STATUS) { + int_cause = MGE_READ(sc, MGE_PORT_INT_CAUSE); + int_cause_ext = MGE_READ(sc, MGE_PORT_INT_CAUSE_EXT); + + /* Check for resource error */ + if (int_cause & MGE_PORT_INT_RXERRQ0) + mge_reinit_rx(sc); + + if (int_cause || int_cause_ext) { + MGE_WRITE(sc, MGE_PORT_INT_CAUSE, ~int_cause); + MGE_WRITE(sc, MGE_PORT_INT_CAUSE_EXT, ~int_cause_ext); + } + } + + mge_intr_tx_locked(sc); + mge_intr_rx_locked(sc, count); + + MGE_GLOBAL_UNLOCK(sc); } +#endif /* DEVICE_POLLING */ static int mge_attach(device_t dev) @@ -494,6 +561,9 @@ sc = device_get_softc(dev); sc->dev = dev; + if (device_get_unit(dev) == 0) + sc_mge0 = sc; + /* Initialize mutexes */ mtx_init(&sc->transmit_lock, device_get_nameunit(dev), "mge TX lock", MTX_DEF); mtx_init(&sc->receive_lock, device_get_nameunit(dev), "mge RX lock", MTX_DEF); @@ -517,6 +587,11 @@ sc->rx_desc_curr = 0; sc->tx_desc_used_idx = 0; + /* Configure defaults for interrupts coalescing */ + sc->rx_ic_time = 768; + sc->tx_ic_time = 768; + mge_add_sysctls(sc); + /* Allocate network interface */ ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { @@ -527,23 +602,28 @@ if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; - ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST; - /* XXX for now IFCAP_RXCSUM is the only extra capability we support */ - ifp->if_capabilities = IFCAP_RXCSUM; + ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST; + ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; + ifp->if_hwassist = MGE_CHECKSUM_FEATURES; + +#ifdef DEVICE_POLLING + /* Advertise that polling is supported */ + ifp->if_capabilities |= IFCAP_POLLING; +#endif ifp->if_init = mge_init; ifp->if_start = mge_start; ifp->if_ioctl = mge_ioctl; + ifp->if_snd.ifq_drv_maxlen = MGE_TX_DESC_NUM - 1; + IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); + IFQ_SET_READY(&ifp->if_snd); + mge_get_mac_address(sc, hwaddr); ether_ifattach(ifp, hwaddr); callout_init(&sc->wd_callout, 0); - IFQ_SET_MAXLEN(&ifp->if_snd, MGE_TX_DESC_NUM - 1); - ifp->if_snd.ifq_drv_maxlen = MGE_TX_DESC_NUM - 1; - IFQ_SET_READY(&ifp->if_snd); - /* Probe PHY(s) */ error = mii_phy_probe(dev, &sc->miibus, mge_ifmedia_upd, mge_ifmedia_sts); if (error) { @@ -579,10 +659,13 @@ sc = device_get_softc(dev); - /* Stop TSEC controller and free TX queue */ + /* Stop controller and free TX queue */ if (sc->ifp) mge_shutdown(dev); + /* Wait for stopping ticks */ + callout_drain(&sc->wd_callout); + /* Stop and release all interrupts */ for (i = 0; i < 2; ++i) { error = bus_teardown_intr(dev, sc->res[1 + i], sc->ih_cookie[i]); @@ -616,7 +699,7 @@ struct mge_softc *sc = ifp->if_softc; struct mii_data *mii; - mtx_lock(&sc->transmit_lock); + MGE_TRANSMIT_LOCK(sc); mii = sc->mii; mii_pollstat(mii); @@ -624,7 +707,7 @@ ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; - mtx_unlock(&sc->transmit_lock); + MGE_TRANSMIT_UNLOCK(sc); } static uint32_t @@ -667,15 +750,13 @@ struct mge_softc *sc = ifp->if_softc; if (ifp->if_flags & IFF_UP) { - mtx_lock(&sc->transmit_lock); - mtx_lock(&sc->receive_lock); + MGE_GLOBAL_LOCK(sc); sc->mge_media_status = sc->mii->mii_media.ifm_media; mii_mediachg(sc->mii); mge_init_locked(sc); - mtx_unlock(&sc->receive_lock); - mtx_unlock(&sc->transmit_lock); + MGE_GLOBAL_UNLOCK(sc); } return (0); @@ -686,13 +767,11 @@ { struct mge_softc *sc = arg; - mtx_lock(&sc->transmit_lock); - mtx_lock(&sc->receive_lock); + MGE_GLOBAL_LOCK(sc); mge_init_locked(arg); - mtx_unlock(&sc->receive_lock); - mtx_unlock(&sc->transmit_lock); + MGE_GLOBAL_UNLOCK(sc); } static void @@ -703,33 +782,31 @@ volatile uint32_t reg_val; int i, count; + + MGE_GLOBAL_LOCK_ASSERT(sc); + /* Stop interface */ mge_stop(sc); - /* Clear and mask interrrupts */ - MGE_WRITE(sc, MGE_INT_CAUSE, 0x0); - MGE_WRITE(sc, MGE_INT_MASK, 0x0); - - MGE_WRITE(sc, MGE_PORT_INT_CAUSE, 0x0); - MGE_WRITE(sc, MGE_PORT_INT_CAUSE_EXT, 0x0); + /* Disable interrupts */ + mge_intrs_ctrl(sc, 0); - MGE_WRITE(sc, MGE_PORT_INT_MASK, 0x0); - MGE_WRITE(sc, MGE_PORT_INT_MASK_EXT, 0x0); - /* Set MAC address */ mge_set_mac_address(sc); - for (i = 0; i < MGE_MCAST_REG_NUMBER; i++) { - MGE_WRITE(sc, MGE_DA_FILTER_SPEC_MCAST(i), 0x0); - MGE_WRITE(sc, MGE_DA_FILTER_OTH_MCAST(i), 0x0); - } + /* Setup multicast filters */ + mge_setup_multicast(sc); +#if defined(MGE_VER2) + MGE_WRITE(sc, MGE_PORT_SERIAL_CTRL1, MGE_RGMII_EN); + MGE_WRITE(sc, MGE_FIXED_PRIO_CONF, MGE_FIXED_PRIO_EN(0)); +#endif /* Initialize TX queue configuration registers */ MGE_WRITE(sc, MGE_TX_TOKEN_COUNT(0), MGE_TX_TOKEN_Q0_DFLT); MGE_WRITE(sc, MGE_TX_TOKEN_CONF(0), MGE_TX_TOKEN_Q0_DFLT); MGE_WRITE(sc, MGE_TX_ARBITER_CONF(0), MGE_TX_ARB_Q0_DFLT); - for (i = 1; i < MGE_RX_QUEUE_NUM; i++) { + for (i = 1; i < 7; i++) { MGE_WRITE(sc, MGE_TX_TOKEN_COUNT(i), MGE_TX_TOKEN_Q1_7_DFLT); MGE_WRITE(sc, MGE_TX_TOKEN_CONF(i), MGE_TX_TOKEN_Q1_7_DFLT); MGE_WRITE(sc, MGE_TX_ARBITER_CONF(i), MGE_TX_ARB_Q1_7_DFLT); @@ -749,11 +826,10 @@ MGE_WRITE(sc, MGE_PORT_SERIAL_CTRL, reg_val); /* Setup SDMA configuration */ - MGE_WRITE(sc, MGE_SDMA_CONFIG , MGE_SMDA_RX_BYTE_SWAP | - MGE_SMDA_TX_BYTE_SWAP | + MGE_WRITE(sc, MGE_SDMA_CONFIG , MGE_SDMA_RX_BYTE_SWAP | + MGE_SDMA_TX_BYTE_SWAP | MGE_SDMA_RX_BURST_SIZE(MGE_SDMA_BURST_16_WORD) | - MGE_SDMA_TX_BURST_SIZE(MGE_SDMA_BURST_16_WORD) | - MGE_SMDA_RX_IPG(0)); + MGE_SDMA_TX_BURST_SIZE(MGE_SDMA_BURST_16_WORD)); MGE_WRITE(sc, MGE_TX_FIFO_URGENT_TRSH, 0x0); @@ -794,11 +870,21 @@ } } + /* Setup interrupts coalescing */ + mge_set_rxic(sc); + mge_set_txic(sc); + /* Enable interrupts */ - MGE_WRITE(sc, MGE_PORT_INT_MASK , MGE_PORT_INT_RX0 | - MGE_PORT_INT_EXTEND | MGE_PORT_INT_RXERR0); - MGE_WRITE(sc, MGE_PORT_INT_MASK_EXT , MGE_PORT_INT_EXT_TXERR | - MGE_PORT_INT_EXT_RXOR | MGE_PORT_INT_EXT_TXUR | MGE_PORT_INT_EXT_TXBUF); +#ifdef DEVICE_POLLING + /* + * * ...only if polling is not turned on. Disable interrupts explicitly + * if polling is enabled. + */ + if (sc->ifp->if_capenable & IFCAP_POLLING) + mge_intrs_ctrl(sc, 0); + else +#endif /* DEVICE_POLLING */ + mge_intrs_ctrl(sc, 1); /* Activate network interface */ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; @@ -830,86 +916,95 @@ } static void -mge_intr_rx(void *arg) -{ +mge_intr_rx(void *arg) { struct mge_softc *sc = arg; - struct ifnet *ifp; - uint32_t int_cause, int_cause_ext, status; - struct mge_desc_wrapper* dw; - struct mbuf *mb; - struct mbuf *rcv_mbufs[MGE_RX_DESC_NUM]; - int c1 = 0; - int c2; + uint32_t int_cause, int_cause_ext; + + MGE_RECEIVE_LOCK(sc); - ifp = sc->ifp; +#ifdef DEVICE_POLLING + if (sc->ifp->if_capenable & IFCAP_POLLING) { + MGE_RECEIVE_UNLOCK(sc); + return; + } +#endif /* Get interrupt cause */ int_cause = MGE_READ(sc, MGE_PORT_INT_CAUSE); int_cause_ext = MGE_READ(sc, MGE_PORT_INT_CAUSE_EXT); /* Check for resource error */ - if (int_cause & MGE_PORT_INT_RXERR0) { + if (int_cause & MGE_PORT_INT_RXERRQ0) { mge_reinit_rx(sc); MGE_WRITE(sc, MGE_PORT_INT_CAUSE, - int_cause & ~MGE_PORT_INT_RXERR0); + int_cause & ~MGE_PORT_INT_RXERRQ0); } - int_cause &= MGE_PORT_INT_RX0; + int_cause &= MGE_PORT_INT_RXQ0; int_cause_ext &= MGE_PORT_INT_EXT_RXOR; if (int_cause || int_cause_ext) { MGE_WRITE(sc, MGE_PORT_INT_CAUSE, ~int_cause); MGE_WRITE(sc, MGE_PORT_INT_CAUSE_EXT, ~int_cause_ext); + mge_intr_rx_locked(sc, -1); + } - mtx_lock(&sc->receive_lock); + MGE_RECEIVE_UNLOCK(sc); +} + + +static void +mge_intr_rx_locked(struct mge_softc *sc, int count) +{ + struct ifnet *ifp = sc->ifp; + uint32_t status; + uint16_t bufsize; + struct mge_desc_wrapper* dw; + struct mbuf *mb; - while(1) { - dw = &sc->mge_rx_desc[sc->rx_desc_curr]; - bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap, - BUS_DMASYNC_POSTREAD); + MGE_RECEIVE_LOCK_ASSERT(sc); - /* Get status */ - status = dw->mge_desc->cmd_status; - if ((status & MGE_DMA_OWNED) != 0) - break; + while(count != 0) { + dw = &sc->mge_rx_desc[sc->rx_desc_curr]; + bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap, + BUS_DMASYNC_POSTREAD); - sc->rx_desc_curr = (++sc->rx_desc_curr % MGE_RX_DESC_NUM); - if (dw->mge_desc->byte_count && - ~(status & MGE_ERR_SUMMARY)) { + /* Get status */ + status = dw->mge_desc->cmd_status; + bufsize = dw->mge_desc->buff_size; + if ((status & MGE_DMA_OWNED) != 0) + break; - bus_dmamap_sync(sc->mge_rx_dtag, dw->buffer_dmap, - BUS_DMASYNC_POSTREAD); + sc->rx_desc_curr = (++sc->rx_desc_curr % MGE_RX_DESC_NUM); + if (dw->mge_desc->byte_count && + ~(status & MGE_ERR_SUMMARY)) { - mb = m_devget(dw->buffer->m_data, - dw->mge_desc->byte_count - ETHER_CRC_LEN, - 0, ifp, NULL); + bus_dmamap_sync(sc->mge_rx_dtag, dw->buffer_dmap, + BUS_DMASYNC_POSTREAD); - mb->m_len -= 2; - mb->m_pkthdr.len -= 2; - mb->m_data += 2; + mb = m_devget(dw->buffer->m_data, + dw->mge_desc->byte_count - ETHER_CRC_LEN, + 0, ifp, NULL); - if (ifp->if_capenable & IFCAP_RXCSUM) { - if (status & MGE_RX_IP_OK) - mb->m_pkthdr.csum_flags = - CSUM_IP_CHECKED | CSUM_IP_VALID; - } + mb->m_len -= 2; + mb->m_pkthdr.len -= 2; + mb->m_data += 2; - rcv_mbufs[c1++] = mb; - if (c1 >= MGE_RX_DESC_NUM) - break; - } + mge_offload_process_frame(ifp, mb, status, + bufsize); - dw->mge_desc->byte_count = 0; - dw->mge_desc->cmd_status = MGE_RX_ENABLE_INT | MGE_DMA_OWNED; - bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + (*ifp->if_input)(ifp, mb); } - mtx_unlock(&sc->receive_lock); + dw->mge_desc->byte_count = 0; + dw->mge_desc->cmd_status = MGE_RX_ENABLE_INT | MGE_DMA_OWNED; + bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - for (c2 = 0; c2 < c1; c2++) - (*ifp->if_input)(ifp, rcv_mbufs[c2]); + if (count > 0) + count -= 1; } + return; } @@ -927,26 +1022,42 @@ mge_intr_tx(void *arg) { struct mge_softc *sc = arg; - struct ifnet *ifp; - uint32_t int_cause_ext, status; - struct mge_desc_wrapper *dw; - struct mge_desc *desc; - int send = 0; + uint32_t int_cause_ext; + + MGE_TRANSMIT_LOCK(sc); - ifp = sc->ifp; +#ifdef DEVICE_POLLING + if (sc->ifp->if_capenable & IFCAP_POLLING) { + MGE_TRANSMIT_UNLOCK(sc); + return; + } +#endif /* Ack the interrupt */ int_cause_ext = MGE_READ(sc, MGE_PORT_INT_CAUSE_EXT); MGE_WRITE(sc, MGE_PORT_INT_CAUSE_EXT, - int_cause_ext & ~MGE_PORT_INT_EXT_TXBUF); + int_cause_ext & ~MGE_PORT_INT_EXT_TXBUF0); + + mge_intr_tx_locked(sc); + + MGE_TRANSMIT_UNLOCK(sc); +} + + +static void +mge_intr_tx_locked(struct mge_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + struct mge_desc_wrapper *dw; + struct mge_desc *desc; + uint32_t status; + int send = 0; + + MGE_TRANSMIT_LOCK_ASSERT(sc); /* Disable watchdog */ sc->wd_timer = 0; - /* Acquire the mutex */ - mtx_assert(&sc->receive_lock, MA_NOTOWNED); - mtx_lock(&sc->transmit_lock); - while (sc->tx_desc_used_count) { /* Get the descriptor */ dw = &sc->mge_tx_desc[sc->tx_desc_used_idx]; @@ -987,8 +1098,6 @@ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mge_start_locked(ifp); } - - mtx_unlock(&sc->transmit_lock); } static int @@ -996,21 +1105,24 @@ { struct mge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; - int error; + int mask, error; uint32_t flags; error = 0; switch (command) { case SIOCSIFFLAGS: - mtx_lock(&sc->transmit_lock); - mtx_lock(&sc->receive_lock); + MGE_GLOBAL_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { flags = ifp->if_flags ^ sc->mge_if_flags; if (flags & IFF_PROMISC) - mge_set_prom_mode(sc, MGE_RX_DEFAULT_QUEUE); + mge_set_prom_mode(sc, + MGE_RX_DEFAULT_QUEUE); + + if (flags & IFF_ALLMULTI) + mge_setup_multicast(sc); } else mge_init_locked(sc); } @@ -1018,11 +1130,55 @@ mge_stop(sc); sc->mge_if_flags = ifp->if_flags; - mtx_unlock(&sc->transmit_lock); - mtx_unlock(&sc->receive_lock); + MGE_GLOBAL_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + MGE_GLOBAL_LOCK(sc); + mge_setup_multicast(sc); + MGE_GLOBAL_UNLOCK(sc); + } + break; + case SIOCSIFCAP: + mask = ifp->if_capenable ^ ifr->ifr_reqcap; + if (mask & IFCAP_HWCSUM) { + ifp->if_capenable &= ~IFCAP_HWCSUM; + ifp->if_capenable |= IFCAP_HWCSUM & ifr->ifr_reqcap; + if (ifp->if_capenable & IFCAP_TXCSUM) + ifp->if_hwassist = MGE_CHECKSUM_FEATURES; + else + ifp->if_hwassist = 0; + } +#ifdef DEVICE_POLLING + if (mask & IFCAP_POLLING) { + if (ifr->ifr_reqcap & IFCAP_POLLING) { + error = ether_poll_register(mge_poll, ifp); + if (error) + return(error); + + MGE_GLOBAL_LOCK(sc); + mge_intrs_ctrl(sc, 0); + ifp->if_capenable |= IFCAP_POLLING; + MGE_GLOBAL_UNLOCK(sc); + } else { + error = ether_poll_deregister(ifp); + MGE_GLOBAL_LOCK(sc); + mge_intrs_ctrl(sc, 1); + ifp->if_capenable &= ~IFCAP_POLLING; + MGE_GLOBAL_UNLOCK(sc); + } + } +#endif break; case SIOCGIFMEDIA: /* fall through */ case SIOCSIFMEDIA: + if (IFM_SUBTYPE(ifr->ifr_media) == IFM_1000_T + && !(ifr->ifr_media & IFM_FDX)) { + device_printf(sc->dev, + "1000baseTX half-duplex unsupported\n"); + return 0; + } error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command); break; default: @@ -1034,35 +1190,46 @@ static int mge_miibus_readreg(device_t dev, int phy, int reg) { - struct mge_softc *sc; uint32_t retries; - sc = device_get_softc(dev); - MGE_WRITE(sc, MGE_REG_SMI, 0x1fffffff & + /* + * We assume static PHY address <=> device unit mapping: + * PHY Address = MV_PHY_ADDR_BASE + devce unit. + * This is true for most Marvell boards. + * + * Code below grants proper PHY detection on each device + * unit. + */ + + if ((MV_PHY_ADDR_BASE + device_get_unit(dev)) != phy) + return (0); + + MGE_WRITE(sc_mge0, MGE_REG_SMI, 0x1fffffff & (MGE_SMI_READ | (reg << 21) | (phy << 16))); retries = MGE_SMI_READ_RETRIES; - while (--retries && !(MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_READVALID)) + while (--retries && !(MGE_READ(sc_mge0, MGE_REG_SMI) & MGE_SMI_READVALID)) DELAY(MGE_SMI_READ_DELAY); if (retries == 0) device_printf(dev, "Timeout while reading from PHY\n"); - return (MGE_READ(sc, MGE_REG_SMI) & 0xffff); + return (MGE_READ(sc_mge0, MGE_REG_SMI) & 0xffff); } static void mge_miibus_writereg(device_t dev, int phy, int reg, int value) { - struct mge_softc *sc; uint32_t retries; - sc = device_get_softc(dev); - MGE_WRITE(sc, MGE_REG_SMI, 0x1fffffff & + if ((MV_PHY_ADDR_BASE + device_get_unit(dev)) != phy) + return; + + MGE_WRITE(sc_mge0, MGE_REG_SMI, 0x1fffffff & (MGE_SMI_WRITE | (reg << 21) | (phy << 16) | (value & 0xffff))); retries = MGE_SMI_WRITE_RETRIES; - while (--retries && MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_BUSY) + while (--retries && MGE_READ(sc_mge0, MGE_REG_SMI) & MGE_SMI_BUSY) DELAY(MGE_SMI_WRITE_DELAY); if (retries == 0) @@ -1074,7 +1241,6 @@ { device_set_desc(dev, "Marvell Gigabit Ethernet controller"); - return (BUS_PROBE_DEFAULT); } @@ -1091,13 +1257,16 @@ { struct mge_softc *sc = device_get_softc(dev); - mtx_lock(&sc->transmit_lock); - mtx_lock(&sc->receive_lock); + MGE_GLOBAL_LOCK(sc); + +#ifdef DEVICE_POLLING + if (sc->ifp->if_capenable & IFCAP_POLLING) + ether_poll_deregister(sc->ifp); +#endif mge_stop(sc); - mtx_unlock(&sc->transmit_lock); - mtx_unlock(&sc->receive_lock); + MGE_GLOBAL_UNLOCK(sc); return (0); } @@ -1116,9 +1285,10 @@ ifp = sc->ifp; /* Check for free descriptors */ - if (sc->tx_desc_used_count + 1 >= MGE_TX_DESC_NUM) + if (sc->tx_desc_used_count + 1 >= MGE_TX_DESC_NUM) { /* No free descriptors */ return (-1); + } /* Fetch unused map */ desc_no = sc->tx_desc_curr; @@ -1143,9 +1313,12 @@ dw->mge_desc->byte_count = segs[seg].ds_len; dw->mge_desc->buffer = segs[seg].ds_addr; dw->buffer = m0; - dw->mge_desc->cmd_status = MGE_TX_DESC_LAST | - MGE_TX_DESC_FIRST | MGE_TX_DESC_ETH_CRC | - MGE_TX_DESC_EN_INT | MGE_TX_DESC_PADDING | MGE_DMA_OWNED; + dw->mge_desc->cmd_status = MGE_TX_LAST | MGE_TX_FIRST | + MGE_TX_ETH_CRC | MGE_TX_EN_INT | MGE_TX_PADDING | + MGE_DMA_OWNED; + + if (seg == 0) + mge_offload_setup_descriptor(sc, dw); } bus_dmamap_sync(sc->mge_desc_dtag, mapp, @@ -1181,12 +1354,10 @@ ifp = sc->ifp; - mtx_lock(&sc->transmit_lock); - mtx_lock(&sc->receive_lock); + MGE_GLOBAL_LOCK(sc); if (sc->wd_timer == 0 || --sc->wd_timer) { - mtx_unlock(&sc->receive_lock); - mtx_unlock(&sc->transmit_lock); + MGE_GLOBAL_UNLOCK(sc); return; } @@ -1196,8 +1367,7 @@ mge_stop(sc); mge_init_locked(sc); - mtx_unlock(&sc->receive_lock); - mtx_unlock(&sc->transmit_lock); + MGE_GLOBAL_UNLOCK(sc); } static void @@ -1205,12 +1375,11 @@ { struct mge_softc *sc = ifp->if_softc; - mtx_assert(&sc->receive_lock, MA_NOTOWNED); - mtx_lock(&sc->transmit_lock); + MGE_TRANSMIT_LOCK(sc); mge_start_locked(ifp); - mtx_unlock(&sc->transmit_lock); + MGE_TRANSMIT_UNLOCK(sc); } static void @@ -1218,10 +1387,12 @@ { struct mge_softc *sc; struct mbuf *m0, *mtmp; - unsigned int queued = 0; + uint32_t reg_val, queued = 0; sc = ifp->if_softc; + MGE_TRANSMIT_LOCK_ASSERT(sc); + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; @@ -1247,7 +1418,8 @@ if (queued) { /* Enable transmitter and watchdog timer */ - MGE_WRITE(sc, MGE_TX_QUEUE_CMD, MGE_ENABLE_TXQ); + reg_val = MGE_READ(sc, MGE_TX_QUEUE_CMD); + MGE_WRITE(sc, MGE_TX_QUEUE_CMD, reg_val | MGE_ENABLE_TXQ); sc->wd_timer = 5; } } @@ -1266,16 +1438,19 @@ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; + /* Stop tick engine */ + callout_stop(&sc->wd_callout); + /* Disable interface */ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->wd_timer = 0; - /* Disable all interrupts and stop DMA */ - MGE_WRITE(sc, MGE_PORT_INT_MASK , 0x0); - MGE_WRITE(sc, MGE_PORT_INT_MASK_EXT , 0x0); + /* Disable interrupts */ + mge_intrs_ctrl(sc, 0); /* Disable Rx and Tx */ - MGE_WRITE(sc, MGE_TX_QUEUE_CMD, MGE_DISABLE_TXQ); + reg_val = MGE_READ(sc, MGE_TX_QUEUE_CMD); + MGE_WRITE(sc, MGE_TX_QUEUE_CMD, reg_val | MGE_DISABLE_TXQ); MGE_WRITE(sc, MGE_RX_QUEUE_CMD, MGE_DISABLE_RXQ_ALL); /* Remove pending data from TX queue */ @@ -1329,3 +1504,252 @@ device_printf(dev, "%s\n", __FUNCTION__); return (0); } + +static void +mge_offload_process_frame(struct ifnet *ifp, struct mbuf *frame, + uint32_t status, uint16_t bufsize) +{ + int csum_flags = 0; + + if (ifp->if_capenable & IFCAP_RXCSUM) { + if ((status & MGE_RX_L3_IS_IP) && (status & MGE_RX_IP_OK)) + csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; + + if ((bufsize & MGE_RX_IP_FRAGMENT) == 0 && + (MGE_RX_L4_IS_TCP(status) || MGE_RX_L4_IS_UDP(status)) && + (status & MGE_RX_L4_CSUM_OK)) { + csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + frame->m_pkthdr.csum_data = 0xFFFF; + } + + frame->m_pkthdr.csum_flags = csum_flags; + } +} + +static void +mge_offload_setup_descriptor(struct mge_softc *sc, struct mge_desc_wrapper *dw) +{ + struct mbuf *m0 = dw->buffer; + struct ether_vlan_header *eh = mtod(m0, struct ether_vlan_header *); + int csum_flags = m0->m_pkthdr.csum_flags; + int cmd_status = 0; + struct ip *ip; + int ehlen, etype; + + if (csum_flags) { + if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { + etype = ntohs(eh->evl_proto); + ehlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; + csum_flags |= MGE_TX_VLAN_TAGGED; + } else { + etype = ntohs(eh->evl_encap_proto); + ehlen = ETHER_HDR_LEN; + } + + if (etype != ETHERTYPE_IP) { + if_printf(sc->ifp, + "TCP/IP Offload enabled for unsupported " + "protocol!\n"); + return; + } + + ip = (struct ip *)(m0->m_data + ehlen); + cmd_status |= MGE_TX_IP_HDR_SIZE(ip->ip_hl); + + if ((m0->m_flags & M_FRAG) == 0) + cmd_status |= MGE_TX_NOT_FRAGMENT; + } + + if (csum_flags & CSUM_IP) + cmd_status |= MGE_TX_GEN_IP_CSUM; + + if (csum_flags & CSUM_TCP) + cmd_status |= MGE_TX_GEN_L4_CSUM; + + if (csum_flags & CSUM_UDP) + cmd_status |= MGE_TX_GEN_L4_CSUM | MGE_TX_UDP; + + dw->mge_desc->cmd_status |= cmd_status; +} + +static void +mge_intrs_ctrl(struct mge_softc *sc, int enable) +{ + + if (enable) { + MGE_WRITE(sc, MGE_PORT_INT_MASK , MGE_PORT_INT_RXQ0 | + MGE_PORT_INT_EXTEND | MGE_PORT_INT_RXERRQ0); + MGE_WRITE(sc, MGE_PORT_INT_MASK_EXT , MGE_PORT_INT_EXT_TXERR0 | + MGE_PORT_INT_EXT_RXOR | MGE_PORT_INT_EXT_TXUR | + MGE_PORT_INT_EXT_TXBUF0); + } else { + MGE_WRITE(sc, MGE_INT_CAUSE, 0x0); + MGE_WRITE(sc, MGE_INT_MASK, 0x0); + + MGE_WRITE(sc, MGE_PORT_INT_CAUSE, 0x0); + MGE_WRITE(sc, MGE_PORT_INT_CAUSE_EXT, 0x0); + + MGE_WRITE(sc, MGE_PORT_INT_MASK, 0x0); + MGE_WRITE(sc, MGE_PORT_INT_MASK_EXT, 0x0); + } +} + +static uint8_t +mge_crc8(uint8_t *data, int size) +{ >>> TRUNCATED FOR MAIL (1000 lines) <<<help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200809061927.m86JRFKG033027>
