svn commit: r183867 - head/sys/dev/mge

Rafal Jaworowski raj at FreeBSD.org
Tue Oct 14 07:24:19 UTC 2008


Author: raj
Date: Tue Oct 14 07:24:18 2008
New Revision: 183867
URL: http://svn.freebsd.org/changeset/base/183867

Log:
  Marvell Gigabit Ethernet controller driver.
  
  This supports 1Gbps Ethernet engine found on ARM-based SOCs (Orion, Kirkwood,
  Discovery), as well as on system controllers for PowerPC processors (MV64430,
  MV6446x).
  
  The following advanced features are supported:
  
    - multicast
    - VLAN tagging
    - IP/TCP/UDP checksum calculation offloading
    - polling
    - interrupt coalescing
  
  Obtained from:	Marvell, Semihalf

Added:
  head/sys/dev/mge/
  head/sys/dev/mge/if_mge.c   (contents, props changed)
  head/sys/dev/mge/if_mgevar.h   (contents, props changed)

Added: head/sys/dev/mge/if_mge.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/mge/if_mge.c	Tue Oct 14 07:24:18 2008	(r183867)
@@ -0,0 +1,1757 @@
+/*-
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of MARVELL nor the names of contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_device_polling.h"
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#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>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#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);
+static int mge_shutdown(device_t dev);
+static int mge_suspend(device_t dev);
+static int mge_resume(device_t dev);
+
+static int mge_miibus_readreg(device_t dev, int phy, int reg);
+static void mge_miibus_writereg(device_t dev, int phy, int reg, int value);
+
+static int mge_ifmedia_upd(struct ifnet *ifp);
+static void mge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
+
+static void mge_init(void *arg);
+static void mge_init_locked(void *arg);
+static void mge_start(struct ifnet *ifp);
+static void mge_start_locked(struct ifnet *ifp);
+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);
+static void mge_stop(struct mge_softc *sc);
+static void mge_tick(void *msc);
+static uint32_t mge_set_port_serial_control(uint32_t media);
+static void mge_get_mac_address(struct mge_softc *sc, uint8_t *addr);
+static void mge_set_mac_address(struct mge_softc *sc);
+static void mge_set_ucast_address(struct mge_softc *sc, uint8_t last_byte,
+    uint8_t queue);
+static void mge_set_prom_mode(struct mge_softc *sc, uint8_t queue);
+static int mge_allocate_dma(struct mge_softc *sc);
+static int mge_alloc_desc_dma(struct mge_softc *sc,
+    struct mge_desc_wrapper* desc_tab, uint32_t size, bus_dma_tag_t *buffer_tag);
+static int mge_new_rxbuf(bus_dma_tag_t tag, bus_dmamap_t map,
+    struct mbuf **mbufp, bus_addr_t *paddr);
+static void mge_get_dma_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+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 */
+	DEVMETHOD(device_probe,		mge_probe),
+	DEVMETHOD(device_attach,	mge_attach),
+	DEVMETHOD(device_detach,	mge_detach),
+	DEVMETHOD(device_shutdown,	mge_shutdown),
+	DEVMETHOD(device_suspend,	mge_suspend),
+	DEVMETHOD(device_resume,	mge_resume),
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	mge_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	mge_miibus_writereg),
+	{ 0, 0 }
+};
+
+static driver_t mge_driver = {
+	"mge",
+	mge_methods,
+	sizeof(struct mge_softc),
+};
+
+static devclass_t mge_devclass;
+
+DRIVER_MODULE(mge, mbus, mge_driver, mge_devclass, 0, 0);
+DRIVER_MODULE(miibus, mge, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(mge, ether, 1, 1, 1);
+MODULE_DEPEND(mge, miibus, 1, 1, 1);
+
+static struct resource_spec res_spec[] = {
+	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
+	{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+	{ SYS_RES_IRQ, 1, RF_ACTIVE | RF_SHAREABLE },
+	{ SYS_RES_IRQ, 2, RF_ACTIVE | RF_SHAREABLE },
+	{ SYS_RES_IRQ, 3, RF_ACTIVE | RF_SHAREABLE },
+	{ SYS_RES_IRQ, 4, RF_ACTIVE | RF_SHAREABLE },
+	{ -1, 0 }
+};
+
+static struct {
+	driver_intr_t *handler;
+	char * description;
+} mge_intrs[MGE_INTR_COUNT] = {
+	{ mge_intr_rx,	"GbE receive interrupt" },
+	{ mge_intr_tx,	"GbE transmit interrupt" },
+	{ mge_intr_misc,"GbE misc interrupt" },
+	{ mge_intr_sum,	"GbE summary interrupt" },
+	{ mge_intr_err,	"GbE error interrupt" },
+};
+
+static void
+mge_get_mac_address(struct mge_softc *sc, uint8_t *addr)
+{
+	uint32_t mac_l, mac_h;
+
+	/* XXX use currently programmed MAC address; eventually this info will
+	 * be provided by the loader */
+
+	mac_l = MGE_READ(sc, MGE_MAC_ADDR_L);
+	mac_h = MGE_READ(sc, MGE_MAC_ADDR_H);
+
+	addr[0] = (mac_h & 0xff000000) >> 24;
+	addr[1] = (mac_h & 0x00ff0000) >> 16;
+	addr[2] = (mac_h & 0x0000ff00) >> 8;
+	addr[3] = (mac_h & 0x000000ff);
+	addr[4] = (mac_l & 0x0000ff00) >> 8;
+	addr[5] = (mac_l & 0x000000ff);
+}
+
+static void
+mge_set_mac_address(struct mge_softc *sc)
+{
+	char *if_mac;
+	uint32_t mac_l, mac_h;
+
+	MGE_GLOBAL_LOCK_ASSERT(sc);
+
+	if_mac = (char *)IF_LLADDR(sc->ifp);
+
+	mac_l = (if_mac[4] << 8) | (if_mac[5]);
+	mac_h = (if_mac[0] << 24)| (if_mac[1] << 16) |
+	    (if_mac[2] << 8) | (if_mac[3] << 0);
+
+	MGE_WRITE(sc, MGE_MAC_ADDR_L, mac_l);
+	MGE_WRITE(sc, MGE_MAC_ADDR_H, mac_h);
+
+	mge_set_ucast_address(sc, if_mac[5], MGE_RX_DEFAULT_QUEUE);
+}
+
+static void
+mge_set_ucast_address(struct mge_softc *sc, uint8_t last_byte, uint8_t queue)
+{
+	uint32_t reg_idx, reg_off, reg_val, i;
+
+	last_byte &= 0xf;
+	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 < MGE_UCAST_REG_NUMBER; i++) {
+		if ( i == reg_idx)
+			MGE_WRITE(sc, MGE_DA_FILTER_UCAST(i), reg_val);
+		else
+			MGE_WRITE(sc, MGE_DA_FILTER_UCAST(i), 0);
+	}
+}
+
+static void
+mge_set_prom_mode(struct mge_softc *sc, uint8_t queue)
+{
+	uint32_t port_config;
+	uint32_t reg_val, i;
+
+	/* Enable or disable promiscuous mode as needed */
+	if (sc->ifp->if_flags & IFF_PROMISC) {
+		port_config = MGE_READ(sc, MGE_PORT_CONFIG);
+		port_config |= PORT_CONFIG_UPM;
+		MGE_WRITE(sc, MGE_PORT_CONFIG, port_config);
+
+		reg_val = ((1 | (queue << 1)) | (1 | (queue << 1)) << 8 |
+		   (1 | (queue << 1)) << 16 | (1 | (queue << 1)) << 24);
+
+		for (i = 0; i < MGE_MCAST_REG_NUMBER; i++) {
+			MGE_WRITE(sc, MGE_DA_FILTER_SPEC_MCAST(i), reg_val);
+			MGE_WRITE(sc, MGE_DA_FILTER_OTH_MCAST(i), 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);
+		port_config &= ~PORT_CONFIG_UPM;
+		MGE_WRITE(sc, MGE_PORT_CONFIG, port_config);
+
+		for (i = 0; i < MGE_MCAST_REG_NUMBER; i++) {
+			MGE_WRITE(sc, MGE_DA_FILTER_SPEC_MCAST(i), 0);
+			MGE_WRITE(sc, MGE_DA_FILTER_OTH_MCAST(i), 0);
+		}
+
+		mge_set_mac_address(sc);
+	}
+}
+
+static void
+mge_get_dma_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+	u_int32_t *paddr;
+
+	KASSERT(nseg == 1, ("wrong number of segments, should be 1"));
+	paddr = arg;
+
+	*paddr = segs->ds_addr;
+}
+
+static int
+mge_new_rxbuf(bus_dma_tag_t tag, bus_dmamap_t map, struct mbuf **mbufp,
+    bus_addr_t *paddr)
+{
+	struct mbuf *new_mbuf;
+	bus_dma_segment_t seg[1];
+	int error;
+	int nsegs;
+
+	KASSERT(mbufp != NULL, ("NULL mbuf pointer!"));
+
+	new_mbuf = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+	if (new_mbuf == NULL)
+		return (ENOBUFS);
+	new_mbuf->m_len = new_mbuf->m_pkthdr.len = new_mbuf->m_ext.ext_size;
+
+	if (*mbufp) {
+		bus_dmamap_sync(tag, map, BUS_DMASYNC_POSTREAD);
+		bus_dmamap_unload(tag, map);
+	}
+
+	error = bus_dmamap_load_mbuf_sg(tag, map, new_mbuf, seg, &nsegs,
+	    BUS_DMA_NOWAIT);
+	KASSERT(nsegs == 1, ("Too many segments returned!"));
+	if (nsegs != 1 || error)
+		panic("mge_new_rxbuf(): nsegs(%d), error(%d)", nsegs, error);
+
+	bus_dmamap_sync(tag, map, BUS_DMASYNC_PREREAD);
+
+	(*mbufp) = new_mbuf;
+	(*paddr) = seg->ds_addr;
+	return (0);
+}
+
+static int
+mge_alloc_desc_dma(struct mge_softc *sc, struct mge_desc_wrapper* tab,
+    uint32_t size, bus_dma_tag_t *buffer_tag)
+{
+	struct mge_desc_wrapper *dw;
+	bus_addr_t desc_paddr;
+	int i, error;
+
+	desc_paddr = 0;
+	for (i = size - 1; i >= 0; i--) {
+		dw = &(tab[i]);
+		error = bus_dmamem_alloc(sc->mge_desc_dtag,
+		    (void**)&(dw->mge_desc),
+		    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+		    &(dw->desc_dmap));
+
+		if (error) {
+			if_printf(sc->ifp, "failed to allocate DMA memory\n");
+			dw->mge_desc = NULL;
+			return (ENXIO);
+		}
+
+		error = bus_dmamap_load(sc->mge_desc_dtag, dw->desc_dmap,
+		    dw->mge_desc, sizeof(struct mge_desc), mge_get_dma_addr,
+		    &(dw->mge_desc_paddr), BUS_DMA_NOWAIT);
+
+		if (error) {
+			if_printf(sc->ifp, "can't load descriptor\n");
+			bus_dmamem_free(sc->mge_desc_dtag, dw->mge_desc,
+			    dw->desc_dmap);
+			dw->mge_desc = NULL;
+			return (ENXIO);
+		}
+
+		/* Chain descriptors */
+		dw->mge_desc->next_desc = desc_paddr;
+		desc_paddr = dw->mge_desc_paddr;
+	}
+	tab[size - 1].mge_desc->next_desc = desc_paddr;
+
+	/* Allocate a busdma tag for mbufs. */
+	error = bus_dma_tag_create(NULL,	/* parent */
+	    8, 0,				/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,			/* highaddr */
+	    NULL, NULL,				/* filtfunc, filtfuncarg */
+	    MCLBYTES, 1,			/* maxsize, nsegments */
+	    MCLBYTES, 0,			/* maxsegsz, flags */
+	    NULL, NULL,				/* lockfunc, lockfuncarg */
+	    buffer_tag);			/* dmat */
+	if (error) {
+		if_printf(sc->ifp, "failed to create busdma tag for mbufs\n");
+		return (ENXIO);
+	}
+
+	/* Create TX busdma maps */
+	for (i = 0; i < size; i++) {
+		dw = &(tab[i]);
+		error = bus_dmamap_create(*buffer_tag, 0, &dw->buffer_dmap);
+		if (error) {
+			if_printf(sc->ifp, "failed to create map for mbuf\n");
+			return (ENXIO);
+		}
+
+		dw->buffer = (struct mbuf*)NULL;
+		dw->mge_desc->buffer = (bus_addr_t)NULL;
+	}
+
+	return (0);
+}
+
+static int
+mge_allocate_dma(struct mge_softc *sc)
+{
+	int error;
+	struct mge_desc_wrapper *dw;
+	int num, i;
+
+
+	num = MGE_TX_DESC_NUM + MGE_RX_DESC_NUM;
+
+	/* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */
+	error = bus_dma_tag_create(NULL,	/* parent */
+	    16, 0,				/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,			/* highaddr */
+	    NULL, NULL,				/* filtfunc, filtfuncarg */
+	    sizeof(struct mge_desc), 1,		/* maxsize, nsegments */
+	    sizeof(struct mge_desc), 0,		/* maxsegsz, flags */
+	    NULL, NULL,				/* lockfunc, lockfuncarg */
+	    &sc->mge_desc_dtag);		/* dmat */
+
+
+	mge_alloc_desc_dma(sc, sc->mge_tx_desc, MGE_TX_DESC_NUM,
+	    &sc->mge_tx_dtag);
+	mge_alloc_desc_dma(sc, sc->mge_rx_desc, MGE_RX_DESC_NUM,
+	    &sc->mge_rx_dtag);
+
+	for (i = 0; i < MGE_RX_DESC_NUM; i++) {
+		dw = &(sc->mge_rx_desc[i]);
+		mge_new_rxbuf(sc->mge_rx_dtag, dw->buffer_dmap, &dw->buffer,
+		    &dw->mge_desc->buffer);
+	}
+
+	sc->tx_desc_start = sc->mge_tx_desc[0].mge_desc_paddr;
+	sc->rx_desc_start = sc->mge_rx_desc[0].mge_desc_paddr;
+
+	return (0);
+}
+
+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)
+{
+	struct mge_desc_wrapper *dw;
+	int i;
+
+	for (i = 0; i < size; i++) {
+		/* Free RX mbuf */
+		dw = &(tab[i]);
+
+		if (dw->buffer_dmap) {
+			if (free_mbufs) {
+				bus_dmamap_sync(buffer_tag, dw->buffer_dmap,
+				    BUS_DMASYNC_POSTREAD);
+				bus_dmamap_unload(buffer_tag, dw->buffer_dmap);
+			}
+			bus_dmamap_destroy(buffer_tag, dw->buffer_dmap);
+			if (free_mbufs)
+				m_freem(dw->buffer);
+		}
+		/* Free RX descriptors */
+		if (dw->desc_dmap) {
+			bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap,
+			    BUS_DMASYNC_POSTREAD);
+			bus_dmamap_unload(sc->mge_desc_dtag, dw->desc_dmap);
+			bus_dmamem_free(sc->mge_desc_dtag, dw->mge_desc,
+			    dw->desc_dmap);
+		}
+	}
+}
+
+static void
+mge_free_dma(struct mge_softc *sc)
+{
+	/* Free desciptors and mbufs */
+	mge_free_desc(sc, sc->mge_rx_desc, MGE_RX_DESC_NUM, sc->mge_rx_dtag, 1);
+	mge_free_desc(sc, sc->mge_tx_desc, MGE_TX_DESC_NUM, sc->mge_tx_dtag, 0);
+
+	/* Destroy mbuf dma tag */
+	bus_dma_tag_destroy(sc->mge_tx_dtag);
+	bus_dma_tag_destroy(sc->mge_rx_dtag);
+	/* Destroy descriptors tag */
+	bus_dma_tag_destroy(sc->mge_desc_dtag);
+}
+
+static void
+mge_reinit_rx(struct mge_softc *sc)
+{
+	struct mge_desc_wrapper *dw;
+	int i;
+
+	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,
+	    &sc->mge_rx_dtag);
+
+	for (i = 0; i < MGE_RX_DESC_NUM; i++) {
+		dw = &(sc->mge_rx_desc[i]);
+		mge_new_rxbuf(sc->mge_rx_dtag, dw->buffer_dmap, &dw->buffer,
+		&dw->mge_desc->buffer);
+	}
+
+	sc->rx_desc_start = sc->mge_rx_desc[0].mge_desc_paddr;
+	sc->rx_desc_curr = 0;
+
+	MGE_WRITE(sc, MGE_RX_CUR_DESC_PTR(MGE_RX_DEFAULT_QUEUE),
+	    sc->rx_desc_start);
+
+	/* Enable RX queue */
+	MGE_WRITE(sc, MGE_RX_QUEUE_CMD, MGE_ENABLE_RXQ(MGE_RX_DEFAULT_QUEUE));
+
+	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)
+{
+	struct mge_softc *sc;
+	struct ifnet *ifp;
+	uint8_t hwaddr[ETHER_ADDR_LEN];
+	int i, error ;
+
+	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);
+
+	/* Allocate IO and IRQ resources */
+	error = bus_alloc_resources(dev, res_spec, sc->res);
+	if (error) {
+		device_printf(dev, "could not allocate resources\n");
+		mge_detach(dev);
+		return (ENXIO);
+	}
+
+	/* Allocate DMA, buffers, buffer descriptors */
+	error = mge_allocate_dma(sc);
+	if (error) {
+		mge_detach(dev);
+		return (ENXIO);
+	}
+
+	sc->tx_desc_curr = 0;
+	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) {
+		device_printf(dev, "if_alloc() failed\n");
+		mge_detach(dev);
+		return (ENOMEM);
+	}
+
+	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+	ifp->if_softc = sc;
+	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);
+
+	/* Probe PHY(s) */
+	error = mii_phy_probe(dev, &sc->miibus, mge_ifmedia_upd, mge_ifmedia_sts);
+	if (error) {
+		device_printf(dev, "MII failed to find PHY\n");
+		if_free(ifp);
+		sc->ifp = NULL;
+		mge_detach(dev);
+		return (error);
+	}
+	sc->mii = device_get_softc(sc->miibus);
+
+	/* Attach interrupt handlers */
+	for (i = 0; i < 2; ++i) {
+		error = bus_setup_intr(dev, sc->res[1 + i],
+		    INTR_TYPE_NET | INTR_MPSAFE, NULL, *mge_intrs[i].handler,
+		    sc, &sc->ih_cookie[i]);
+		if (error) {
+			device_printf(dev, "could not setup %s\n",
+			    mge_intrs[i].description);
+			ether_ifdetach(sc->ifp);
+			return (error);
+		}
+	}
+
+	return (0);
+}
+
+static int
+mge_detach(device_t dev)
+{
+	struct mge_softc *sc;
+	int error,i;
+
+	sc = device_get_softc(dev);
+
+	/* 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]);
+		if (error)
+			device_printf(dev, "could not release %s\n",
+			    mge_intrs[i].description);
+	}
+
+	/* Detach network interface */
+	if (sc->ifp) {
+		ether_ifdetach(sc->ifp);
+		if_free(sc->ifp);
+	}
+
+	/* Free DMA resources */
+	mge_free_dma(sc);
+
+	/* Free IO memory handler */
+	bus_release_resources(dev, res_spec, sc->res);
+
+	/* Destroy mutexes */
+	mtx_destroy(&sc->receive_lock);
+	mtx_destroy(&sc->transmit_lock);
+
+	return (0);
+}
+
+static void
+mge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct mge_softc *sc = ifp->if_softc;
+	struct mii_data *mii;
+
+	MGE_TRANSMIT_LOCK(sc);
+
+	mii = sc->mii;
+	mii_pollstat(mii);
+
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+
+	MGE_TRANSMIT_UNLOCK(sc);
+}
+
+static uint32_t
+mge_set_port_serial_control(uint32_t media)
+{
+	uint32_t port_config;
+
+	port_config = PORT_SERIAL_RES_BIT9 | PORT_SERIAL_FORCE_LINK_FAIL |
+	    PORT_SERIAL_MRU(PORT_SERIAL_MRU_1552);
+
+	if (IFM_TYPE(media) == IFM_ETHER) {
+		switch(IFM_SUBTYPE(media)) {
+			case IFM_AUTO:
+				break;
+			case IFM_1000_T:
+				port_config  |= (PORT_SERIAL_GMII_SPEED_1000 |
+				    PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC |
+				    PORT_SERIAL_SPEED_AUTONEG);
+				break;
+			case IFM_100_TX:
+				port_config  |= (PORT_SERIAL_MII_SPEED_100 |
+				    PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC |
+				    PORT_SERIAL_SPEED_AUTONEG);
+				break;
+			case IFM_10_T:
+				port_config  |= (PORT_SERIAL_AUTONEG |
+				    PORT_SERIAL_AUTONEG_FC |
+				    PORT_SERIAL_SPEED_AUTONEG);
+				break;
+		}
+		if (media & IFM_FDX)
+			port_config |= PORT_SERIAL_FULL_DUPLEX;
+	}
+	return (port_config);
+}
+
+static int
+mge_ifmedia_upd(struct ifnet *ifp)
+{
+	struct mge_softc *sc = ifp->if_softc;
+
+	if (ifp->if_flags & IFF_UP) {
+		MGE_GLOBAL_LOCK(sc);
+
+		sc->mge_media_status = sc->mii->mii_media.ifm_media;
+		mii_mediachg(sc->mii);
+		mge_init_locked(sc);
+
+		MGE_GLOBAL_UNLOCK(sc);
+	}
+
+	return (0);
+}
+
+static void
+mge_init(void *arg)
+{
+	struct mge_softc *sc = arg;
+
+	MGE_GLOBAL_LOCK(sc);
+
+	mge_init_locked(arg);
+
+	MGE_GLOBAL_UNLOCK(sc);
+}
+
+static void
+mge_init_locked(void *arg)
+{
+	struct mge_softc *sc = arg;
+	struct mge_desc_wrapper *dw;
+	volatile uint32_t reg_val;
+	int i, count;
+
+
+	MGE_GLOBAL_LOCK_ASSERT(sc);
+
+	/* Stop interface */
+	mge_stop(sc);
+
+	/* Disable interrupts */
+	mge_intrs_ctrl(sc, 0);
+
+	/* Set MAC address */
+	mge_set_mac_address(sc);
+
+	/* 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 < 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);
+	}
+
+	/* Set default MTU */
+	MGE_WRITE(sc, MGE_MTU, MGE_MTU_DEFAULT);
+
+	/* Port configuration */
+	MGE_WRITE(sc, MGE_PORT_CONFIG,
+	    PORT_CONFIG_RXCS | PORT_CONFIG_DFLT_RXQ(0) |
+	    PORT_CONFIG_ARO_RXQ(0));
+	MGE_WRITE(sc, MGE_PORT_EXT_CONFIG , 0x0);
+
+	/* Setup port configuration */
+	reg_val = mge_set_port_serial_control(sc->mge_media_status);
+	MGE_WRITE(sc, MGE_PORT_SERIAL_CTRL, reg_val);
+
+	/* Setup SDMA configuration */
+	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_WRITE(sc, MGE_TX_FIFO_URGENT_TRSH, 0x0);
+
+	MGE_WRITE(sc, MGE_TX_CUR_DESC_PTR, sc->tx_desc_start);
+	MGE_WRITE(sc, MGE_RX_CUR_DESC_PTR(MGE_RX_DEFAULT_QUEUE),
+	    sc->rx_desc_start);
+
+	/* Reset descriptor indexes */
+	sc->tx_desc_curr = 0;
+	sc->rx_desc_curr = 0;
+	sc->tx_desc_used_idx = 0;
+
+	/* Enable RX descriptors */
+	for (i = 0; i < MGE_RX_DESC_NUM; i++) {
+		dw = &sc->mge_rx_desc[i];
+		dw->mge_desc->cmd_status = MGE_RX_ENABLE_INT | MGE_DMA_OWNED;
+		dw->mge_desc->buff_size = MCLBYTES;
+		bus_dmamap_sync(sc->mge_desc_dtag, dw->desc_dmap,
+		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+	}
+
+	/* Enable RX queue */
+	MGE_WRITE(sc, MGE_RX_QUEUE_CMD, MGE_ENABLE_RXQ(MGE_RX_DEFAULT_QUEUE));
+
+	/* Enable port */
+	reg_val = MGE_READ(sc, MGE_PORT_SERIAL_CTRL);
+	reg_val |= PORT_SERIAL_ENABLE;
+	MGE_WRITE(sc, MGE_PORT_SERIAL_CTRL, reg_val);
+	count = 0x100000;
+	for (;;) {
+		reg_val = MGE_READ(sc, MGE_PORT_STATUS);
+		if (reg_val & MGE_STATUS_LINKUP)
+			break;
+		DELAY(100);
+		if (--count == 0) {
+			if_printf(sc->ifp, "Timeout on link-up\n");
+			break;
+		}
+	}
+
+	/* Setup interrupts coalescing */
+	mge_set_rxic(sc);
+	mge_set_txic(sc);
+
+	/* Enable interrupts */
+#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;
+	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	sc->wd_timer = 0;
+
+	/* Schedule watchdog timeout */
+	callout_reset(&sc->wd_callout, hz, mge_tick, sc);
+}
+
+static void
+mge_intr_err(void *arg)
+{
+	struct mge_softc *sc = arg;
+	struct ifnet *ifp;
+
+	ifp = sc->ifp;
+	if_printf(ifp, "%s\n", __FUNCTION__);
+}
+
+static void
+mge_intr_misc(void *arg)
+{
+	struct mge_softc *sc = arg;
+	struct ifnet *ifp;
+
+	ifp = sc->ifp;
+	if_printf(ifp, "%s\n", __FUNCTION__);
+}
+
+static void
+mge_intr_rx(void *arg) {
+	struct mge_softc *sc = arg;
+	uint32_t int_cause, int_cause_ext;
+
+	MGE_RECEIVE_LOCK(sc);
+
+#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_RXERRQ0) {
+		mge_reinit_rx(sc);
+		MGE_WRITE(sc, MGE_PORT_INT_CAUSE,
+		    int_cause & ~MGE_PORT_INT_RXERRQ0);
+	}
+
+	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);
+	}
+
+	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;
+
+	MGE_RECEIVE_LOCK_ASSERT(sc);
+
+	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);
+
+		/* Get status */
+		status = dw->mge_desc->cmd_status;
+		bufsize = dw->mge_desc->buff_size;
+		if ((status & MGE_DMA_OWNED) != 0)
+			break;
+
+		sc->rx_desc_curr = (++sc->rx_desc_curr % MGE_RX_DESC_NUM);
+		if (dw->mge_desc->byte_count &&
+		    ~(status & MGE_ERR_SUMMARY)) {
+
+			bus_dmamap_sync(sc->mge_rx_dtag, dw->buffer_dmap,
+			    BUS_DMASYNC_POSTREAD);
+
+			mb = m_devget(dw->buffer->m_data,
+			    dw->mge_desc->byte_count - ETHER_CRC_LEN,
+			    0, ifp, NULL);
+
+			mb->m_len -= 2;
+			mb->m_pkthdr.len -= 2;
+			mb->m_data += 2;
+
+			mge_offload_process_frame(ifp, mb, status,
+			    bufsize);
+
+			MGE_RECEIVE_UNLOCK(sc);
+			(*ifp->if_input)(ifp, mb);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list