[PATCH] Ethernet driver For BBB ported from FreeBSD

ragunath ragunath3252 at gmail.com
Thu Jun 25 16:18:40 UTC 2015


This patch includes changes for porting cpsw ethernet driver for Beaglebone black from Freebsd.
It also includes smsc phy ported from FreeBSD. Corresponding nexus devices are added.
For networking to work properly, two additional changes are needed on the RTEMS side.

---
 Makefile                                           |    3 +
 freebsd/sys/arm/ti/cpsw/if_cpsw.c                  | 2239 ++++++++++++++++++++
 freebsd/sys/arm/ti/cpsw/if_cpswreg.h               |  137 ++
 freebsd/sys/arm/ti/cpsw/if_cpswvar.h               |  126 ++
 freebsd/sys/dev/mii/smscphy.c                      |  227 ++
 rtemsbsd/include/bsp/nexus-devices.h               |   34 +
 rtemsbsd/include/machine/rtems-bsd-cache.h         |    2 +-
 .../include/rtems/bsd/test/network-config.h.in     |    2 +
 8 files changed, 2769 insertions(+), 1 deletion(-)
 create mode 100644 freebsd/sys/arm/ti/cpsw/if_cpsw.c
 create mode 100644 freebsd/sys/arm/ti/cpsw/if_cpswreg.h
 create mode 100644 freebsd/sys/arm/ti/cpsw/if_cpswvar.h
 create mode 100644 freebsd/sys/dev/mii/smscphy.c

diff --git a/Makefile b/Makefile
index f747106..e215a07 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,7 @@ COMMON_FLAGS += -Ifreebsd/contrib/tcpdump
 COMMON_FLAGS += -Ifreebsd/lib/libmemstat
 COMMON_FLAGS += -Ifreebsd/lib/libipsec
 COMMON_FLAGS += -Irtemsbsd/sys
+COMMON_FLAGS += -Irtemsbsd/include/rtems/bsd/local
 COMMON_FLAGS += -ImDNSResponder/mDNSCore
 COMMON_FLAGS += -ImDNSResponder/mDNSShared
 COMMON_FLAGS += -ImDNSResponder/mDNSPosix
@@ -185,10 +186,12 @@ LIB_C_FILES += freebsd/sys/dev/mii/brgphy.c
 LIB_C_FILES += freebsd/sys/dev/mii/micphy.c
 LIB_C_FILES += freebsd/sys/dev/mii/ukphy.c
 LIB_C_FILES += freebsd/sys/dev/mii/ukphy_subr.c
+LIB_C_FILES += freebsd/sys/dev/mii/smscphy.c
 LIB_C_FILES += freebsd/sys/dev/tsec/if_tsec.c
 LIB_C_FILES += freebsd/sys/dev/cadence/if_cgem.c
 LIB_C_FILES += freebsd/sys/dev/dwc/if_dwc.c
 LIB_C_FILES += freebsd/sys/arm/xilinx/zy7_slcr.c
+LIB_C_FILES += freebsd/sys/arm/ti/cpsw/if_cpsw.c
 LIB_C_FILES += freebsd/sys/dev/random/harvest.c
 LIB_C_FILES += freebsd/sys/netinet/tcp_hostcache.c
 LIB_C_FILES += freebsd/sys/dev/led/led.c
diff --git a/freebsd/sys/arm/ti/cpsw/if_cpsw.c b/freebsd/sys/arm/ti/cpsw/if_cpsw.c
new file mode 100644
index 0000000..0eb73ee
--- /dev/null
+++ b/freebsd/sys/arm/ti/cpsw/if_cpsw.c
@@ -0,0 +1,2239 @@
+#include <machine/rtems-bsd-kernel-space.h>
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion at Freebsd.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
+ */
+
+/*
+ * TI Common Platform Ethernet Switch (CPSW) Driver
+ * Found in TI8148 "DaVinci" and AM335x "Sitara" SoCs.
+ *
+ * This controller is documented in the AM335x Technical Reference
+ * Manual, in the TMS320DM814x DaVinci Digital Video Processors TRM
+ * and in the TMS320C6452 3 Port Switch Ethernet Subsystem TRM.
+ *
+ * It is basically a single Ethernet port (port 0) wired internally to
+ * a 3-port store-and-forward switch connected to two independent
+ * "sliver" controllers (port 1 and port 2).  You can operate the
+ * controller in a variety of different ways by suitably configuring
+ * the slivers and the Address Lookup Engine (ALE) that routes packets
+ * between the ports.
+ *
+ * This code was developed and tested on a BeagleBone with
+ * an AM335x SoC.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#ifndef __rtems__
+#include <sys/lock.h>
+#else
+#include <rtems/bsd/sys/lock.h>
+#endif
+#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_var.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>
+#ifndef __rtems__
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+#include "if_cpswreg.h"
+#include "if_cpswvar.h"
+#ifndef __rtems__
+#include <arm/ti/ti_scm.h>
+#endif
+#include "miibus_if.h"
+
+/* Device probe/attach/detach. */
+static int cpsw_probe(device_t);
+static void cpsw_init_slots(struct cpsw_softc *);
+static int cpsw_attach(device_t);
+static void cpsw_free_slot(struct cpsw_softc *, struct cpsw_slot *);
+static int cpsw_detach(device_t);
+
+/* Device Init/shutdown. */
+static void cpsw_init(void *);
+static void cpsw_init_locked(void *);
+static int cpsw_shutdown(device_t);
+static void cpsw_shutdown_locked(struct cpsw_softc *);
+
+/* Device Suspend/Resume. */
+static int cpsw_suspend(device_t);
+static int cpsw_resume(device_t);
+
+/* Ioctl. */
+static int cpsw_ioctl(struct ifnet *, u_long command, caddr_t data);
+
+static int cpsw_miibus_readreg(device_t, int phy, int reg);
+static int cpsw_miibus_writereg(device_t, int phy, int reg, int value);
+
+/* Send/Receive packets. */
+static void cpsw_intr_rx(void *arg);
+static struct mbuf *cpsw_rx_dequeue(struct cpsw_softc *);
+static void cpsw_rx_enqueue(struct cpsw_softc *);
+static void cpsw_start(struct ifnet *);
+static void cpsw_tx_enqueue(struct cpsw_softc *);
+static int cpsw_tx_dequeue(struct cpsw_softc *);
+
+/* Misc interrupts and watchdog. */
+static void cpsw_intr_rx_thresh(void *);
+static void cpsw_intr_misc(void *);
+static void cpsw_tick(void *);
+static void cpsw_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int cpsw_ifmedia_upd(struct ifnet *);
+static void cpsw_tx_watchdog(struct cpsw_softc *);
+
+/* ALE support */
+static void cpsw_ale_read_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry);
+static void cpsw_ale_write_entry(struct cpsw_softc *, uint16_t idx, uint32_t *ale_entry);
+static int cpsw_ale_mc_entry_set(struct cpsw_softc *, uint8_t portmap, uint8_t *mac);
+static int cpsw_ale_update_addresses(struct cpsw_softc *, int purge);
+static void cpsw_ale_dump_table(struct cpsw_softc *);
+
+/* Statistics and sysctls. */
+#ifndef __rtems__
+static void cpsw_add_sysctls(struct cpsw_softc *);
+static void cpsw_stats_collect(struct cpsw_softc *);
+static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS);
+#endif
+
+/*
+ * Arbitrary limit on number of segments in an mbuf to be transmitted.
+ * Packets with more segments than this will be defragmented before
+ * they are queued.
+ */
+#define CPSW_TXFRAGS 8
+
+
+/*
+ * TODO: The CPSW subsystem (CPSW_SS) can drive two independent PHYs
+ * as separate Ethernet ports.  To properly support this, we should
+ * break this into two separate devices: a CPSW_SS device that owns
+ * the interrupts and actually talks to the CPSW hardware, and a
+ * separate CPSW Ethernet child device for each Ethernet port.  The RX
+ * interrupt, for example, would be part of CPSW_SS; it would receive
+ * a packet, note the input port, and then dispatch it to the child
+ * device's interface queue.  Similarly for transmit.
+ *
+ * It's not clear to me whether the device tree should be restructured
+ * with a cpsw_ss node and two child nodes.  That would allow specifying
+ * MAC addresses for each port, for example, but might be overkill.
+ *
+ * Unfortunately, I don't have hardware right now that supports two
+ * Ethernet ports via CPSW.
+ */
+
+static device_method_t cpsw_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		cpsw_probe),
+	DEVMETHOD(device_attach,	cpsw_attach),
+	DEVMETHOD(device_detach,	cpsw_detach),
+	DEVMETHOD(device_shutdown,	cpsw_shutdown),
+	DEVMETHOD(device_suspend,	cpsw_suspend),
+	DEVMETHOD(device_resume,	cpsw_resume),
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	cpsw_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	cpsw_miibus_writereg),
+	{ 0, 0 }
+};
+
+static driver_t cpsw_driver = {
+	"cpsw",
+	cpsw_methods,
+	sizeof(struct cpsw_softc),
+};
+
+static devclass_t cpsw_devclass;
+
+#ifndef __rtems__
+DRIVER_MODULE(cpsw, simplebus, cpsw_driver, cpsw_devclass, 0, 0);
+#else
+DRIVER_MODULE(cpsw, nexus, cpsw_driver, cpsw_devclass, 0, 0);
+#endif
+DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(cpsw, ether, 1, 1, 1);
+MODULE_DEPEND(cpsw, 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 },
+	{ -1, 0 }
+};
+
+#ifndef __rtems__
+/* Number of entries here must match size of stats
+ * array in struct cpsw_softc. */
+static struct cpsw_stat {
+	int	reg;
+	char *oid;
+} cpsw_stat_sysctls[CPSW_SYSCTL_COUNT] = {
+	{0x00, "GoodRxFrames"},
+	{0x04, "BroadcastRxFrames"},
+	{0x08, "MulticastRxFrames"},
+	{0x0C, "PauseRxFrames"},
+	{0x10, "RxCrcErrors"},
+	{0x14, "RxAlignErrors"},
+	{0x18, "OversizeRxFrames"},
+	{0x1c, "RxJabbers"},
+	{0x20, "ShortRxFrames"},
+	{0x24, "RxFragments"},
+	{0x30, "RxOctets"},
+	{0x34, "GoodTxFrames"},
+	{0x38, "BroadcastTxFrames"},
+	{0x3c, "MulticastTxFrames"},
+	{0x40, "PauseTxFrames"},
+	{0x44, "DeferredTxFrames"},
+	{0x48, "CollisionsTxFrames"},
+	{0x4c, "SingleCollisionTxFrames"},
+	{0x50, "MultipleCollisionTxFrames"},
+	{0x54, "ExcessiveCollisions"},
+	{0x58, "LateCollisions"},
+	{0x5c, "TxUnderrun"},
+	{0x60, "CarrierSenseErrors"},
+	{0x64, "TxOctets"},
+	{0x68, "RxTx64OctetFrames"},
+	{0x6c, "RxTx65to127OctetFrames"},
+	{0x70, "RxTx128to255OctetFrames"},
+	{0x74, "RxTx256to511OctetFrames"},
+	{0x78, "RxTx512to1024OctetFrames"},
+	{0x7c, "RxTx1024upOctetFrames"},
+	{0x80, "NetOctets"},
+	{0x84, "RxStartOfFrameOverruns"},
+	{0x88, "RxMiddleOfFrameOverruns"},
+	{0x8c, "RxDmaOverruns"}
+};
+#endif
+
+/*
+ * Basic debug support.
+ */
+
+#define IF_DEBUG(sc)  if (sc->cpsw_if_flags & IFF_DEBUG)
+
+static void
+cpsw_debugf_head(const char *funcname)
+{
+	int t = (int)(time_second % (24 * 60 * 60));
+
+	printf("%02d:%02d:%02d %s ", t / (60 * 60), (t / 60) % 60, t % 60, funcname);
+}
+
+#include <machine/stdarg.h>
+static void
+cpsw_debugf(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+	printf("\n");
+
+}
+
+#define CPSW_DEBUGF(a) do {					\
+	IF_DEBUG(sc) {						\
+		cpsw_debugf_head(__func__);			\
+		cpsw_debugf a;					\
+	}							\
+} while (0)
+
+
+/*
+ * Locking macros
+ */
+#define CPSW_TX_LOCK(sc) do {					\
+		mtx_assert(&(sc)->rx.lock, MA_NOTOWNED);		\
+		mtx_lock(&(sc)->tx.lock);				\
+} while (0)
+
+#define CPSW_TX_UNLOCK(sc)	mtx_unlock(&(sc)->tx.lock)
+#define CPSW_TX_LOCK_ASSERT(sc)	mtx_assert(&(sc)->tx.lock, MA_OWNED)
+
+#define CPSW_RX_LOCK(sc) do {					\
+		mtx_assert(&(sc)->tx.lock, MA_NOTOWNED);		\
+		mtx_lock(&(sc)->rx.lock);				\
+} while (0)
+
+#define CPSW_RX_UNLOCK(sc)		mtx_unlock(&(sc)->rx.lock)
+#define CPSW_RX_LOCK_ASSERT(sc)	mtx_assert(&(sc)->rx.lock, MA_OWNED)
+
+#define CPSW_GLOBAL_LOCK(sc) do {					\
+		if ((mtx_owned(&(sc)->tx.lock) ? 1 : 0) !=	\
+		    (mtx_owned(&(sc)->rx.lock) ? 1 : 0)) {		\
+			panic("cpsw deadlock possibility detection!");	\
+		}							\
+		mtx_lock(&(sc)->tx.lock);				\
+		mtx_lock(&(sc)->rx.lock);				\
+} while (0)
+
+#define CPSW_GLOBAL_UNLOCK(sc) do {					\
+		CPSW_RX_UNLOCK(sc);				\
+		CPSW_TX_UNLOCK(sc);				\
+} while (0)
+
+#define CPSW_GLOBAL_LOCK_ASSERT(sc) do {				\
+		CPSW_TX_LOCK_ASSERT(sc);				\
+		CPSW_RX_LOCK_ASSERT(sc);				\
+} while (0)
+
+/*
+ * Read/Write macros
+ */
+#define	cpsw_read_4(sc, reg)		bus_read_4(sc->res[0], reg)
+#define	cpsw_write_4(sc, reg, val)	bus_write_4(sc->res[0], reg, val)
+#define BUS_SPACE_PHYSADDR(res, offs) \
+        ((u_int)(rman_get_start(res)+(offs)))
+#define CONTROL_MOD_BASE 0x44E10000
+#define cm_read(a)    (*(volatile uint32_t *)(a))
+#define cm_write(a,v) (*(volatile uint32_t *)(a) = (v))
+
+
+#define	cpsw_cpdma_bd_offset(i)	(CPSW_CPPI_RAM_OFFSET + ((i)*16))
+
+#define	cpsw_cpdma_bd_paddr(sc, slot)				\
+	BUS_SPACE_PHYSADDR(sc->res[0], slot->bd_offset)
+#define	cpsw_cpdma_read_bd(sc, slot, val)				\
+	bus_read_region_4(sc->res[0], slot->bd_offset, (uint32_t *) val, 4)
+#define	cpsw_cpdma_write_bd(sc, slot, val)				\
+	bus_write_region_4(sc->res[0], slot->bd_offset, (uint32_t *) val, 4)
+#define	cpsw_cpdma_write_bd_next(sc, slot, next_slot)			\
+	cpsw_write_4(sc, slot->bd_offset, cpsw_cpdma_bd_paddr(sc, next_slot))
+#define	cpsw_cpdma_read_bd_flags(sc, slot)		\
+	bus_read_2(sc->res[0], slot->bd_offset + 14)
+#define	cpsw_write_hdp_slot(sc, queue, slot)				\
+	cpsw_write_4(sc, (queue)->hdp_offset, cpsw_cpdma_bd_paddr(sc, slot))
+#define	CP_OFFSET (CPSW_CPDMA_TX_CP(0) - CPSW_CPDMA_TX_HDP(0))
+#define	cpsw_read_cp(sc, queue)				\
+	cpsw_read_4(sc, (queue)->hdp_offset + CP_OFFSET)
+#define	cpsw_write_cp(sc, queue, val)				\
+	cpsw_write_4(sc, (queue)->hdp_offset + CP_OFFSET, (val))
+#define	cpsw_write_cp_slot(sc, queue, slot)		\
+	cpsw_write_cp(sc, queue, cpsw_cpdma_bd_paddr(sc, slot))
+
+#if 0
+/* XXX temporary function versions for debugging. */
+static void
+cpsw_write_hdp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
+{
+	uint32_t reg = queue->hdp_offset;
+	uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
+	CPSW_DEBUGF(("HDP <=== 0x%08x (was 0x%08x)", v, cpsw_read_4(sc, reg)));
+	cpsw_write_4(sc, reg, v);
+}
+
+static void
+cpsw_write_cp_slotX(struct cpsw_softc *sc, struct cpsw_queue *queue, struct cpsw_slot *slot)
+{
+	uint32_t v = cpsw_cpdma_bd_paddr(sc, slot);
+	CPSW_DEBUGF(("CP <=== 0x%08x (expecting 0x%08x)", v, cpsw_read_cp(sc, queue)));
+	cpsw_write_cp(sc, queue, v);
+}
+#endif
+
+/*
+ * Expanded dump routines for verbose debugging.
+ */
+static void
+cpsw_dump_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
+{
+	static const char *flags[] = {"SOP", "EOP", "Owner", "EOQ",
+	    "TDownCmplt", "PassCRC", "Long", "Short", "MacCtl", "Overrun",
+	    "PktErr1", "PortEn/PktErr0", "RxVlanEncap", "Port2", "Port1",
+	    "Port0"};
+	struct cpsw_cpdma_bd bd;
+	const char *sep;
+	int i;
+
+	cpsw_cpdma_read_bd(sc, slot, &bd);
+	printf("BD Addr: 0x%08x   Next: 0x%08x\n", cpsw_cpdma_bd_paddr(sc, slot), bd.next);
+	printf("  BufPtr: 0x%08x   BufLen: 0x%08x\n", bd.bufptr, bd.buflen);
+	printf("  BufOff: 0x%08x   PktLen: 0x%08x\n", bd.bufoff, bd.pktlen);
+	printf("  Flags: ");
+	sep = "";
+	for (i = 0; i < 16; ++i) {
+		if (bd.flags & (1 << (15 - i))) {
+			printf("%s%s", sep, flags[i]);
+			sep = ",";
+		}
+	}
+	printf("\n");
+	if (slot->mbuf) {
+		printf("  Ether:  %14D\n",
+		    (char *)(slot->mbuf->m_data), " ");
+		printf("  Packet: %16D\n",
+		    (char *)(slot->mbuf->m_data) + 14, " ");
+	}
+}
+
+#define CPSW_DUMP_SLOT(cs, slot) do {				\
+	IF_DEBUG(sc) {						\
+		cpsw_dump_slot(sc, slot);			\
+	}							\
+} while (0)
+
+
+static void
+cpsw_dump_queue(struct cpsw_softc *sc, struct cpsw_slots *q)
+{
+	struct cpsw_slot *slot;
+	int i = 0;
+	int others = 0;
+
+	STAILQ_FOREACH(slot, q, next) {
+		if (i > 4)
+			++others;
+		else
+			cpsw_dump_slot(sc, slot);
+		++i;
+	}
+	if (others)
+		printf(" ... and %d more.\n", others);
+	printf("\n");
+}
+
+#define CPSW_DUMP_QUEUE(sc, q) do {				\
+	IF_DEBUG(sc) {						\
+		cpsw_dump_queue(sc, q);				\
+	}							\
+} while (0)
+
+
+/*
+ *
+ * Device Probe, Attach, Detach.
+ *
+ */
+
+static int
+cpsw_probe(device_t dev)
+{
+#ifndef __rtems__
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "ti,cpsw"))
+		return (ENXIO);
+#endif
+	device_set_desc(dev, "3-port Switch Ethernet Subsystem");
+	return (BUS_PROBE_DEFAULT);
+}
+
+
+static void
+cpsw_init_slots(struct cpsw_softc *sc)
+{
+	struct cpsw_slot *slot;
+	int i;
+
+	STAILQ_INIT(&sc->avail);
+
+	/* Put the slot descriptors onto the global avail list. */
+	for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); i++) {
+		slot = &sc->_slots[i];
+		slot->bd_offset = cpsw_cpdma_bd_offset(i);
+		STAILQ_INSERT_TAIL(&sc->avail, slot, next);
+	}
+}
+
+/*
+ * bind an interrupt, add the relevant info to sc->interrupts
+ */
+static int
+cpsw_attach_interrupt(struct cpsw_softc *sc, struct resource *res, driver_intr_t *handler, const char *description)
+{
+	void **pcookie;
+	int error;
+
+	sc->interrupts[sc->interrupt_count].res = res;
+	sc->interrupts[sc->interrupt_count].description = description;
+	pcookie = &sc->interrupts[sc->interrupt_count].ih_cookie;
+
+	error = bus_setup_intr(sc->dev, res, INTR_TYPE_NET | INTR_MPSAFE,
+	    NULL, *handler, sc, pcookie);
+	if (error)
+		device_printf(sc->dev,
+		    "could not setup %s\n", description);
+	else
+		++sc->interrupt_count;
+	return (error);
+}
+
+/*
+ * teardown everything in sc->interrupts.
+ */
+static void
+cpsw_detach_interrupts(struct cpsw_softc *sc)
+{
+	int error;
+	int i;
+
+	for (i = 0; i < sizeof(sc->interrupts) / sizeof(sc->interrupts[0]); ++i) {
+		if (!sc->interrupts[i].ih_cookie)
+			continue;
+		error = bus_teardown_intr(sc->dev,
+		    sc->interrupts[i].res, sc->interrupts[i].ih_cookie);
+		if (error)
+			device_printf(sc->dev, "could not release %s\n",
+			    sc->interrupts[i].description);
+		sc->interrupts[i].ih_cookie = NULL;
+	}
+}
+
+static int
+cpsw_add_slots(struct cpsw_softc *sc, struct cpsw_queue *queue, int requested)
+{
+	const int max_slots = sizeof(sc->_slots) / sizeof(sc->_slots[0]);
+	struct cpsw_slot *slot;
+	int i;
+
+	if (requested < 0)
+		requested = max_slots;
+
+	for (i = 0; i < requested; ++i) {
+		slot = STAILQ_FIRST(&sc->avail);
+		if (slot == NULL)
+			return (0);
+		if (bus_dmamap_create(sc->mbuf_dtag, 0, &slot->dmamap)) {
+			if_printf(sc->ifp, "failed to create dmamap\n");
+			return (ENOMEM);
+		}
+		STAILQ_REMOVE_HEAD(&sc->avail, next);
+		STAILQ_INSERT_TAIL(&queue->avail, slot, next);
+		++queue->avail_queue_len;
+		++queue->queue_slots;
+	}
+	return (0);
+}
+
+static int
+cpsw_attach(device_t dev)
+{
+	bus_dma_segment_t segs[1];
+	struct cpsw_softc *sc = device_get_softc(dev);
+	struct mii_softc *miisc;
+	struct ifnet *ifp;
+#ifndef __rtems__
+	void *phy_sc;
+	int phy;
+#endif
+	int error, nsegs;
+	uint32_t reg;
+
+	CPSW_DEBUGF((""));
+
+	getbinuptime(&sc->attach_uptime);
+	sc->dev = dev;
+#ifndef __rtems__
+	sc->node = ofw_bus_get_node(dev);
+	/* Get phy address from fdt */
+	if (fdt_get_phyaddr(sc->node, sc->dev, &phy, &phy_sc) != 0) {
+		device_printf(dev, "failed to get PHY address from FDT\n");
+		return (ENXIO);
+	}
+#endif
+	/* Initialize mutexes */
+	mtx_init(&sc->tx.lock, device_get_nameunit(dev),
+	    "cpsw TX lock", MTX_DEF);
+	mtx_init(&sc->rx.lock, device_get_nameunit(dev),
+	    "cpsw 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");
+		cpsw_detach(dev);
+		return (ENXIO);
+	}
+
+	reg = cpsw_read_4(sc, CPSW_SS_IDVER);
+	device_printf(dev, "CPSW SS Version %d.%d (%d)\n", (reg >> 8 & 0x7),
+		reg & 0xFF, (reg >> 11) & 0x1F);
+
+#ifndef __rtems__
+	cpsw_add_sysctls(sc);
+#endif
+
+	/* Allocate a busdma tag and DMA safe memory for mbufs. */
+	error = bus_dma_tag_create(
+		bus_get_dma_tag(sc->dev),	/* parent */
+		1, 0,				/* alignment, boundary */
+		BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+		BUS_SPACE_MAXADDR,		/* highaddr */
+		NULL, NULL,			/* filtfunc, filtfuncarg */
+		MCLBYTES, CPSW_TXFRAGS,		/* maxsize, nsegments */
+		MCLBYTES, 0,			/* maxsegsz, flags */
+		NULL, NULL,			/* lockfunc, lockfuncarg */
+		&sc->mbuf_dtag);		/* dmatag */
+	if (error) {
+		device_printf(dev, "bus_dma_tag_create failed\n");
+		cpsw_detach(dev);
+		return (error);
+	}
+
+	/* Allocate network interface */
+	ifp = sc->ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		device_printf(dev, "if_alloc() failed\n");
+		cpsw_detach(dev);
+		return (ENOMEM);
+	}
+
+	/* Allocate the null mbuf and pre-sync it. */
+	sc->null_mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+	memset(sc->null_mbuf->m_data, 0, sc->null_mbuf->m_ext.ext_size);
+	bus_dmamap_create(sc->mbuf_dtag, 0, &sc->null_mbuf_dmamap);
+	bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, sc->null_mbuf_dmamap,
+	    sc->null_mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
+	bus_dmamap_sync(sc->mbuf_dtag, sc->null_mbuf_dmamap,
+	    BUS_DMASYNC_PREWRITE);
+	sc->null_mbuf_paddr = segs[0].ds_addr;
+
+	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_VLAN_MTU | IFCAP_HWCSUM; //FIXME VLAN?
+	ifp->if_capenable = ifp->if_capabilities;
+
+	ifp->if_init = cpsw_init;
+	ifp->if_start = cpsw_start;
+	ifp->if_ioctl = cpsw_ioctl;
+
+	cpsw_init_slots(sc);
+
+	/* Allocate slots to TX and RX queues. */
+	STAILQ_INIT(&sc->rx.avail);
+	STAILQ_INIT(&sc->rx.active);
+	STAILQ_INIT(&sc->tx.avail);
+	STAILQ_INIT(&sc->tx.active);
+	// For now:  128 slots to TX, rest to RX.
+	// XXX TODO: start with 32/64 and grow dynamically based on demand.
+	if (cpsw_add_slots(sc, &sc->tx, 128) || cpsw_add_slots(sc, &sc->rx, -1)) {
+		device_printf(dev, "failed to allocate dmamaps\n");
+		cpsw_detach(dev);
+		return (ENOMEM);
+	}
+	device_printf(dev, "Initial queue size TX=%d RX=%d\n",
+	    sc->tx.queue_slots, sc->rx.queue_slots);
+
+	ifp->if_snd.ifq_drv_maxlen = sc->tx.queue_slots;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+	IFQ_SET_READY(&ifp->if_snd);
+
+	sc->tx.hdp_offset = CPSW_CPDMA_TX_HDP(0);
+	sc->rx.hdp_offset = CPSW_CPDMA_RX_HDP(0);
+
+	/* Get high part of MAC address from control module (mac_id0_hi) */
+	/* TODO: Get MAC ID1 as well as MAC ID0. */
+#ifndef __rtems__
+	ti_scm_reg_read_4(0x634, &reg);
+#else
+        reg = cm_read(CONTROL_MOD_BASE+0x634);
+#endif
+	sc->mac_addr[0] = reg & 0xFF;
+	sc->mac_addr[1] = (reg >>  8) & 0xFF;
+	sc->mac_addr[2] = (reg >> 16) & 0xFF;
+	sc->mac_addr[3] = (reg >> 24) & 0xFF;
+
+	/* Get low part of MAC address from control module (mac_id0_lo) */
+#ifndef __rtems__
+	ti_scm_reg_read_4(0x630, &reg);
+#else
+        reg = cm_read(CONTROL_MOD_BASE+0x630);
+#endif
+	sc->mac_addr[4] = reg & 0xFF;
+	sc->mac_addr[5] = (reg >>  8) & 0xFF;
+
+	/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
+	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
+	cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF);
+
+	/* Clear ALE */
+	cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 30);
+
+	/* Attach PHY(s) */
+	error = mii_attach(dev, &sc->miibus, ifp, cpsw_ifmedia_upd,
+	    cpsw_ifmedia_sts, BMSR_DEFCAPMASK,MII_PHY_ANY, MII_OFFSET_ANY, 0);
+	if (error) {
+		device_printf(dev, "attaching PHYs failed\n");
+		cpsw_detach(dev);
+		return (error);
+	}
+	sc->mii = device_get_softc(sc->miibus);
+
+	/* Tell the MAC where to find the PHY so autoneg works */
+	miisc = LIST_FIRST(&sc->mii->mii_phys);
+
+	/* Select PHY and enable interrupts */
+	cpsw_write_4(sc, MDIOUSERPHYSEL0, 1 << 6 | (miisc->mii_phy & 0x1F));
+
+	/* Note: We don't use sc->res[3] (TX interrupt) */
+	if (cpsw_attach_interrupt(sc, sc->res[1],
+		cpsw_intr_rx_thresh, "CPSW RX threshold interrupt") ||
+	    cpsw_attach_interrupt(sc, sc->res[2],
+		cpsw_intr_rx, "CPSW RX interrupt") ||
+	    cpsw_attach_interrupt(sc, sc->res[4],
+		cpsw_intr_misc, "CPSW misc interrupt")) {
+		cpsw_detach(dev);
+		return (ENXIO);
+	}
+
+	ether_ifattach(ifp, sc->mac_addr);
+	callout_init(&sc->watchdog.callout, 0);
+
+	return (0);
+}
+
+static void
+cpsw_free_slot(struct cpsw_softc *sc, struct cpsw_slot *slot)
+{
+	int error;
+
+	if (slot->dmamap) {
+		error = bus_dmamap_destroy(sc->mbuf_dtag, slot->dmamap);
+		KASSERT(error == 0, ("Mapping still active"));
+		slot->dmamap = NULL;
+	}
+	if (slot->mbuf) {
+		m_freem(slot->mbuf);
+		slot->mbuf = NULL;
+	}
+}
+
+static int
+cpsw_detach(device_t dev)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+	int error, i;
+
+	CPSW_DEBUGF((""));
+
+	/* Stop controller and free TX queue */
+	if (device_is_attached(dev)) {
+		ether_ifdetach(sc->ifp);
+		CPSW_GLOBAL_LOCK(sc);
+		cpsw_shutdown_locked(sc);
+		CPSW_GLOBAL_UNLOCK(sc);
+		callout_drain(&sc->watchdog.callout);
+	}
+
+	bus_generic_detach(dev);
+	if (sc->miibus)
+		device_delete_child(dev, sc->miibus);
+
+	/* Stop and release all interrupts */
+	cpsw_detach_interrupts(sc);
+
+	/* Free dmamaps and mbufs */
+	for (i = 0; i < sizeof(sc->_slots) / sizeof(sc->_slots[0]); ++i)
+		cpsw_free_slot(sc, &sc->_slots[i]);
+	if (sc->null_mbuf_dmamap) {
+		error = bus_dmamap_destroy(sc->mbuf_dtag, sc->null_mbuf_dmamap);
+		KASSERT(error == 0, ("Mapping still active"));
+	}
+	if (sc->null_mbuf)
+		m_freem(sc->null_mbuf);
+
+	/* Free DMA tag */
+	error = bus_dma_tag_destroy(sc->mbuf_dtag);
+	KASSERT(error == 0, ("Unable to destroy DMA tag"));
+
+	/* Free IO memory handler */
+	bus_release_resources(dev, res_spec, sc->res);
+
+	if (sc->ifp != NULL)
+		if_free(sc->ifp);
+
+	/* Destroy mutexes */
+	mtx_destroy(&sc->rx.lock);
+	mtx_destroy(&sc->tx.lock);
+
+	return (0);
+}
+
+/*
+ *
+ * Init/Shutdown.
+ *
+ */
+
+static void
+cpsw_reset(struct cpsw_softc *sc)
+{
+	int i;
+
+	/* Reset RMII/RGMII wrapper. */
+	cpsw_write_4(sc, CPSW_WR_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_WR_SOFT_RESET) & 1)
+		;
+
+	/* Disable TX and RX interrupts for all cores. */
+	for (i = 0; i < 3; ++i) {
+		cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_TX_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_RX_EN(i), 0x00);
+		cpsw_write_4(sc, CPSW_WR_C_MISC_EN(i), 0x00);
+	}
+
+	/* Reset CPSW subsystem. */
+	cpsw_write_4(sc, CPSW_SS_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_SS_SOFT_RESET) & 1)
+		;
+
+	/* Reset Sliver port 1 and 2 */
+	for (i = 0; i < 2; i++) {
+		/* Reset */
+		cpsw_write_4(sc, CPSW_SL_SOFT_RESET(i), 1);
+		while (cpsw_read_4(sc, CPSW_SL_SOFT_RESET(i)) & 1)
+			;
+	}
+
+	/* Reset DMA controller. */
+	cpsw_write_4(sc, CPSW_CPDMA_SOFT_RESET, 1);
+	while (cpsw_read_4(sc, CPSW_CPDMA_SOFT_RESET) & 1)
+		;
+
+	/* Disable TX & RX DMA */
+	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 0);
+	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 0);
+
+	/* Clear all queues. */
+	for (i = 0; i < 8; i++) {
+		cpsw_write_4(sc, CPSW_CPDMA_TX_HDP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_RX_HDP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_TX_CP(i), 0);
+		cpsw_write_4(sc, CPSW_CPDMA_RX_CP(i), 0);
+	}
+
+	/* Clear all interrupt Masks */
+	cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF);
+	cpsw_write_4(sc, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF);
+}
+
+static void
+cpsw_init(void *arg)
+{
+	struct cpsw_softc *sc = arg;
+
+	CPSW_DEBUGF((""));
+	CPSW_GLOBAL_LOCK(sc);
+	cpsw_init_locked(arg);
+	CPSW_GLOBAL_UNLOCK(sc);
+}
+
+static void
+cpsw_init_locked(void *arg)
+{
+	struct ifnet *ifp;
+	struct cpsw_softc *sc = arg;
+	struct cpsw_slot *slot;
+	uint32_t i;
+
+	CPSW_DEBUGF((""));
+	ifp = sc->ifp;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+		return;
+
+	getbinuptime(&sc->init_uptime);
+
+	/* Reset the controller. */
+	cpsw_reset(sc);
+
+	/* Enable ALE */
+	cpsw_write_4(sc, CPSW_ALE_CONTROL, 1 << 31 | 1 << 4);
+
+	/* Init Sliver port 1 and 2 */
+	for (i = 0; i < 2; i++) {
+		/* Set Slave Mapping */
+		cpsw_write_4(sc, CPSW_SL_RX_PRI_MAP(i), 0x76543210);
+		cpsw_write_4(sc, CPSW_PORT_P_TX_PRI_MAP(i + 1), 0x33221100);
+		cpsw_write_4(sc, CPSW_SL_RX_MAXLEN(i), 0x5f2);
+		/* Set MACCONTROL for ports 0,1: IFCTL_B(16), IFCTL_A(15),
+		   GMII_EN(5), FULLDUPLEX(1) */
+		/* TODO: Docs claim that IFCTL_B and IFCTL_A do the same thing? */
+		/* Huh?  Docs call bit 0 "Loopback" some places, "FullDuplex" others. */
+		cpsw_write_4(sc, CPSW_SL_MACCONTROL(i), 1 << 15 | 1 << 5 | 1);
+	}
+
+	/* Set Host Port Mapping */
+	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210);
+	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
+
+	/* Initialize ALE: all ports set to forwarding(3), initialize addrs */
+	for (i = 0; i < 3; i++)
+		cpsw_write_4(sc, CPSW_ALE_PORTCTL(i), 3);
+	cpsw_ale_update_addresses(sc, 1);
+
+	cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
+
+	/* Enable statistics for ports 0, 1 and 2 */
+	cpsw_write_4(sc, CPSW_SS_STAT_PORT_EN, 7);
+
+	/* Experiment:  Turn off flow control */
+	/* This seems to fix the watchdog resets that have plagued
+	   earlier versions of this driver; I'm not yet sure if there
+	   are negative effects yet. */
+	cpsw_write_4(sc, CPSW_SS_FLOW_CONTROL, 0);
+
+	/* Make IP hdr aligned with 4 */
+	cpsw_write_4(sc, CPSW_CPDMA_RX_BUFFER_OFFSET, 2);
+
+	/* Initialize RX Buffer Descriptors */
+	cpsw_write_4(sc, CPSW_CPDMA_RX_FREEBUFFER(0), 0);
+
+	/* Enable TX & RX DMA */
+	cpsw_write_4(sc, CPSW_CPDMA_TX_CONTROL, 1);
+	cpsw_write_4(sc, CPSW_CPDMA_RX_CONTROL, 1);
+
+	/* Enable Interrupts for core 0 */
+	cpsw_write_4(sc, CPSW_WR_C_RX_THRESH_EN(0), 0xFF);
+	cpsw_write_4(sc, CPSW_WR_C_RX_EN(0), 0xFF);
+	cpsw_write_4(sc, CPSW_WR_C_MISC_EN(0), 0x3F);
+
+	/* Enable host Error Interrupt */
+	cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_SET, 3);
+
+	/* Enable interrupts for RX Channel 0 */
+	cpsw_write_4(sc, CPSW_CPDMA_RX_INTMASK_SET, 1);
+
+	/* Initialze MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
+	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
+	cpsw_write_4(sc, MDIOCONTROL, 1 << 30 | 1 << 18 | 0xFF);
+
+	/* Select MII in GMII_SEL, Internal Delay mode */
+	//ti_scm_reg_write_4(0x650, 0);
+
+	/* Initialize active queues. */
+	slot = STAILQ_FIRST(&sc->tx.active);
+	if (slot != NULL)
+		cpsw_write_hdp_slot(sc, &sc->tx, slot);
+	slot = STAILQ_FIRST(&sc->rx.active);
+	if (slot != NULL)
+		cpsw_write_hdp_slot(sc, &sc->rx, slot);
+	cpsw_rx_enqueue(sc);
+
+	/* Activate network interface */
+	sc->rx.running = 1;
+	sc->tx.running = 1;
+	sc->watchdog.timer = 0;
+	callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc);
+	sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
+	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+}
+
+static int
+cpsw_shutdown(device_t dev)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+
+	CPSW_DEBUGF((""));
+	CPSW_GLOBAL_LOCK(sc);
+	cpsw_shutdown_locked(sc);
+	CPSW_GLOBAL_UNLOCK(sc);
+	return (0);
+}
+
+static void
+cpsw_rx_teardown_locked(struct cpsw_softc *sc)
+{
+	struct mbuf *received, *next;
+	int i = 0;
+
+	CPSW_DEBUGF(("starting RX teardown"));
+	cpsw_write_4(sc, CPSW_CPDMA_RX_TEARDOWN, 0);
+	for (;;) {
+		received = cpsw_rx_dequeue(sc);
+		CPSW_GLOBAL_UNLOCK(sc);
+		while (received != NULL) {
+			next = received->m_nextpkt;
+			received->m_nextpkt = NULL;
+			(*sc->ifp->if_input)(sc->ifp, received);
+			received = next;
+		}
+		CPSW_GLOBAL_LOCK(sc);
+		if (!sc->rx.running) {
+			CPSW_DEBUGF(("finished RX teardown (%d retries)", i));
+			return;
+		}
+		if (++i > 10) {
+			if_printf(sc->ifp, "Unable to cleanly shutdown receiver\n");
+			return;
+		}
+		DELAY(10);
+	}
+}
+
+static void
+cpsw_tx_teardown_locked(struct cpsw_softc *sc)
+{
+	int i = 0;
+
+	CPSW_DEBUGF(("starting TX teardown"));
+	cpsw_write_4(sc, CPSW_CPDMA_TX_TEARDOWN, 0);
+	cpsw_tx_dequeue(sc);
+	while (sc->tx.running && ++i < 10) {
+		DELAY(10);
+		cpsw_tx_dequeue(sc);
+	}
+	if (sc->tx.running)
+		if_printf(sc->ifp, "Unable to cleanly shutdown transmitter\n");
+	CPSW_DEBUGF(("finished TX teardown (%d retries, %d idle buffers)",
+	    i, sc->tx.active_queue_len));
+}
+
+static void
+cpsw_shutdown_locked(struct cpsw_softc *sc)
+{
+	struct ifnet *ifp;
+
+	CPSW_DEBUGF((""));
+	CPSW_GLOBAL_LOCK_ASSERT(sc);
+	ifp = sc->ifp;
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		return;
+
+	/* Disable interface */
+	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+	ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+
+	/* Stop ticker */
+	callout_stop(&sc->watchdog.callout);
+
+	/* Tear down the RX/TX queues. */
+	cpsw_rx_teardown_locked(sc);
+	cpsw_tx_teardown_locked(sc);
+
+#ifndef __rtems__
+	/* Capture stats before we reset controller. */
+	cpsw_stats_collect(sc);
+#endif
+
+	cpsw_reset(sc);
+}
+
+/*
+ *  Suspend/Resume.
+ */
+
+static int
+cpsw_suspend(device_t dev)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+
+	CPSW_DEBUGF((""));
+	CPSW_GLOBAL_LOCK(sc);
+	cpsw_shutdown_locked(sc);
+	CPSW_GLOBAL_UNLOCK(sc);
+	return (0);
+}
+
+static int
+cpsw_resume(device_t dev)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+
+	CPSW_DEBUGF(("UNIMPLEMENTED"));
+	return (0);
+}
+
+/*
+ *
+ *  IOCTL
+ *
+ */
+
+static void
+cpsw_set_promisc(struct cpsw_softc *sc, int set)
+{
+	/*
+	 * Enabling promiscuous mode requires two bits of work: First,
+	 * ALE_BYPASS needs to be enabled.  That disables the ALE
+	 * forwarding logic and causes every packet to be sent to the
+	 * host port.  That makes us promiscuous wrt received packets.
+	 *
+	 * With ALE forwarding disabled, the transmitter needs to set
+	 * an explicit output port on every packet to route it to the
+	 * correct egress.  This should be doable for systems such as
+	 * BeagleBone where only one egress port is actually wired to
+	 * a PHY.  If you have both egress ports wired up, life gets a
+	 * lot more interesting.
+	 *
+	 * Hmmm.... NetBSD driver uses ALE_BYPASS always and doesn't
+	 * seem to set explicit egress ports.  Does that mean they
+	 * are always promiscuous?
+	 */
+	if (set) {
+		printf("Promiscuous mode unimplemented\n");
+	}
+}
+
+static void
+cpsw_set_allmulti(struct cpsw_softc *sc, int set)
+{
+	if (set) {
+		printf("All-multicast mode unimplemented\n");
+	}
+}
+
+static int
+cpsw_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+	struct cpsw_softc *sc = ifp->if_softc;
+	struct ifreq *ifr = (struct ifreq *)data;
+	int error;
+	uint32_t changed;
+
+	error = 0;
+
+	switch (command) {
+	case SIOCSIFFLAGS:
+		CPSW_GLOBAL_LOCK(sc);
+		if (ifp->if_flags & IFF_UP) {
+			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+				changed = ifp->if_flags ^ sc->cpsw_if_flags;
+				CPSW_DEBUGF(("SIOCSIFFLAGS: UP & RUNNING (changed=0x%x)", changed));
+				if (changed & IFF_PROMISC)
+					cpsw_set_promisc(sc,
+					    ifp->if_flags & IFF_PROMISC);
+				if (changed & IFF_ALLMULTI)
+					cpsw_set_allmulti(sc,
+					    ifp->if_flags & IFF_ALLMULTI);
+			} else {
+				CPSW_DEBUGF(("SIOCSIFFLAGS: UP but not RUNNING; starting up"));
+				cpsw_init_locked(sc);
+			}
+		} else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+			CPSW_DEBUGF(("SIOCSIFFLAGS: not UP but RUNNING; shutting down"));
+			cpsw_shutdown_locked(sc);
+		}
+
+		sc->cpsw_if_flags = ifp->if_flags;
+		CPSW_GLOBAL_UNLOCK(sc);
+		break;
+	case SIOCADDMULTI:
+		cpsw_ale_update_addresses(sc, 0);
+		break;
+	case SIOCDELMULTI:
+		/* Ugh.  DELMULTI doesn't provide the specific address
+		   being removed, so the best we can do is remove
+		   everything and rebuild it all. */
+		cpsw_ale_update_addresses(sc, 1);
+		break;
+	case SIOCGIFMEDIA:
+	case SIOCSIFMEDIA:
+		error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media, command);
+		break;
+	default:
+		error = ether_ioctl(ifp, command, data);
+	}
+	return (error);
+}
+
+/*
+ *
+ * MIIBUS
+ *
+ */
+static int
+cpsw_miibus_ready(struct cpsw_softc *sc)
+{
+	uint32_t r, retries = CPSW_MIIBUS_RETRIES;
+
+	while (--retries) {
+		r = cpsw_read_4(sc, MDIOUSERACCESS0);
+		if ((r & 1 << 31) == 0)
+			return 1;
+		DELAY(CPSW_MIIBUS_DELAY);
+	}
+	return 0;
+}
+
+static int
+cpsw_miibus_readreg(device_t dev, int phy, int reg)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+	uint32_t cmd, r;
+
+	if (!cpsw_miibus_ready(sc)) {
+		device_printf(dev, "MDIO not ready to read\n");
+		return 0;
+	}
+
+	/* Set GO, reg, phy */
+	cmd = 1 << 31 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16;
+	cpsw_write_4(sc, MDIOUSERACCESS0, cmd);
+
+	if (!cpsw_miibus_ready(sc)) {
+		device_printf(dev, "MDIO timed out during read\n");
+		return 0;
+	}
+
+	r = cpsw_read_4(sc, MDIOUSERACCESS0);
+	if((r & 1 << 29) == 0) {
+		CPSW_DEBUGF(("Failed to read from PHY.\n"));
+		r = 0;
+	}
+	return (r & 0xFFFF);
+}
+
+static int
+cpsw_miibus_writereg(device_t dev, int phy, int reg, int value)
+{
+	struct cpsw_softc *sc = device_get_softc(dev);
+	uint32_t cmd;
+
+	if (!cpsw_miibus_ready(sc)) {
+		device_printf(dev, "MDIO not ready to write\n");
+		return 0;
+	}
+
+	/* Set GO, WRITE, reg, phy, and value */
+	cmd = 3 << 30 | (reg & 0x1F) << 21 | (phy & 0x1F) << 16
+	    | (value & 0xFFFF);
+	cpsw_write_4(sc, MDIOUSERACCESS0, cmd);
+
+	if (!cpsw_miibus_ready(sc)) {
+		device_printf(dev, "MDIO timed out during write\n");
+		return 0;
+	}
+
+	if((cpsw_read_4(sc, MDIOUSERACCESS0) & (1 << 29)) == 0)
+		CPSW_DEBUGF(("Failed to write to PHY.\n"));
+
+	return 0;
+}
+
+/*
+ *
+ * Transmit/Receive Packets.
+ *
+ */
+
+
+static void
+cpsw_intr_rx(void *arg)
+{
+	struct cpsw_softc *sc = arg;
+	struct mbuf *received, *next;
+
+	CPSW_RX_LOCK(sc);
+	received = cpsw_rx_dequeue(sc);
+	cpsw_rx_enqueue(sc);
+	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 1);
+	CPSW_RX_UNLOCK(sc);
+
+	while (received != NULL) {
+		next = received->m_nextpkt;
+		received->m_nextpkt = NULL;
+		(*sc->ifp->if_input)(sc->ifp, received);
+		received = next;
+	}
+}
+
+static struct mbuf *
+cpsw_rx_dequeue(struct cpsw_softc *sc)
+{
+	struct cpsw_cpdma_bd bd;
+	struct cpsw_slot *slot;
+	struct ifnet *ifp;
+	struct mbuf *mb_head, *mb_tail;
+	int removed = 0;
+
+	ifp = sc->ifp;
+	mb_head = mb_tail = NULL;
+
+	/* Pull completed packets off hardware RX queue. */
+	while ((slot = STAILQ_FIRST(&sc->rx.active)) != NULL) {
+		cpsw_cpdma_read_bd(sc, slot, &bd);
+		if (bd.flags & CPDMA_BD_OWNER)
+			break; /* Still in use by hardware */
+
+		CPSW_DEBUGF(("Removing received packet from RX queue"));
+		++removed;
+		STAILQ_REMOVE_HEAD(&sc->rx.active, next);
+		STAILQ_INSERT_TAIL(&sc->rx.avail, slot, next);
+
+		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTREAD);
+		bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+
+		if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
+			CPSW_DEBUGF(("RX teardown in progress"));
+			m_freem(slot->mbuf);
+			slot->mbuf = NULL;
+			cpsw_write_cp(sc, &sc->rx, 0xfffffffc);
+			sc->rx.running = 0;
+			break;
+		}
+
+		cpsw_write_cp_slot(sc, &sc->rx, slot);
+
+		/* Set up mbuf */
+		/* TODO: track SOP/EOP bits to assemble a full mbuf
+		   out of received fragments. */
+		slot->mbuf->m_data += bd.bufoff;
+		slot->mbuf->m_len = bd.pktlen - 4;
+		slot->mbuf->m_pkthdr.len = bd.pktlen - 4;
+		slot->mbuf->m_flags |= M_PKTHDR;
+		slot->mbuf->m_pkthdr.rcvif = ifp;
+		slot->mbuf->m_nextpkt = NULL;
+
+		if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) {
+			/* check for valid CRC by looking into pkt_err[5:4] */
+			if ((bd.flags & CPDMA_BD_PKT_ERR_MASK) == 0) {
+				slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
+				slot->mbuf->m_pkthdr.csum_flags |= CSUM_IP_VALID;
+				slot->mbuf->m_pkthdr.csum_data = 0xffff;
+			}
+		}
+
+		/* Add mbuf to packet list to be returned. */
+		if (mb_tail) {
+			mb_tail->m_nextpkt = slot->mbuf;
+		} else {
+			mb_head = slot->mbuf;
+		}
+		mb_tail = slot->mbuf;
+		slot->mbuf = NULL;
+	}
+
+	if (removed != 0) {
+		sc->rx.queue_removes += removed;
+		sc->rx.active_queue_len -= removed;
+		sc->rx.avail_queue_len += removed;
+		if (sc->rx.avail_queue_len > sc->rx.max_avail_queue_len)
+			sc->rx.max_avail_queue_len = sc->rx.avail_queue_len;
+	}
+	return (mb_head);
+}
+
+static void
+cpsw_rx_enqueue(struct cpsw_softc *sc)
+{
+	bus_dma_segment_t seg[1];
+	struct cpsw_cpdma_bd bd;
+	struct ifnet *ifp = sc->ifp;
+	struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue);
+	struct cpsw_slot *slot, *prev_slot = NULL;
+	struct cpsw_slot *last_old_slot, *first_new_slot;
+	int error, nsegs, added = 0;
+
+	/* Register new mbufs with hardware. */
+	while ((slot = STAILQ_FIRST(&sc->rx.avail)) != NULL) {
+		if (slot->mbuf == NULL) {
+			slot->mbuf = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+			if (slot->mbuf == NULL) {
+				if_printf(sc->ifp, "Unable to fill RX queue\n");
+				break;
+			}
+			slot->mbuf->m_len =
+			    slot->mbuf->m_pkthdr.len =
+			    slot->mbuf->m_ext.ext_size;
+		}
+
+		error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap,
+		    slot->mbuf, seg, &nsegs, BUS_DMA_NOWAIT);
+
+		KASSERT(nsegs == 1, ("More than one segment (nsegs=%d)", nsegs));
+		KASSERT(error == 0, ("DMA error (error=%d)", error));
+		if (error != 0 || nsegs != 1) {
+			if_printf(ifp,
+			    "%s: Can't prep RX buf for DMA (nsegs=%d, error=%d)\n",
+			    __func__, nsegs, error);
+			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+			m_freem(slot->mbuf);
+			slot->mbuf = NULL;
+			break;
+		}
+
+		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_PREREAD);
+
+		/* Create and submit new rx descriptor*/
+		bd.next = 0;
+		bd.bufptr = seg->ds_addr;
+		bd.bufoff = 0;
+		bd.buflen = MCLBYTES - 1;
+		bd.pktlen = bd.buflen;
+		bd.flags = CPDMA_BD_OWNER;
+		cpsw_cpdma_write_bd(sc, slot, &bd);
+		++added;
+
+		if (prev_slot != NULL)
+			cpsw_cpdma_write_bd_next(sc, prev_slot, slot);
+		prev_slot = slot;
+		STAILQ_REMOVE_HEAD(&sc->rx.avail, next);
+		sc->rx.avail_queue_len--;
+		STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
+	}
+
+	if (added == 0)
+		return;
+
+	CPSW_DEBUGF(("Adding %d buffers to RX queue", added));
+
+	/* Link new entries to hardware RX queue. */
+	last_old_slot = STAILQ_LAST(&sc->rx.active, cpsw_slot, next);
+	first_new_slot = STAILQ_FIRST(&tmpqueue);
+	STAILQ_CONCAT(&sc->rx.active, &tmpqueue);
+	if (first_new_slot == NULL) {
+		return;
+	} else if (last_old_slot == NULL) {
+		/* Start a fresh queue. */
+		cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
+	} else {
+		/* Add buffers to end of current queue. */
+		cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot);
+		/* If underrun, restart queue. */
+		if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) {
+			cpsw_write_hdp_slot(sc, &sc->rx, first_new_slot);
+		}
+	}
+	sc->rx.queue_adds += added;
+	sc->rx.active_queue_len += added;
+	if (sc->rx.active_queue_len > sc->rx.max_active_queue_len) {
+		sc->rx.max_active_queue_len = sc->rx.active_queue_len;
+	}
+}
+
+static void
+cpsw_start(struct ifnet *ifp)
+{
+	struct cpsw_softc *sc = ifp->if_softc;
+
+	CPSW_TX_LOCK(sc);
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) && sc->tx.running) {
+		cpsw_tx_enqueue(sc);
+		cpsw_tx_dequeue(sc);
+	}
+	CPSW_TX_UNLOCK(sc);
+}
+
+static void
+cpsw_tx_enqueue(struct cpsw_softc *sc)
+{
+	bus_dma_segment_t segs[CPSW_TXFRAGS];
+	struct cpsw_cpdma_bd bd;
+	struct cpsw_slots tmpqueue = STAILQ_HEAD_INITIALIZER(tmpqueue);
+	struct cpsw_slot *slot, *prev_slot = NULL;
+	struct cpsw_slot *last_old_slot, *first_new_slot;
+	struct mbuf *m0;
+	int error, nsegs, seg, added = 0, padlen;
+
+	/* Pull pending packets from IF queue and prep them for DMA. */
+	while ((slot = STAILQ_FIRST(&sc->tx.avail)) != NULL) {
+		IF_DEQUEUE(&sc->ifp->if_snd, m0);
+		if (m0 == NULL)
+			break;
+
+		slot->mbuf = m0;
+		padlen = ETHER_MIN_LEN - slot->mbuf->m_pkthdr.len;
+		if (padlen < 0)
+			padlen = 0;
+
+		/* Create mapping in DMA memory */
+		error = bus_dmamap_load_mbuf_sg(sc->mbuf_dtag, slot->dmamap,
+		    slot->mbuf, segs, &nsegs, BUS_DMA_NOWAIT);
+		/* If the packet is too fragmented, try to simplify. */
+		if (error == EFBIG ||
+		    (error == 0 &&
+			nsegs + (padlen > 0 ? 1 : 0) > sc->tx.avail_queue_len)) {
+			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+			if (padlen > 0) /* May as well add padding. */
+				m_append(slot->mbuf, padlen,
+				    sc->null_mbuf->m_data);
+			m0 = m_defrag(slot->mbuf, M_NOWAIT);
+			if (m0 == NULL) {
+				if_printf(sc->ifp,
+				    "Can't defragment packet; dropping\n");
+				m_freem(slot->mbuf);
+			} else {
+				CPSW_DEBUGF(("Requeueing defragmented packet"));
+				IF_PREPEND(&sc->ifp->if_snd, m0);
+			}
+			slot->mbuf = NULL;
+			continue;
+		}
+		if (error != 0) {
+			if_printf(sc->ifp,
+			    "%s: Can't setup DMA (error=%d), dropping packet\n",
+			    __func__, error);
+			bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+			m_freem(slot->mbuf);
+			slot->mbuf = NULL;
+			break;
+		}
+
+		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap,
+				BUS_DMASYNC_PREWRITE);
+
+
+		CPSW_DEBUGF(("Queueing TX packet: %d segments + %d pad bytes",
+			nsegs, padlen));
+
+		/* If there is only one segment, the for() loop
+		 * gets skipped and the single buffer gets set up
+		 * as both SOP and EOP. */
+		/* Start by setting up the first buffer */
+		bd.next = 0;
+		bd.bufptr = segs[0].ds_addr;
+		bd.bufoff = 0;
+		bd.buflen = segs[0].ds_len;
+		bd.pktlen = m_length(slot->mbuf, NULL) + padlen;
+		bd.flags =  CPDMA_BD_SOP | CPDMA_BD_OWNER;
+		for (seg = 1; seg < nsegs; ++seg) {
+			/* Save the previous buffer (which isn't EOP) */
+			cpsw_cpdma_write_bd(sc, slot, &bd);
+			if (prev_slot != NULL)
+				cpsw_cpdma_write_bd_next(sc, prev_slot, slot);
+			prev_slot = slot;
+			STAILQ_REMOVE_HEAD(&sc->tx.avail, next);
+			sc->tx.avail_queue_len--;
+			STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
+			++added;
+			slot = STAILQ_FIRST(&sc->tx.avail);
+
+			/* Setup next buffer (which isn't SOP) */
+			bd.next = 0;
+			bd.bufptr = segs[seg].ds_addr;
+			bd.bufoff = 0;
+			bd.buflen = segs[seg].ds_len;
+			bd.pktlen = 0;
+			bd.flags = CPDMA_BD_OWNER;
+		}
+		/* Save the final buffer. */
+		if (padlen <= 0)
+			bd.flags |= CPDMA_BD_EOP;
+		cpsw_cpdma_write_bd(sc, slot, &bd);
+		if (prev_slot != NULL)
+			cpsw_cpdma_write_bd_next(sc, prev_slot, slot);
+		prev_slot = slot;
+		STAILQ_REMOVE_HEAD(&sc->tx.avail, next);
+		sc->tx.avail_queue_len--;
+		STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
+		++added;
+
+		if (padlen > 0) {
+			slot = STAILQ_FIRST(&sc->tx.avail);
+			STAILQ_REMOVE_HEAD(&sc->tx.avail, next);
+			sc->tx.avail_queue_len--;
+			STAILQ_INSERT_TAIL(&tmpqueue, slot, next);
+			++added;
+
+			/* Setup buffer of null pad bytes (definitely EOP) */
+			cpsw_cpdma_write_bd_next(sc, prev_slot, slot);
+			prev_slot = slot;
+			bd.next = 0;
+			bd.bufptr = sc->null_mbuf_paddr;
+			bd.bufoff = 0;
+			bd.buflen = padlen;
+			bd.pktlen = 0;
+			bd.flags = CPDMA_BD_EOP | CPDMA_BD_OWNER;
+			cpsw_cpdma_write_bd(sc, slot, &bd);
+			++nsegs;
+		}
+
+		if (nsegs > sc->tx.longest_chain)
+			sc->tx.longest_chain = nsegs;
+
+		// TODO: Should we defer the BPF tap until
+		// after all packets are queued?
+		BPF_MTAP(sc->ifp, m0);
+	}
+
+	/* Attach the list of new buffers to the hardware TX queue. */
+	last_old_slot = STAILQ_LAST(&sc->tx.active, cpsw_slot, next);
+	first_new_slot = STAILQ_FIRST(&tmpqueue);
+	STAILQ_CONCAT(&sc->tx.active, &tmpqueue);
+	if (first_new_slot == NULL) {
+		return;
+	} else if (last_old_slot == NULL) {
+		/* Start a fresh queue. */
+		cpsw_write_hdp_slot(sc, &sc->tx, first_new_slot);
+	} else {
+		/* Add buffers to end of current queue. */
+		cpsw_cpdma_write_bd_next(sc, last_old_slot, first_new_slot);
+		/* If underrun, restart queue. */
+		if (cpsw_cpdma_read_bd_flags(sc, last_old_slot) & CPDMA_BD_EOQ) {
+			cpsw_write_hdp_slot(sc, &sc->tx, first_new_slot);
+		}
+	}
+	sc->tx.queue_adds += added;
+	sc->tx.active_queue_len += added;
+	if (sc->tx.active_queue_len > sc->tx.max_active_queue_len) {
+		sc->tx.max_active_queue_len = sc->tx.active_queue_len;
+	}
+}
+
+static int
+cpsw_tx_dequeue(struct cpsw_softc *sc)
+{
+	struct cpsw_slot *slot, *last_removed_slot = NULL;
+	uint32_t flags, removed = 0;
+
+	slot = STAILQ_FIRST(&sc->tx.active);
+	if (slot == NULL && cpsw_read_cp(sc, &sc->tx) == 0xfffffffc) {
+		CPSW_DEBUGF(("TX teardown of an empty queue"));
+		cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
+		sc->tx.running = 0;
+		return (0);
+	}
+
+	/* Pull completed buffers off the hardware TX queue. */
+	while (slot != NULL) {
+		flags = cpsw_cpdma_read_bd_flags(sc, slot);
+		if (flags & CPDMA_BD_OWNER)
+			break; /* Hardware is still using this packet. */
+
+		CPSW_DEBUGF(("TX removing completed packet"));
+		bus_dmamap_sync(sc->mbuf_dtag, slot->dmamap, BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->mbuf_dtag, slot->dmamap);
+		m_freem(slot->mbuf);
+		slot->mbuf = NULL;
+
+		/* Dequeue any additional buffers used by this packet. */
+		while (slot != NULL && slot->mbuf == NULL) {
+			STAILQ_REMOVE_HEAD(&sc->tx.active, next);
+			STAILQ_INSERT_TAIL(&sc->tx.avail, slot, next);
+			++removed;
+			last_removed_slot = slot;
+			slot = STAILQ_FIRST(&sc->tx.active);
+		}
+
+		/* TearDown complete is only marked on the SOP for the packet. */
+		if (flags & CPDMA_BD_TDOWNCMPLT) {
+			CPSW_DEBUGF(("TX teardown in progress"));
+			cpsw_write_cp(sc, &sc->tx, 0xfffffffc);
+			// TODO: Increment a count of dropped TX packets
+			sc->tx.running = 0;
+			break;
+		}
+	}
+
+	if (removed != 0) {
+		cpsw_write_cp_slot(sc, &sc->tx, last_removed_slot);
+		sc->tx.queue_removes += removed;
+		sc->tx.active_queue_len -= removed;
+		sc->tx.avail_queue_len += removed;
+		if (sc->tx.avail_queue_len > sc->tx.max_avail_queue_len)
+			sc->tx.max_avail_queue_len = sc->tx.avail_queue_len;
+	}
+	return (removed);
+}
+
+/*
+ *
+ * Miscellaneous interrupts.
+ *
+ */
+
+static void
+cpsw_intr_rx_thresh(void *arg)
+{
+	struct cpsw_softc *sc = arg;
+	uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_RX_THRESH_STAT(0));
+
+	CPSW_DEBUGF(("stat=%x", stat));
+	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 0);
+}
+
+static void
+cpsw_intr_misc_host_error(struct cpsw_softc *sc)
+{
+	uint32_t intstat;
+	uint32_t dmastat;
+	int txerr, rxerr, txchan, rxchan;
+
+	printf("\n\n");
+	device_printf(sc->dev,
+	    "HOST ERROR:  PROGRAMMING ERROR DETECTED BY HARDWARE\n");
+	printf("\n\n");
+	intstat = cpsw_read_4(sc, CPSW_CPDMA_DMA_INTSTAT_MASKED);
+	device_printf(sc->dev, "CPSW_CPDMA_DMA_INTSTAT_MASKED=0x%x\n", intstat);
+	dmastat = cpsw_read_4(sc, CPSW_CPDMA_DMASTATUS);
+	device_printf(sc->dev, "CPSW_CPDMA_DMASTATUS=0x%x\n", dmastat);
+
+	txerr = (dmastat >> 20) & 15;
+	txchan = (dmastat >> 16) & 7;
+	rxerr = (dmastat >> 12) & 15;
+	rxchan = (dmastat >> 8) & 7;
+
+	switch (txerr) {
+	case 0: break;
+	case 1:	printf("SOP error on TX channel %d\n", txchan);
+		break;
+	case 2:	printf("Ownership bit not set on SOP buffer on TX channel %d\n", txchan);
+		break;
+	case 3:	printf("Zero Next Buffer but not EOP on TX channel %d\n", txchan);
+		break;
+	case 4:	printf("Zero Buffer Pointer on TX channel %d\n", txchan);
+		break;
+	case 5:	printf("Zero Buffer Length on TX channel %d\n", txchan);
+		break;
+	case 6:	printf("Packet length error on TX channel %d\n", txchan);
+		break;
+	default: printf("Unknown error on TX channel %d\n", txchan);
+		break;
+	}
+
+	if (txerr != 0) {
+		printf("CPSW_CPDMA_TX%d_HDP=0x%x\n",
+		    txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_HDP(txchan)));
+		printf("CPSW_CPDMA_TX%d_CP=0x%x\n",
+		    txchan, cpsw_read_4(sc, CPSW_CPDMA_TX_CP(txchan)));
+		cpsw_dump_queue(sc, &sc->tx.active);
+	}
+
+	switch (rxerr) {
+	case 0: break;
+	case 2:	printf("Ownership bit not set on RX channel %d\n", rxchan);
+		break;
+	case 4:	printf("Zero Buffer Pointer on RX channel %d\n", rxchan);
+		break;
+	case 5:	printf("Zero Buffer Length on RX channel %d\n", rxchan);
+		break;
+	case 6:	printf("Buffer offset too big on RX channel %d\n", rxchan);
+		break;
+	default: printf("Unknown RX error on RX channel %d\n", rxchan);
+		break;
+	}
+
+	if (rxerr != 0) {
+		printf("CPSW_CPDMA_RX%d_HDP=0x%x\n",
+		    rxchan, cpsw_read_4(sc,CPSW_CPDMA_RX_HDP(rxchan)));
+		printf("CPSW_CPDMA_RX%d_CP=0x%x\n",
+		    rxchan, cpsw_read_4(sc, CPSW_CPDMA_RX_CP(rxchan)));
+		cpsw_dump_queue(sc, &sc->rx.active);
+	}
+
+	printf("\nALE Table\n");
+	cpsw_ale_dump_table(sc);
+
+	// XXX do something useful here??
+	panic("CPSW HOST ERROR INTERRUPT");
+
+	// Suppress this interrupt in the future.
+	cpsw_write_4(sc, CPSW_CPDMA_DMA_INTMASK_CLEAR, intstat);
+	printf("XXX HOST ERROR INTERRUPT SUPPRESSED\n");
+	// The watchdog will probably reset the controller
+	// in a little while.  It will probably fail again.
+}
+
+static void
+cpsw_intr_misc(void *arg)
+{
+	struct cpsw_softc *sc = arg;
+	uint32_t stat = cpsw_read_4(sc, CPSW_WR_C_MISC_STAT(0));
+
+	if (stat & 16)
+		CPSW_DEBUGF(("Time sync event interrupt unimplemented"));
+#ifndef __rtems__
+	if (stat & 8)
+		cpsw_stats_collect(sc);
+#endif
+	if (stat & 4)
+		cpsw_intr_misc_host_error(sc);
+	if (stat & 2)
+		CPSW_DEBUGF(("MDIO link change interrupt unimplemented"));
+	if (stat & 1)
+		CPSW_DEBUGF(("MDIO operation completed interrupt unimplemented"));
+	cpsw_write_4(sc, CPSW_CPDMA_CPDMA_EOI_VECTOR, 3);
+}
+
+/*
+ *
+ * Periodic Checks and Watchdog.
+ *
+ */
+
+static void
+cpsw_tick(void *msc)
+{
+	struct cpsw_softc *sc = msc;
+
+	/* Check for TX timeout */
+	cpsw_tx_watchdog(sc);
+
+	/* Check for media type change */
+	mii_tick(sc->mii);
+	if(sc->cpsw_media_status != sc->mii->mii_media.ifm_media) {
+		printf("%s: media type changed (ifm_media=%x)\n", __func__,
+			sc->mii->mii_media.ifm_media);
+		cpsw_ifmedia_upd(sc->ifp);
+	}
+
+	/* Schedule another timeout one second from now */
+	callout_reset(&sc->watchdog.callout, hz, cpsw_tick, sc);
+}
+
+static void
+cpsw_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct cpsw_softc *sc = ifp->if_softc;
+	struct mii_data *mii;
+
+	CPSW_DEBUGF((""));
+	CPSW_TX_LOCK(sc);
+
+	mii = sc->mii;
+	mii_pollstat(mii);
+
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+
+	CPSW_TX_UNLOCK(sc);
+}
+
+static int
+cpsw_ifmedia_upd(struct ifnet *ifp)
+{
+	struct cpsw_softc *sc = ifp->if_softc;
+
+	CPSW_DEBUGF((""));
+	if (ifp->if_flags & IFF_UP) {
+		CPSW_GLOBAL_LOCK(sc);
+		sc->cpsw_media_status = sc->mii->mii_media.ifm_media;
+		mii_mediachg(sc->mii);
+		cpsw_init_locked(sc);
+		CPSW_GLOBAL_UNLOCK(sc);
+	}
+
+	return (0);
+}
+
+static void
+cpsw_tx_watchdog_full_reset(struct cpsw_softc *sc)
+{
+	cpsw_debugf_head("CPSW watchdog");
+	if_printf(sc->ifp, "watchdog timeout\n");
+	cpsw_shutdown_locked(sc);
+	cpsw_init_locked(sc);
+}
+
+static void
+cpsw_tx_watchdog(struct cpsw_softc *sc)
+{
+	struct ifnet *ifp = sc->ifp;
+
+	CPSW_GLOBAL_LOCK(sc);
+	if (sc->tx.active_queue_len == 0 || (ifp->if_flags & IFF_UP) == 0 ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || !sc->tx.running) {
+		sc->watchdog.timer = 0; /* Nothing to do. */
+	} else if (sc->tx.queue_removes > sc->tx.queue_removes_at_last_tick) {
+		sc->watchdog.timer = 0;  /* Stuff done while we weren't looking. */
+	} else if (cpsw_tx_dequeue(sc) > 0) {
+		sc->watchdog.timer = 0;  /* We just did something. */
+	} else {
+		/* There was something to do but it didn't get done. */
+		++sc->watchdog.timer;
+		if (sc->watchdog.timer > 2) {
+			sc->watchdog.timer = 0;
+#ifndef __rtems__
+			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+#else
+                        sc->ifp->if_oerrors++;
+#endif
+			++sc->watchdog.resets;
+			cpsw_tx_watchdog_full_reset(sc);
+		}
+	}
+	sc->tx.queue_removes_at_last_tick = sc->tx.queue_removes;
+	CPSW_GLOBAL_UNLOCK(sc);
+}
+
+/*
+ *
+ * ALE support routines.
+ *
+ */
+
+static void
+cpsw_ale_read_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+	cpsw_write_4(sc, CPSW_ALE_TBLCTL, idx & 1023);
+	ale_entry[0] = cpsw_read_4(sc, CPSW_ALE_TBLW0);
+	ale_entry[1] = cpsw_read_4(sc, CPSW_ALE_TBLW1);
+	ale_entry[2] = cpsw_read_4(sc, CPSW_ALE_TBLW2);
+}
+
+static void
+cpsw_ale_write_entry(struct cpsw_softc *sc, uint16_t idx, uint32_t *ale_entry)
+{
+	cpsw_write_4(sc, CPSW_ALE_TBLW0, ale_entry[0]);
+	cpsw_write_4(sc, CPSW_ALE_TBLW1, ale_entry[1]);
+	cpsw_write_4(sc, CPSW_ALE_TBLW2, ale_entry[2]);
+	cpsw_write_4(sc, CPSW_ALE_TBLCTL, 1 << 31 | (idx & 1023));
+}
+
+static int
+cpsw_ale_remove_all_mc_entries(struct cpsw_softc *sc)
+{
+	int i;
+	uint32_t ale_entry[3];
+
+	/* First two entries are link address and broadcast. */
+	for (i = 2; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+		if (((ale_entry[1] >> 28) & 3) == 1 && /* Address entry */
+		    ((ale_entry[1] >> 8) & 1) == 1) { /* MCast link addr */
+			ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+			cpsw_ale_write_entry(sc, i, ale_entry);
+		}
+	}
+	return CPSW_MAX_ALE_ENTRIES;
+}
+
+static int
+cpsw_ale_mc_entry_set(struct cpsw_softc *sc, uint8_t portmap, uint8_t *mac)
+{
+	int free_index = -1, matching_index = -1, i;
+	uint32_t ale_entry[3];
+
+	/* Find a matching entry or a free entry. */
+	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+
+		/* Entry Type[61:60] is 0 for free entry */
+		if (free_index < 0 && ((ale_entry[1] >> 28) & 3) == 0) {
+			free_index = i;
+		}
+
+		if ((((ale_entry[1] >> 8) & 0xFF) == mac[0]) &&
+		    (((ale_entry[1] >> 0) & 0xFF) == mac[1]) &&
+		    (((ale_entry[0] >>24) & 0xFF) == mac[2]) &&
+		    (((ale_entry[0] >>16) & 0xFF) == mac[3]) &&
+		    (((ale_entry[0] >> 8) & 0xFF) == mac[4]) &&
+		    (((ale_entry[0] >> 0) & 0xFF) == mac[5])) {
+			matching_index = i;
+			break;
+		}
+	}
+
+	if (matching_index < 0) {
+		if (free_index < 0)
+			return (ENOMEM);
+		i = free_index;
+	}
+
+	/* Set MAC address */
+	ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+	ale_entry[1] = mac[0] << 8 | mac[1];
+
+	/* Entry type[61:60] is addr entry(1), Mcast fwd state[63:62] is fw(3)*/
+	ale_entry[1] |= 0xd0 << 24;
+
+	/* Set portmask [68:66] */
+	ale_entry[2] = (portmap & 7) << 2;
+
+	cpsw_ale_write_entry(sc, i, ale_entry);
+
+	return 0;
+}
+
+static void
+cpsw_ale_dump_table(struct cpsw_softc *sc) {
+	int i;
+	uint32_t ale_entry[3];
+	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+		if (ale_entry[0] || ale_entry[1] || ale_entry[2]) {
+			printf("ALE[%4u] %08x %08x %08x ", i, ale_entry[0],
+				ale_entry[1], ale_entry[2]);
+			printf("mac: %02x:%02x:%02x:%02x:%02x:%02x ",
+				(ale_entry[1] >> 8) & 0xFF,
+				(ale_entry[1] >> 0) & 0xFF,
+				(ale_entry[0] >>24) & 0xFF,
+				(ale_entry[0] >>16) & 0xFF,
+				(ale_entry[0] >> 8) & 0xFF,
+				(ale_entry[0] >> 0) & 0xFF);
+			printf(((ale_entry[1] >> 8) & 1) ? "mcast " : "ucast ");
+			printf("type: %u ", (ale_entry[1] >> 28) & 3);
+			printf("port: %u ", (ale_entry[2] >> 2) & 7);
+			printf("\n");
+		}
+	}
+	printf("\n");
+}
+
+static int
+cpsw_ale_update_addresses(struct cpsw_softc *sc, int purge)
+{
+	uint8_t *mac;
+	uint32_t ale_entry[3];
+	struct ifnet *ifp = sc->ifp;
+	struct ifmultiaddr *ifma;
+	int i;
+
+	/* Route incoming packets for our MAC address to Port 0 (host). */
+	/* For simplicity, keep this entry at table index 0 in the ALE. */
+        if_addr_rlock(ifp);
+	mac = LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr);
+	ale_entry[0] = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+	ale_entry[1] = 0x10 << 24 | mac[0] << 8 | mac[1]; /* addr entry + mac */
+	ale_entry[2] = 0; /* port = 0 */
+	cpsw_ale_write_entry(sc, 0, ale_entry);
+
+	/* Set outgoing MAC Address for Ports 1 and 2. */
+	for (i = 1; i < 3; ++i) {
+		cpsw_write_4(sc, CPSW_PORT_P_SA_HI(i),
+		    mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]);
+		cpsw_write_4(sc, CPSW_PORT_P_SA_LO(i),
+		    mac[5] << 8 | mac[4]);
+	}
+        if_addr_runlock(ifp);
+
+	/* Keep the broadcast address at table entry 1. */
+	ale_entry[0] = 0xffffffff; /* Lower 32 bits of MAC */
+	ale_entry[1] = 0xd000ffff; /* FW (3 << 30), Addr entry (1 << 24), upper 16 bits of Mac */
+	ale_entry[2] = 0x0000001c; /* Forward to all ports */
+	cpsw_ale_write_entry(sc, 1, ale_entry);
+
+	/* SIOCDELMULTI doesn't specify the particular address
+	   being removed, so we have to remove all and rebuild. */
+	if (purge)
+		cpsw_ale_remove_all_mc_entries(sc);
+
+        /* Set other multicast addrs desired. */
+        if_maddr_rlock(ifp);
+        TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+                if (ifma->ifma_addr->sa_family != AF_LINK)
+                        continue;
+		cpsw_ale_mc_entry_set(sc, 7,
+		    LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+        }
+        if_maddr_runlock(ifp);
+
+	return (0);
+}
+
+/*
+ *
+ * Statistics and Sysctls.
+ *
+ */
+
+#if 0
+static void
+cpsw_stats_dump(struct cpsw_softc *sc)
+{
+	int i;
+	uint32_t r;
+
+	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+		r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
+		    cpsw_stat_sysctls[i].reg);
+		CPSW_DEBUGF(("%s: %ju + %u = %ju", cpsw_stat_sysctls[i].oid,
+			     (intmax_t)sc->shadow_stats[i], r,
+			     (intmax_t)sc->shadow_stats[i] + r));
+	}
+}
+#endif
+
+#ifndef __rtems__
+static void
+cpsw_stats_collect(struct cpsw_softc *sc)
+{
+	int i;
+	uint32_t r;
+
+	CPSW_DEBUGF(("Controller shadow statistics updated."));
+
+	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+		r = cpsw_read_4(sc, CPSW_STATS_OFFSET +
+		    cpsw_stat_sysctls[i].reg);
+		sc->shadow_stats[i] += r;
+		cpsw_write_4(sc, CPSW_STATS_OFFSET + cpsw_stat_sysctls[i].reg, r);
+	}
+}
+
+static int
+cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	struct cpsw_softc *sc;
+	struct cpsw_stat *stat;
+	uint64_t result;
+
+	sc = (struct cpsw_softc *)arg1;
+	stat = &cpsw_stat_sysctls[oidp->oid_number];
+	result = sc->shadow_stats[oidp->oid_number];
+	result += cpsw_read_4(sc, CPSW_STATS_OFFSET + stat->reg);
+	return (sysctl_handle_64(oidp, &result, 0, req));
+}
+
+static int
+cpsw_stat_attached(SYSCTL_HANDLER_ARGS)
+{
+	struct cpsw_softc *sc;
+	struct bintime t;
+	unsigned result;
+
+	sc = (struct cpsw_softc *)arg1;
+	getbinuptime(&t);
+	bintime_sub(&t, &sc->attach_uptime);
+	result = t.sec;
+	return (sysctl_handle_int(oidp, &result, 0, req));
+}
+
+static int
+cpsw_stat_uptime(SYSCTL_HANDLER_ARGS)
+{
+	struct cpsw_softc *sc;
+	struct bintime t;
+	unsigned result;
+
+	sc = (struct cpsw_softc *)arg1;
+	if (sc->ifp->if_drv_flags & IFF_DRV_RUNNING) {
+		getbinuptime(&t);
+		bintime_sub(&t, &sc->init_uptime);
+		result = t.sec;
+	} else
+		result = 0;
+	return (sysctl_handle_int(oidp, &result, 0, req));
+}
+
+static void
+cpsw_add_queue_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_queue *queue)
+{
+	struct sysctl_oid_list *parent;
+
+	parent = SYSCTL_CHILDREN(node);
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "totalBuffers",
+	    CTLFLAG_RD, &queue->queue_slots, 0,
+	    "Total buffers currently assigned to this queue");
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "activeBuffers",
+	    CTLFLAG_RD, &queue->active_queue_len, 0,
+	    "Buffers currently registered with hardware controller");
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxActiveBuffers",
+	    CTLFLAG_RD, &queue->max_active_queue_len, 0,
+	    "Max value of activeBuffers since last driver reset");
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "availBuffers",
+	    CTLFLAG_RD, &queue->avail_queue_len, 0,
+	    "Buffers allocated to this queue but not currently "
+	    "registered with hardware controller");
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "maxAvailBuffers",
+	    CTLFLAG_RD, &queue->max_avail_queue_len, 0,
+	    "Max value of availBuffers since last driver reset");
+	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalEnqueued",
+	    CTLFLAG_RD, &queue->queue_adds, 0,
+	    "Total buffers added to queue");
+	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "totalDequeued",
+	    CTLFLAG_RD, &queue->queue_removes, 0,
+	    "Total buffers removed from queue");
+	SYSCTL_ADD_UINT(ctx, parent, OID_AUTO, "longestChain",
+	    CTLFLAG_RD, &queue->longest_chain, 0,
+	    "Max buffers used for a single packet");
+}
+
+static void
+cpsw_add_watchdog_sysctls(struct sysctl_ctx_list *ctx, struct sysctl_oid *node, struct cpsw_softc *sc)
+{
+	struct sysctl_oid_list *parent;
+
+	parent = SYSCTL_CHILDREN(node);
+	SYSCTL_ADD_INT(ctx, parent, OID_AUTO, "resets",
+	    CTLFLAG_RD, &sc->watchdog.resets, 0,
+	    "Total number of watchdog resets");
+}
+
+static void
+cpsw_add_sysctls(struct cpsw_softc *sc)
+{
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid *stats_node, *queue_node, *node;
+	struct sysctl_oid_list *parent, *stats_parent, *queue_parent;
+	int i;
+
+	ctx = device_get_sysctl_ctx(sc->dev);
+	parent = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
+
+	SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "attachedSecs",
+	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_attached, "IU",
+	    "Time since driver attach");
+
+	SYSCTL_ADD_PROC(ctx, parent, OID_AUTO, "uptime",
+	    CTLTYPE_UINT | CTLFLAG_RD, sc, 0, cpsw_stat_uptime, "IU",
+	    "Seconds since driver init");
+
+	stats_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "stats",
+				     CTLFLAG_RD, NULL, "CPSW Statistics");
+	stats_parent = SYSCTL_CHILDREN(stats_node);
+	for (i = 0; i < CPSW_SYSCTL_COUNT; ++i) {
+		SYSCTL_ADD_PROC(ctx, stats_parent, i,
+				cpsw_stat_sysctls[i].oid,
+				CTLTYPE_U64 | CTLFLAG_RD, sc, 0,
+				cpsw_stats_sysctl, "IU",
+				cpsw_stat_sysctls[i].oid);
+	}
+
+	queue_node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "queue",
+	    CTLFLAG_RD, NULL, "CPSW Queue Statistics");
+	queue_parent = SYSCTL_CHILDREN(queue_node);
+
+	node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "tx",
+	    CTLFLAG_RD, NULL, "TX Queue Statistics");
+	cpsw_add_queue_sysctls(ctx, node, &sc->tx);
+
+	node = SYSCTL_ADD_NODE(ctx, queue_parent, OID_AUTO, "rx",
+	    CTLFLAG_RD, NULL, "RX Queue Statistics");
+	cpsw_add_queue_sysctls(ctx, node, &sc->rx);
+
+	node = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "watchdog",
+	    CTLFLAG_RD, NULL, "Watchdog Statistics");
+	cpsw_add_watchdog_sysctls(ctx, node, sc);
+}
+#endif
diff --git a/freebsd/sys/arm/ti/cpsw/if_cpswreg.h b/freebsd/sys/arm/ti/cpsw/if_cpswreg.h
new file mode 100644
index 0000000..46f8417
--- /dev/null
+++ b/freebsd/sys/arm/ti/cpsw/if_cpswreg.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion at Freebsd.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_IF_CPSWREG_H
+#define	_IF_CPSWREG_H
+
+#define CPSW_SS_OFFSET			0x0000
+#define CPSW_SS_IDVER			(CPSW_SS_OFFSET + 0x00)
+#define CPSW_SS_SOFT_RESET		(CPSW_SS_OFFSET + 0x08)
+#define CPSW_SS_STAT_PORT_EN		(CPSW_SS_OFFSET + 0x0C)
+#define CPSW_SS_PTYPE			(CPSW_SS_OFFSET + 0x10)
+#define	CPSW_SS_FLOW_CONTROL		(CPSW_SS_OFFSET + 0x24)
+
+#define CPSW_PORT_OFFSET		0x0100
+#define	CPSW_PORT_P_MAX_BLKS(p)		(CPSW_PORT_OFFSET + 0x08 + ((p) * 0x100))
+#define	CPSW_PORT_P_BLK_CNT(p)		(CPSW_PORT_OFFSET + 0x0C + ((p) * 0x100))
+#define CPSW_PORT_P_TX_PRI_MAP(p)	(CPSW_PORT_OFFSET + 0x118 + ((p-1) * 0x100))
+#define CPSW_PORT_P0_CPDMA_TX_PRI_MAP	(CPSW_PORT_OFFSET + 0x01C)
+#define CPSW_PORT_P0_CPDMA_RX_CH_MAP	(CPSW_PORT_OFFSET + 0x020)
+#define CPSW_PORT_P_SA_LO(p)		(CPSW_PORT_OFFSET + 0x120 + ((p-1) * 0x100))
+#define CPSW_PORT_P_SA_HI(p)		(CPSW_PORT_OFFSET + 0x124 + ((p-1) * 0x100))
+
+#define CPSW_CPDMA_OFFSET		0x0800
+#define CPSW_CPDMA_TX_CONTROL		(CPSW_CPDMA_OFFSET + 0x04)
+#define CPSW_CPDMA_TX_TEARDOWN		(CPSW_CPDMA_OFFSET + 0x08)
+#define CPSW_CPDMA_RX_CONTROL		(CPSW_CPDMA_OFFSET + 0x14)
+#define CPSW_CPDMA_RX_TEARDOWN		(CPSW_CPDMA_OFFSET + 0x18)
+#define CPSW_CPDMA_SOFT_RESET		(CPSW_CPDMA_OFFSET + 0x1c)
+#define CPSW_CPDMA_DMACONTROL		(CPSW_CPDMA_OFFSET + 0x20)
+#define CPSW_CPDMA_DMASTATUS		(CPSW_CPDMA_OFFSET + 0x24)
+#define CPSW_CPDMA_RX_BUFFER_OFFSET	(CPSW_CPDMA_OFFSET + 0x28)
+#define CPSW_CPDMA_TX_INTSTAT_RAW	(CPSW_CPDMA_OFFSET + 0x80)
+#define CPSW_CPDMA_TX_INTSTAT_MASKED	(CPSW_CPDMA_OFFSET + 0x84)
+#define CPSW_CPDMA_TX_INTMASK_SET	(CPSW_CPDMA_OFFSET + 0x88)
+#define CPSW_CPDMA_TX_INTMASK_CLEAR	(CPSW_CPDMA_OFFSET + 0x8C)
+#define CPSW_CPDMA_CPDMA_EOI_VECTOR	(CPSW_CPDMA_OFFSET + 0x94)
+#define CPSW_CPDMA_RX_INTSTAT_RAW	(CPSW_CPDMA_OFFSET + 0xA0)
+#define CPSW_CPDMA_RX_INTSTAT_MASKED	(CPSW_CPDMA_OFFSET + 0xA4)
+#define CPSW_CPDMA_RX_INTMASK_SET	(CPSW_CPDMA_OFFSET + 0xA8)
+#define CPSW_CPDMA_RX_INTMASK_CLEAR	(CPSW_CPDMA_OFFSET + 0xAc)
+#define CPSW_CPDMA_DMA_INTSTAT_RAW	(CPSW_CPDMA_OFFSET + 0xB0)
+#define CPSW_CPDMA_DMA_INTSTAT_MASKED	(CPSW_CPDMA_OFFSET + 0xB4)
+#define CPSW_CPDMA_DMA_INTMASK_SET	(CPSW_CPDMA_OFFSET + 0xB8)
+#define CPSW_CPDMA_DMA_INTMASK_CLEAR	(CPSW_CPDMA_OFFSET + 0xBC)
+#define CPSW_CPDMA_RX_FREEBUFFER(p)	(CPSW_CPDMA_OFFSET + 0x0e0 + ((p) * 0x04))
+
+#define CPSW_STATS_OFFSET		0x0900
+
+#define CPSW_STATERAM_OFFSET		0x0A00
+#define CPSW_CPDMA_TX_HDP(p)		(CPSW_STATERAM_OFFSET + 0x00 + ((p) * 0x04))
+#define CPSW_CPDMA_RX_HDP(p)		(CPSW_STATERAM_OFFSET + 0x20 + ((p) * 0x04))
+#define CPSW_CPDMA_TX_CP(p)		(CPSW_STATERAM_OFFSET + 0x40 + ((p) * 0x04))
+#define CPSW_CPDMA_RX_CP(p)		(CPSW_STATERAM_OFFSET + 0x60 + ((p) * 0x04))
+
+#define CPSW_CPTS_OFFSET		0x0C00
+
+#define CPSW_ALE_OFFSET			0x0D00
+#define CPSW_ALE_CONTROL		(CPSW_ALE_OFFSET + 0x08)
+#define CPSW_ALE_TBLCTL			(CPSW_ALE_OFFSET + 0x20)
+#define CPSW_ALE_TBLW2			(CPSW_ALE_OFFSET + 0x34)
+#define CPSW_ALE_TBLW1			(CPSW_ALE_OFFSET + 0x38)
+#define CPSW_ALE_TBLW0			(CPSW_ALE_OFFSET + 0x3C)
+#define CPSW_ALE_PORTCTL(p)		(CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04))
+
+/* SL1 is at 0x0D80, SL2 is at 0x0DC0 */
+#define CPSW_SL_OFFSET			0x0D80
+#define CPSW_SL_MACCONTROL(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x04)
+#define CPSW_SL_MACSTATUS(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x08)
+#define CPSW_SL_SOFT_RESET(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x0C)
+#define CPSW_SL_RX_MAXLEN(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x10)
+#define CPSW_SL_RX_PAUSE(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x18)
+#define CPSW_SL_TX_PAUSE(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x1C)
+#define CPSW_SL_RX_PRI_MAP(p)		(CPSW_SL_OFFSET + (0x40 * (p)) + 0x24)
+
+#define MDIO_OFFSET			0x1000
+#define MDIOCONTROL			(MDIO_OFFSET + 0x04)
+#define MDIOUSERACCESS0			(MDIO_OFFSET + 0x80)
+#define MDIOUSERPHYSEL0			(MDIO_OFFSET + 0x84)
+
+#define CPSW_WR_OFFSET			0x1200
+#define CPSW_WR_SOFT_RESET		(CPSW_WR_OFFSET + 0x04)
+#define CPSW_WR_CONTROL			(CPSW_WR_OFFSET + 0x08)
+#define CPSW_WR_INT_CONTROL		(CPSW_WR_OFFSET + 0x0c)
+#define CPSW_WR_C_RX_THRESH_EN(p)	(CPSW_WR_OFFSET + (0x10 * (p)) + 0x10)
+#define CPSW_WR_C_RX_EN(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x14)
+#define CPSW_WR_C_TX_EN(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x18)
+#define CPSW_WR_C_MISC_EN(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x1C)
+#define CPSW_WR_C_RX_THRESH_STAT(p)	(CPSW_WR_OFFSET + (0x10 * (p)) + 0x40)
+#define CPSW_WR_C_RX_STAT(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x44)
+#define CPSW_WR_C_TX_STAT(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x48)
+#define CPSW_WR_C_MISC_STAT(p)		(CPSW_WR_OFFSET + (0x10 * (p)) + 0x4C)
+
+#define CPSW_CPPI_RAM_OFFSET		0x2000
+#define	CPSW_CPPI_RAM_SIZE		0x2000
+
+#define CPDMA_BD_SOP		(1<<15)
+#define CPDMA_BD_EOP		(1<<14)
+#define CPDMA_BD_OWNER		(1<<13)
+#define CPDMA_BD_EOQ		(1<<12)
+#define CPDMA_BD_TDOWNCMPLT	(1<<11)
+#define CPDMA_BD_PKT_ERR_MASK	(3<< 4)
+
+struct cpsw_cpdma_bd {
+	volatile uint32_t next;
+	volatile uint32_t bufptr;
+	volatile uint16_t buflen;
+	volatile uint16_t bufoff;
+	volatile uint16_t pktlen;
+	volatile uint16_t flags;
+};
+
+#endif /*_IF_CPSWREG_H */
diff --git a/freebsd/sys/arm/ti/cpsw/if_cpswvar.h b/freebsd/sys/arm/ti/cpsw/if_cpswvar.h
new file mode 100644
index 0000000..7ca640f
--- /dev/null
+++ b/freebsd/sys/arm/ti/cpsw/if_cpswvar.h
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion at Freebsd.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 THE 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_IF_CPSWVAR_H
+#define	_IF_CPSWVAR_H
+
+#define CPSW_INTR_COUNT		4
+
+/* MII BUS  */
+#define CPSW_MIIBUS_RETRIES	5
+#define CPSW_MIIBUS_DELAY	1000
+
+#define CPSW_MAX_ALE_ENTRIES	1024
+
+#define CPSW_SYSCTL_COUNT 34
+
+struct cpsw_slot {
+	uint32_t bd_offset;  /* Offset of corresponding BD within CPPI RAM. */
+	bus_dmamap_t dmamap;
+	struct mbuf *mbuf;
+	STAILQ_ENTRY(cpsw_slot) next;
+};
+STAILQ_HEAD(cpsw_slots, cpsw_slot);
+
+struct cpsw_queue {
+	struct mtx	lock;
+	int		running;
+	struct cpsw_slots active;
+	struct cpsw_slots avail;
+	uint32_t	queue_adds; /* total bufs added */
+	uint32_t	queue_removes; /* total bufs removed */
+	uint32_t	queue_removes_at_last_tick; /* Used by watchdog */
+	int		queue_slots;
+	int		active_queue_len;
+	int		max_active_queue_len;
+	int		avail_queue_len;
+	int		max_avail_queue_len;
+	int		longest_chain; /* Largest # segments in a single packet. */
+	int		hdp_offset;
+};
+
+struct cpsw_softc {
+	struct ifnet	*ifp;
+#ifndef __rtems__
+	phandle_t	node;
+#endif
+	device_t	dev;
+	struct bintime	attach_uptime; /* system uptime when attach happened. */
+	struct bintime	init_uptime; /* system uptime when init happened. */
+
+	/* TODO: We should set up a child structure for each port;
+	   store mac, phy information, etc, in that structure. */
+	uint8_t		mac_addr[ETHER_ADDR_LEN];
+
+	device_t	miibus;
+	struct mii_data	*mii;
+	/* We expect 1 memory resource and 4 interrupts from the device tree. */
+	struct resource	*res[1 + CPSW_INTR_COUNT];
+
+	/* Interrupts get recorded here as we initialize them. */
+	/* Interrupt teardown just walks this list. */
+	struct {
+		struct resource *res;
+		void		*ih_cookie;
+		const char *description;
+	} interrupts[CPSW_INTR_COUNT];
+	int		interrupt_count;
+
+	uint32_t	cpsw_if_flags;
+	int		cpsw_media_status;
+
+	struct {
+		int resets;
+		int timer;
+		struct callout	callout;
+	} watchdog;
+
+	bus_dma_tag_t	mbuf_dtag;
+
+	/* An mbuf full of nulls for TX padding. */
+	bus_dmamap_t null_mbuf_dmamap;
+	struct mbuf *null_mbuf;
+	bus_addr_t null_mbuf_paddr;
+
+	/* RX and TX buffer tracking */
+	struct cpsw_queue rx, tx;
+
+	/* 64-bit versions of 32-bit hardware statistics counters */
+	uint64_t shadow_stats[CPSW_SYSCTL_COUNT];
+
+	/* CPPI STATERAM has 512 slots for building TX/RX queues. */
+	/* TODO: Size here supposedly varies with different versions
+	   of the controller.  Check DaVinci specs and find a good
+	   way to adjust this.  One option is to have a separate
+	   Device Tree parameter for number slots; another option
+	   is to calculate it from the memory size in the device tree. */
+	struct cpsw_slot _slots[CPSW_CPPI_RAM_SIZE / sizeof(struct cpsw_cpdma_bd)];
+	struct cpsw_slots avail;
+};
+
+#endif /*_IF_CPSWVAR_H */
diff --git a/freebsd/sys/dev/mii/smscphy.c b/freebsd/sys/dev/mii/smscphy.c
new file mode 100644
index 0000000..85893b2
--- /dev/null
+++ b/freebsd/sys/dev/mii/smscphy.c
@@ -0,0 +1,227 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2006 Benno Rice.  All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the SMSC LAN8710A
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include "miidevs.h"
+
+#include "miibus_if.h"
+
+static int	smscphy_probe(device_t);
+static int	smscphy_attach(device_t);
+
+static int	smscphy_service(struct mii_softc *, struct mii_data *, int);
+static void	smscphy_auto(struct mii_softc *, int);
+static void	smscphy_status(struct mii_softc *);
+
+static device_method_t smscphy_methods[] = {
+	/* device interface */
+	DEVMETHOD(device_probe,		smscphy_probe),
+	DEVMETHOD(device_attach,	smscphy_attach),
+	DEVMETHOD(device_detach,	mii_phy_detach),
+	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
+	DEVMETHOD_END
+};
+
+static devclass_t smscphy_devclass;
+
+static driver_t smscphy_driver = {
+	"smscphy",
+	smscphy_methods,
+	sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(smscphy, miibus, smscphy_driver, smscphy_devclass, 0, 0);
+
+static const struct mii_phydesc smscphys[] = {
+	MII_PHY_DESC(SMC, LAN8710A),
+	MII_PHY_END
+};
+
+static const struct mii_phy_funcs smscphy_funcs = {
+	smscphy_service,
+	smscphy_status,
+	mii_phy_reset
+};
+
+static int
+smscphy_probe(device_t dev)
+{
+
+	return (mii_phy_dev_probe(dev, smscphys, BUS_PROBE_DEFAULT));
+}
+
+static int
+smscphy_attach(device_t dev)
+{
+	struct mii_softc *sc;
+	const struct mii_phy_funcs *mpf;
+
+	sc = device_get_softc(dev);
+	mpf = &smscphy_funcs;
+	mii_phy_dev_attach(dev, MIIF_NOISOLATE | MIIF_NOMANPAUSE, mpf, 1);
+	mii_phy_setmedia(sc);
+
+	return (0);
+}
+
+static int
+smscphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+        struct	ifmedia_entry *ife;
+        int	reg;
+
+	ife = mii->mii_media.ifm_cur;
+
+        switch (cmd) {
+        case MII_POLLSTAT:
+                break;
+
+        case MII_MEDIACHG:
+		switch (IFM_SUBTYPE(ife->ifm_media)) {
+		case IFM_AUTO:
+			smscphy_auto(sc, ife->ifm_media);
+			break;
+
+		default:
+	mii_phy_setmedia(sc);
+			break;
+		}
+
+                break;
+
+        case MII_TICK:
+		if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
+			break;
+		}
+
+		/* I have no idea why BMCR_ISO gets set. */
+		reg = PHY_READ(sc, MII_BMCR);
+		if (reg & BMCR_ISO) {
+			PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
+		}
+
+		reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+		if (reg & BMSR_LINK) {
+			sc->mii_ticks = 0;
+			break;
+		}
+
+		if (++sc->mii_ticks <= MII_ANEGTICKS) {
+			break;
+		}
+
+		sc->mii_ticks = 0;
+		PHY_RESET(sc);
+		smscphy_auto(sc, ife->ifm_media);
+                break;
+        }
+
+        /* Update the media status. */
+        PHY_STATUS(sc);
+
+        /* Callback if something changed. */
+        mii_phy_update(sc, cmd);
+        return (0);
+}
+
+static void
+smscphy_auto(struct mii_softc *sc, int media)
+{
+	uint16_t	anar;
+
+	anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) | ANAR_CSMA;
+	if ((media & IFM_FLOW) != 0 || (sc->mii_flags & MIIF_FORCEPAUSE) != 0)
+		anar |= ANAR_FC;
+	PHY_WRITE(sc, MII_ANAR, anar);
+	/* Apparently this helps. */
+	anar = PHY_READ(sc, MII_ANAR);
+	PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+}
+
+static void
+smscphy_status(struct mii_softc *sc)
+{
+	struct mii_data *mii;
+	uint32_t bmcr, bmsr, status;
+
+	mii = sc->mii_pdata;
+	mii->mii_media_status = IFM_AVALID;
+	mii->mii_media_active = IFM_ETHER;
+
+	bmsr = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+	if ((bmsr & BMSR_LINK) != 0)
+		mii->mii_media_status |= IFM_ACTIVE;
+
+	bmcr = PHY_READ(sc, MII_BMCR);
+	if ((bmcr & BMCR_ISO) != 0) {
+		mii->mii_media_active |= IFM_NONE;
+		mii->mii_media_status = 0;
+		return;
+	}
+
+	if ((bmcr & BMCR_LOOP) != 0)
+		mii->mii_media_active |= IFM_LOOP;
+
+	if ((bmcr & BMCR_AUTOEN) != 0) {
+		if ((bmsr & BMSR_ACOMP) == 0) {
+			/* Erg, still trying, I guess... */
+			mii->mii_media_active |= IFM_NONE;
+			return;
+		}
+	}
+
+	status = PHY_READ(sc, 0x1F);
+	if (status & 0x0008)
+		mii->mii_media_active |= IFM_100_TX;
+	else
+		mii->mii_media_active |= IFM_10_T;
+	if (status & 0x0010)
+		mii->mii_media_active |= IFM_FDX | mii_phy_flowstatus(sc);
+	else
+		mii->mii_media_active |= IFM_HDX;
+}
diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h
index fcd8775..5b3e811 100644
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -52,6 +52,40 @@ static const rtems_bsd_device_resource smc0_res[] = {
 RTEMS_BSD_DEFINE_NEXUS_DEVICE(smc, 0, RTEMS_ARRAY_SIZE(smc0_res),
     &smc0_res[0]);
 
+#elif defined(LIBBSP_ARM_BEAGLE_BSP_H)
+
+static const rtems_bsd_device_resource cpsw0_res[] = {
+        {
+                .type = RTEMS_BSD_RES_MEMORY,
+                .start_request = 0,
+                .start_actual = 0x4a100000
+        }, {
+                .type = RTEMS_BSD_RES_IRQ,
+                .start_request = 0,
+                .start_actual = 0x28
+        },
+	   {
+                .type = RTEMS_BSD_RES_IRQ,
+                .start_request = 1,
+                .start_actual = 0x29
+        },
+           {
+                .type = RTEMS_BSD_RES_IRQ,
+                .start_request = 2,
+                .start_actual = 0x2a
+        },
+           {
+                .type = RTEMS_BSD_RES_IRQ,
+                .start_request = 3,
+                .start_actual = 0x2b
+        }
+};
+
+RTEMS_BSD_DEFINE_NEXUS_DEVICE(cpsw, 0, RTEMS_ARRAY_SIZE(cpsw0_res),
+    &cpsw0_res[0]);
+
+SYSINIT_DRIVER_REFERENCE(smscphy, miibus);
+
 #elif defined(__GENMCF548X_BSP_H)
 
 RTEMS_BSD_DEFINE_NEXUS_DEVICE(fec, 0, 0, NULL);
diff --git a/rtemsbsd/include/machine/rtems-bsd-cache.h b/rtemsbsd/include/machine/rtems-bsd-cache.h
index b8c4ce7..b8914ec 100644
--- a/rtemsbsd/include/machine/rtems-bsd-cache.h
+++ b/rtemsbsd/include/machine/rtems-bsd-cache.h
@@ -45,7 +45,7 @@
 #if defined(LIBBSP_ARM_LPC24XX_BSP_H)
   /* No cache */
 #elif defined(LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H) || \
-  defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H)
+  defined(LIBBSP_ARM_XILINX_ZYNQ_BSP_H) || defined(LIBBSP_ARM_BEAGLE_BSP_H)
   /* With cache, no coherency support in hardware */
   #define CPU_DATA_CACHE_ALIGNMENT 32
 #elif defined(LIBBSP_ARM_LPC32XX_BSP_H)
diff --git a/testsuite/include/rtems/bsd/test/network-config.h.in b/testsuite/include/rtems/bsd/test/network-config.h.in
index 30a9c5a..3169e76 100644
--- a/testsuite/include/rtems/bsd/test/network-config.h.in
+++ b/testsuite/include/rtems/bsd/test/network-config.h.in
@@ -42,6 +42,8 @@
   #define NET_CFG_INTERFACE_0 "cgem0"
 #elif defined(__GENMCF548X_BSP_H)
   #define NET_CFG_INTERFACE_0 "fec0"
+#elif defined(LIBBSP_ARM_BEAGLE_BSP_H)
+  #define NET_CFG_INTERFACE_0 "cpsw0"
 #else
   #define NET_CFG_INTERFACE_0 "lo0"
 #endif
-- 
1.9.1



More information about the devel mailing list