
/*
 * Copyright (c) 2013 Sughosh Ganu
 *
 * 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 this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce this list of conditions
 *    and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``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 ANY
 * 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.
 */

/*
 * TODO list
 * multicast address hash bit settings to be looked into
 */

#include <sys/cdefs.h>

__KERNEL_RCSID(0, "$NetBSD: omapl1x_emac.c,v 1.11 2019/05/30 02:32:17 msaitoh Exp $");

#include "opt_omapl1x.h"

#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/intr.h>
#include <sys/kmem.h>
#include <sys/mutex.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <sys/callout.h>

#include <net/if.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <net/bpf.h>

#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>

#include <arm/omap/omapl1x_reg.h>
#include <arm/omap/omap_tipb.h>

struct emac_cppi_bd;

#define EMAC_TXDESCS_SIZE (EMAC_CPPI_RAM_SIZE / 2)
#define EMAC_RXDESCS_SIZE \
    (EMAC_CPPI_RAM_SIZE - EMAC_TXDESCS_SIZE)

#define EMAC_NTXDESCS (EMAC_TXDESCS_SIZE/sizeof(struct emac_cppi_bd))
#define EMAC_NRXDESCS (EMAC_RXDESCS_SIZE/sizeof(struct emac_cppi_bd))

struct emac_cppi_bd {
	u_int		desc_next;
	u_int		buffer;
	u_int		len;
	u_int		mode;
} __packed;

struct emac_chain {
	bus_dmamap_t dmamap;
	struct emac_cppi_bd *bd;
	struct mbuf *m;
	SIMPLEQ_ENTRY(emac_chain) link;
};

struct emac_desc {
	struct emac_cppi_bd *tx_desc[EMAC_NTXDESCS];
	struct emac_cppi_bd *rx_desc[EMAC_NRXDESCS];
};

struct emac_channel {
	u_int			hdp;
	u_int			cp;
	void			*desc_base;
	bus_dmamap_t		desc_map;
	kmutex_t		*lock;
	uint8_t			ch;
	uint8_t			run;
	int			desc_rseg;
	bus_dma_segment_t	desc_seg;
	SIMPLEQ_HEAD(emac_free, emac_chain) free_head;
	SIMPLEQ_HEAD(emac_inuse, emac_chain) inuse_head;
};

struct emac_softc {
	device_t		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	bus_addr_t		sc_addr;
	size_t			sc_size;
	int			sc_intr;
	bus_dma_tag_t		sc_desct;
	bus_dma_tag_t		sc_buft;
	kmutex_t		*sc_hwlock;
	kmutex_t		*sc_mii_lock;
	callout_t		sc_mii_callout;
	struct ethercom		sc_ec;
#define	sc_if			sc_ec.ec_if
	struct mii_data		sc_mii;
	struct ifmedia		sc_media;
	void			*sc_soft_ih;
	uint32_t		sc_soft_flags;
#define SOFT_RESET		0x1

	struct emac_desc	descs;
	uint8_t			sc_enaddr[ETHER_ADDR_LEN];
	struct emac_chain	*tx_chain[EMAC_NTXDESCS];
	struct emac_chain	*rx_chain[EMAC_NRXDESCS];
	struct emac_channel	tx_chan;
	struct emac_channel	rx_chan;
};

#define EMAC_INTROFF_RXTH	0
#define EMAC_INTROFF_RX		1
#define EMAC_INTROFF_TX		2
#define EMAC_INTROFF_MISC	3

#define EMAC_FULL_DUPLEX	__BIT(0)
#define EMAC_GMII_EN		__BIT(5)
#define EMAC_TXPRIO_FIXED	__BIT(9)
#define EMAC_RMII_SPEED		__BIT(15)
#define EMAC_MDIO_CLKDIV	0x24

#define EMAC_RXMCASTEN		__BIT(5)
#define EMAC_RXBROADEN		__BIT(13)

#define RXBROADCH		0
#define RXMCASTCH		0
#define RXCHAN			0
#define TXCHAN			0

#define RXBROADCH_MASK		0x7
#define RXMCASTCH_MASK		0x7
#define RXMCASTCH_SHIFT		0
#define RXBROADCH_SHIFT		8

#define RXMAXLEN		1522
#define HOSTMASK		0x2

#define RXCH0EN			__BIT(0)
#define TXCH0EN			__BIT(0)
#define TX0EN			__BIT(0)
#define RX0EN			__BIT(0)
#define TXEN			__BIT(0)
#define RXEN			__BIT(0)
#define HOSTPENDEN		__BIT(2)

#define MAX_CHANS		8

#define EMAC_INIT_RX_DESC	128
#define EMAC_MIN_PKT_LEN	60
#define EMAC_TX_DESC_FREE	32
#define EMAC_RX_DESC_FREE	16
#define TXCH			1
#define RXCH			2

#define C0TXDONE		0x2
#define C0RXDONE		0x1
#define TX0PEND			__BIT(16)
#define RX0PEND			__BIT(0)
#define HOSTPEND		__BIT(26)

#define SOP			__BIT(31)
#define EOP			__BIT(30)
#define OWNER			__BIT(29)
#define EOQ			__BIT(28)
#define TDOWNCMPLT		__BIT(27)
#define PASSCRC			__BIT(26)

#define BUFLEN_MASK		0xFFFF
#define MDIO_IDLE		__BIT(31)
#define MDIO_ENABLE		__BIT(30)

#define USERACCESS_GO		__BIT(31)
#define USERACCESS_ACK		__BIT(29)
#define USERACCESS_WRITE	__BIT(30)
#define USERACCESS_REG(x)	(((x) & 0x1f) << 21)
#define USERACCESS_PHY(x)	(((x) & 0x1f) << 16)
#define USERACCESS_DATA(x)	((x) & 0xffff)

extern struct arm32_bus_dma_tag omapl1x_bus_dma_tag;
extern struct arm32_bus_dma_tag omapl1x_desc_dma_tag;

#define	EMAC_READ(sc, o)		\
	bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (o))
#define	EMAC_WRITE(sc, o, v)	\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (o), v)

static int emac_match(device_t parent, struct cfdata *match, void *aux);
static void emac_attach(device_t parent, device_t self, void *aux);

static int emac_intr(void *arg);
static void emac_int_disable(struct emac_softc * const sc);
static void emac_int_enable(struct emac_softc * const sc);

static int emac_ifinit(struct ifnet *ifp);
static void emac_ifstart(struct ifnet *ifp);
static void emac_ifstop(struct ifnet *ifp, int diable);
static int emac_ifioctl(struct ifnet *ifp, u_long cmd, void *data);
static void emac_ifwatchdog(struct ifnet *ifp);

static void emac_setmac(struct emac_softc * const sc, uint8_t *mac_addr);
static void emac_setmacsrcaddr(struct emac_softc *const sc, uint8_t *mac_addr);
static void emac_setmacaddr(struct emac_softc *const sc, uint8_t chan,
			    uint8_t *mac_addr);

static void emac_soft_intr(void *frame);

static int emac_rx_mbuf_map(struct emac_softc const *sc,
			    struct emac_chain *entry);

static int emac_tx_desc_dequeue(struct emac_softc *sc,
				struct emac_channel *chan);

static int emac_rx_desc_process(struct emac_softc *sc,
				struct emac_channel *chan);

static int emac_td_check(struct emac_softc * const sc,
			 bus_size_t cp, bus_size_t hdp);

static bus_addr_t emac_desc_phys(struct emac_channel *chan,
			    struct emac_cppi_bd *desc);

static void emac_add_desc_tail(struct emac_softc *sc, struct emac_channel *chan,
			      struct emac_chain *free_entry);

static int emac_desc_map(struct emac_softc * const sc,
			 struct emac_channel *chan, size_t memsize);

static void emac_desc_list_create(struct emac_cppi_bd **desc,
				  void *desc_base_ptr, int ndescs);

static u_int emac_free_descs(struct emac_softc * const sc,
			   struct emac_channel *chan, u_int num_desc);

static void emac_mii_statchg(struct ifnet *ifp);
static int emac_mii_wait(struct emac_softc * const sc);
static int emac_mii_readreg(device_t dev, int phy, int reg, uint16_t *val);
static int emac_mii_writereg(device_t dev, int phy, int reg, uint16_t val);

CFATTACH_DECL_NEW(emac, sizeof(struct emac_softc),
		  emac_match, emac_attach, NULL, NULL);

static int
emac_mii_wait (struct emac_softc * const sc)
{
	u_int tries;

	for (tries = 0; tries < 1000; tries++) {
		if ((EMAC_READ(sc, MACMDIOUSERACCESS0) & USERACCESS_GO) == 0)
			return 0;

		delay(1000);
	}

	return ETIMEDOUT;
}

static int
emac_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
{
	int ret = 0;
	uint32_t v, reg_mask = 0;
	struct emac_softc * const sc = device_private(dev);

	mutex_spin_enter(sc->sc_mii_lock);

	reg_mask = USERACCESS_GO | USERACCESS_REG(reg) | USERACCESS_PHY(phy);

	while (1) {
		ret = emac_mii_wait(sc);
		if (ret == ETIMEDOUT) {
			ret = 0;
			if ((EMAC_READ(sc, MACMDIOCONTROL) & MDIO_IDLE)) {
				EMAC_WRITE(sc, MACMDIOCONTROL,
					   MDIO_ENABLE | EMAC_MDIO_CLKDIV);
			}
			continue;
		}

		EMAC_WRITE(sc, MACMDIOUSERACCESS0, reg_mask);

		ret = emac_mii_wait(sc);
		if (ret == ETIMEDOUT) {
			ret = 0;
			if ((EMAC_READ(sc, MACMDIOCONTROL) & MDIO_IDLE)) {
				EMAC_WRITE(sc, MACMDIOCONTROL,
					   MDIO_ENABLE | EMAC_MDIO_CLKDIV);
			}
			continue;
		}
		break;
	}

	v = EMAC_READ(sc, MACMDIOUSERACCESS0);
	if (v & USERACCESS_ACK) {
		*val = USERACCESS_DATA(v);
		ret = 0;
	} else
		ret = -1;

	mutex_spin_exit(sc->sc_mii_lock);

	return ret;
}

static int
emac_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
{
	int ret = 0;
	uint32_t reg_mask = 0;
	struct emac_softc * const sc = device_private(dev);

	mutex_spin_enter(sc->sc_mii_lock);

	reg_mask = USERACCESS_GO | USERACCESS_WRITE | USERACCESS_REG(reg) |
		USERACCESS_PHY(phy) | USERACCESS_DATA(val);

	while (1) {
		ret = emac_mii_wait(sc);
		if (ret == ETIMEDOUT) {
			if ((EMAC_READ(sc, MACMDIOCONTROL) & MDIO_IDLE)) {
				EMAC_WRITE(sc, MACMDIOCONTROL,
					   MDIO_ENABLE | EMAC_MDIO_CLKDIV);
			}
			continue;
		}

		EMAC_WRITE(sc, MACMDIOUSERACCESS0, reg_mask);

		ret = emac_mii_wait(sc);
		if (ret == ETIMEDOUT) {
			if ((EMAC_READ(sc, MACMDIOCONTROL) & MDIO_IDLE)) {
				EMAC_WRITE(sc, MACMDIOCONTROL,
					   MDIO_ENABLE | EMAC_MDIO_CLKDIV);
			}
			continue;
		}
		break;
	}

	mutex_spin_exit(sc->sc_mii_lock);

	return 0;
}

static void
emac_mii_statchg (struct ifnet *ifp)
{
	struct emac_softc * const sc = ifp->if_softc;
	uint32_t maccontrol = 0;
	uint32_t maccontrol_read;

	maccontrol_read = EMAC_READ(sc, MACCONTROL);

	/* speed config */
	if (IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_100_TX) {
		maccontrol |= EMAC_RMII_SPEED;
	} else {
		maccontrol &= ~EMAC_RMII_SPEED;
	}

	/* duplex */
	if (sc->sc_mii.mii_media_active & IFM_FDX) {
		maccontrol |= EMAC_FULL_DUPLEX;
	} else {
		maccontrol &= ~EMAC_FULL_DUPLEX;
	}

	if (maccontrol_read == 0) {
		EMAC_WRITE(sc, MACCONTROL, maccontrol);
	} else if ((maccontrol_read & (EMAC_FULL_DUPLEX | EMAC_RMII_SPEED)) !=
		   (maccontrol & (EMAC_FULL_DUPLEX | EMAC_RMII_SPEED))) {
		/* Things have changed. Re-init */
		sc->sc_soft_flags |= SOFT_RESET;
	}

	return;
}

static void
emac_mii_tick (void *arg)
{
	struct emac_softc * const sc = arg;

	mii_tick(&sc->sc_mii);

	int s = splnet();

	if (sc->sc_soft_flags & SOFT_RESET) {
		mutex_enter(sc->sc_hwlock);
		emac_int_disable(sc);
		mutex_exit(sc->sc_hwlock);

		softint_schedule(sc->sc_soft_ih);
	}

	splx(s);

	callout_schedule(&sc->sc_mii_callout, hz);
}

static void
emac_setmacaddr (struct emac_softc *const sc, uint8_t chan, uint8_t *mac_addr)
{
	uint32_t val;

	EMAC_WRITE(sc, MACINDEX, chan);

	val = (mac_addr[3] << 24 | mac_addr[2] << 16 |
	       mac_addr[1] << 8 | mac_addr[0]);

	EMAC_WRITE(sc, MACADDRHI, val);

	val = 0;
	val = (mac_addr[5] << 8 | mac_addr[4] | ((chan & 0x7) << 16) |
	       __BIT(19) | __BIT(20));
	EMAC_WRITE(sc, MACADDRLO, val);
}

static void
emac_setmacsrcaddr (struct emac_softc *const sc, uint8_t *mac_addr)
{
	uint32_t val;

	val = (mac_addr[3] << 24 | mac_addr[2] << 16 |
	       mac_addr[1] << 8 | mac_addr[0]);

	EMAC_WRITE(sc, MACSRCADDRHI, val);

	val = 0;
	val = (mac_addr[5] << 8 | mac_addr[4]);
	EMAC_WRITE(sc, MACSRCADDRLO, val);
}

static void
emac_setmac (struct emac_softc *const sc, uint8_t *mac_addr)
{
	uint8_t i;

	for (i = 0; i < MAX_CHANS; i++)
		emac_setmacaddr(sc, i, mac_addr);
	emac_setmacsrcaddr(sc, mac_addr);
}

static bus_addr_t
emac_desc_phys (struct emac_channel *chan, struct emac_cppi_bd *desc)
{
	vaddr_t offset;

	offset = (vaddr_t)desc - (vaddr_t)chan->desc_base;

	return chan->desc_map->dm_segs[0].ds_addr + offset;
}

static void
emac_add_desc_tail (struct emac_softc *sc, struct emac_channel *chan,
	       struct emac_chain *free_entry)
{
	struct emac_cppi_bd *desc, *tail_desc;
	struct emac_chain *tail_entry;
	bus_dmamap_t map;
	u_int mode;
	bus_addr_t desc1_offset, desc2_offset, desc_base;
	bus_size_t buf_len;

	/* Prepare the desc for adding to the end of the chain */
	desc = free_entry->bd;
	map = free_entry->dmamap;
	buf_len = map->dm_segs[0].ds_len;
	desc_base = (bus_addr_t)chan->desc_base;
	memset(desc, 0, sizeof(*desc));

	desc1_offset = (bus_addr_t)desc - desc_base;

	bus_dmamap_sync(sc->sc_desct, chan->desc_map,
			desc1_offset, sizeof(*desc),
			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);

	desc->desc_next = 0;
	desc->buffer = (u_int)map->dm_segs[0].ds_addr;
	desc->len = (u_int)(buf_len & 0xffff);
	if (chan->ch == TXCH) {
		desc->mode = SOP | EOP | OWNER | (u_int)buf_len;
	} else {
		desc->mode = OWNER;
	}
	if (SIMPLEQ_EMPTY(&chan->inuse_head)) {
		/* idle list */
		SIMPLEQ_REMOVE_HEAD(&chan->free_head, link);
		SIMPLEQ_INSERT_HEAD(&chan->inuse_head, free_entry, link);
		EMAC_WRITE(sc, chan->hdp, (bus_size_t)emac_desc_phys(chan, desc));
		goto sync2;
	}

	tail_entry = SIMPLEQ_LAST(&chan->inuse_head, emac_chain, link);
	tail_desc = tail_entry->bd;

	desc2_offset = (bus_addr_t)tail_desc - desc_base;

	bus_dmamap_sync(sc->sc_desct, chan->desc_map,
			desc2_offset, sizeof(*desc),
			BUS_DMASYNC_PREREAD);

	tail_desc->desc_next = (u_int)emac_desc_phys(chan, desc);

	mode = tail_desc->mode;
	if ((mode & (EOQ | OWNER)) == EOQ) {
		tail_desc->mode &= ~EOQ;
		EMAC_WRITE(sc, chan->hdp,
			   (bus_size_t)emac_desc_phys(chan, desc));
	}

	SIMPLEQ_REMOVE_HEAD(&chan->free_head, link);
	SIMPLEQ_INSERT_TAIL(&chan->inuse_head, free_entry, link);

	desc2_offset = (bus_addr_t)tail_desc - desc_base;

	bus_dmamap_sync(sc->sc_desct, chan->desc_map,
			desc2_offset, sizeof(*desc),
			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
sync2:
	bus_dmamap_sync(sc->sc_desct, chan->desc_map,
			desc1_offset, sizeof(*desc),
			BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}

static int
emac_rx_mbuf_map (struct emac_softc const *sc, struct emac_chain *entry)
{
	int error;
	struct mbuf *m;
	bus_dmamap_t map;

	MGETHDR(m, M_WAIT, MT_DATA);
	if (__predict_false(m == NULL))
		return ENOMEM;

	MCLGET(m, M_WAIT);
	if (__predict_false((m->m_flags & M_EXT) == 0)) {
		m_freem(m);
		return ENOMEM;
	}

	entry->m = m;
	map = entry->dmamap;
	error = bus_dmamap_load(sc->sc_buft, map, m->m_ext.ext_buf,
				m->m_ext.ext_size, NULL,
				BUS_DMA_READ | BUS_DMA_WAITOK);
	if (error)
		return error;

	bus_dmamap_sync(sc->sc_buft, map, 0, map->dm_mapsize,
			BUS_DMASYNC_PREREAD);

	return 0;
}

static int
emac_tx_desc_dequeue (struct emac_softc *sc, struct emac_channel *chan)
{
	struct emac_cppi_bd *desc;
	struct emac_chain *entry;
	bus_dmamap_t map;
	bus_addr_t desc_offset;
	bus_addr_t desc_base;

	if ((entry = SIMPLEQ_FIRST(&chan->inuse_head)) == NULL)
		return ENOENT;

	desc = entry->bd;
	map = entry->dmamap;
	desc_base = (bus_addr_t)chan->desc_base;

	desc_offset = (bus_addr_t)desc - desc_base;
	bus_dmamap_sync(sc->sc_desct, chan->desc_map, desc_offset,
			sizeof(*desc),
			BUS_DMASYNC_POSTREAD);

	if (desc->mode & OWNER) {
		return EBUSY;
	}

	if (desc->mode & EOQ) {
		EMAC_WRITE(sc, chan->hdp, 0);
	}

	bus_dmamap_unload(sc->sc_buft, map);
	m_freem(entry->m);
	entry->m = NULL;

	EMAC_WRITE(sc, chan->cp, emac_desc_phys(chan, desc));

	SIMPLEQ_REMOVE_HEAD(&chan->inuse_head, link);
	SIMPLEQ_INSERT_TAIL(&chan->free_head, entry, link);

	return 0;
}

static int
emac_rx_desc_process (struct emac_softc *sc, struct emac_channel *chan)
{
	struct emac_cppi_bd *desc;
	struct ifnet * const ifp = &sc->sc_if;
	struct emac_chain *entry;
	bus_dmamap_t map;
	bus_addr_t desc_offset;
	struct mbuf *mb;
	bus_addr_t desc_base;
	u_int buf_len;

	if ((entry = SIMPLEQ_FIRST(&chan->inuse_head)) == NULL) {
		return ENOENT;
	}

	desc = entry->bd;
	map = entry->dmamap;
	mb = entry->m;
	desc_base = (bus_addr_t)chan->desc_base;

	desc_offset = (bus_addr_t)desc - desc_base;
	bus_dmamap_sync(sc->sc_desct, chan->desc_map, desc_offset,
			sizeof(*desc), BUS_DMASYNC_PREREAD);

	if (desc->mode & OWNER) {
		return EBUSY;
	}

	if ((desc->mode & (SOP | EOP)) != (SOP | EOP)) {
		/* This needs a look */
		device_printf(sc->sc_dev,
			     "Received packet spanning multiple buffers\n");
	}

	//off = __SHIFTOUT(desc->len, (uint32_t)__BITS(26, 16));
	buf_len = __SHIFTOUT(desc->mode, (uint32_t)__BITS(10,  0));

	if (desc->mode & PASSCRC)
		buf_len -= ETHER_CRC_LEN;

	bus_dmamap_unload(sc->sc_buft, map);
	m_set_rcvif(mb, ifp);
	mb->m_pkthdr.len = mb->m_len = buf_len;
	if_percpuq_enqueue(ifp->if_percpuq, mb);

	entry->m = NULL;

	if (desc->mode & EOQ)
		EMAC_WRITE(sc, chan->hdp, 0);

	EMAC_WRITE(sc, chan->cp, emac_desc_phys(chan, desc));

	SIMPLEQ_REMOVE_HEAD(&chan->inuse_head, link);
	SIMPLEQ_INSERT_TAIL(&chan->free_head, entry, link);

	return 0;
}

static void
emac_rx_chain_create (struct emac_softc *sc, struct emac_channel *chan)
{
	int i, error;
	struct emac_chain *entry;

	mutex_enter(chan->lock);

	for (i = 0; i < EMAC_INIT_RX_DESC; i++) {
		if ((entry = SIMPLEQ_FIRST(&chan->free_head)) != NULL) {
			error = emac_rx_mbuf_map(sc, entry);
			if (error) {
				device_printf(sc->sc_dev,
					      "%s: Could not map Rx mbufs"
					      " Queued up %d descs\n",
					      __func__, i);
				goto unlock;
			}
			emac_add_desc_tail(sc, chan, entry);
		} else {
			/*
			 * If we don't have the descriptors at init
			 * time, something is seriously wrong
			 * XXX Should this be a panic, or should the driver
			 * be cleanly exited.
			 */
			panic("%s: Only %d descriptors at init time!\n",
			      __func__, i);
		}
	}

unlock:
	mutex_exit(chan->lock);
}

static int
emac_desc_dequeue (struct emac_softc *sc, struct emac_channel *chan)
{
	int ret;
	struct emac_chain *entry;
	struct ifnet * const ifp = &sc->sc_if;

	if (chan->ch == TXCH) {
		ret = emac_tx_desc_dequeue(sc, chan);
		if (ret == 0) {
			ifp->if_flags &= ~IFF_OACTIVE;
		}
	} else {
		/* Process the received packet */
		ret = emac_rx_desc_process(sc, chan);
		if (ret == 0) {
			/* Now add a desc to the rx list */
			if ((entry = SIMPLEQ_FIRST(&chan->free_head)) != NULL) {
				ret = emac_rx_mbuf_map(sc, entry);
				if (ret == 0) {
					emac_add_desc_tail(sc, chan, entry);
				}
			}
		}
	}

	return ret;
}

static u_int
emac_free_descs (struct emac_softc * const sc, struct emac_channel *chan,
		 u_int num_desc)
{
	int i, ret;

	mutex_enter(chan->lock);

	for (i = 0; i < num_desc; i++) {
		ret = emac_desc_dequeue(sc, chan);
		if (ret > 0)
			break;
	}

	mutex_exit(chan->lock);

	return i;
}

static void
emac_int_disable (struct emac_softc * const sc)
{
	EMAC_WRITE(sc, MAC_CR_C_TX_EN(0), 0x0);
	EMAC_WRITE(sc, MAC_CR_C_RX_EN(0), 0x0);
	EMAC_WRITE(sc, MAC_CR_C_MISC_EN(0), 0x0);
}

static void
emac_int_enable (struct emac_softc * const sc)
{
	EMAC_WRITE(sc, MACTXINTMASKSET, TX0EN);
	EMAC_WRITE(sc, MACRXINTMASKSET, RX0EN);
	EMAC_WRITE(sc, MACINTMASKSET, HOSTMASK);
	EMAC_WRITE(sc, MAC_CR_C_TX_EN(0), TXCH0EN);
	EMAC_WRITE(sc, MAC_CR_C_RX_EN(0), RXCH0EN);
	EMAC_WRITE(sc, MAC_CR_C_MISC_EN(0), HOSTPENDEN);
}

static void
emac_soft_intr (void *arg)
{
	uint32_t mask;
	struct emac_softc * const sc = arg;
	struct ifnet * const ifp = &sc->sc_if;
	u_int soft_flags = atomic_swap_uint(&sc->sc_soft_flags, 0);

	if (soft_flags & SOFT_RESET) {
		int s = splnet();
		emac_ifinit(ifp);
		splx(s);
		return;
	}

	mutex_enter(sc->sc_hwlock);

	mask = EMAC_READ(sc, MACINVECTOR);

	/* We are working on channel 0 */
	if (mask & TX0PEND) {
		emac_free_descs(sc, &sc->tx_chan, EMAC_TX_DESC_FREE);
	}

	EMAC_WRITE(sc, MACEOIVECTOR, C0TXDONE);

	if (mask & RX0PEND) {
		emac_free_descs(sc, &sc->rx_chan, EMAC_RX_DESC_FREE);
	}

	EMAC_WRITE(sc, MACEOIVECTOR, C0RXDONE);

	if (__predict_false(mask & HOSTPEND)) {
		device_printf(sc->sc_dev,
			      "Host Error. Stopping the device\n");
		emac_ifstop(ifp, 0);

		return;
	}

	emac_int_enable(sc);

	mutex_exit(sc->sc_hwlock);

	return;
}

static int
emac_intr (void *arg)
{
	struct emac_softc * const sc = arg;

	mutex_enter(sc->sc_hwlock);
	emac_int_disable(sc);
	mutex_exit(sc->sc_hwlock);

	softint_schedule(sc->sc_soft_ih);

	return 1;
}

static int
emac_td_check (struct emac_softc * const sc, bus_size_t cp, bus_size_t hdp)
{
	uint32_t cp_val;

	cp_val = EMAC_READ(sc, cp);

	if (cp_val == 0xfffffffc) {
		EMAC_WRITE(sc, cp, 0xfffffffc);
		EMAC_WRITE(sc, hdp, 0);
		return 1;
	}

	return 0;
}

static void
emac_ifstart (struct ifnet *ifp)
{
	struct emac_softc * const sc = ifp->if_softc;
	struct mbuf *mb, *m;
	struct emac_chain *entry;
	struct emac_channel *chan;
	bus_dmamap_t map;
	int error;

	if (__predict_false((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) !=
			    IFF_RUNNING)) {
		return;
	}

	chan = &sc->tx_chan;
	mutex_enter(chan->lock);

	m = NULL;
	IFQ_POLL(&ifp->if_snd, mb);
	if (mb == NULL)
		goto unlock;

	while ((entry = SIMPLEQ_FIRST(&chan->free_head)) != NULL) {
		if (mb->m_pkthdr.len < ETHER_MIN_LEN) {
remap:
			MGETHDR(m, M_DONTWAIT, MT_DATA);
			if (m == NULL) {
				printf("%s: unable to allocate Tx mbuf\n",
				       device_xname(sc->sc_dev));
				goto unlock;
			}
			if (mb->m_pkthdr.len > MHLEN) {
				MCLGET(m, M_DONTWAIT);
				if ((m->m_flags & M_EXT) == 0) {
					printf("%s: unable to allocate Tx "
					    "cluster\n", device_xname(sc->sc_dev));
					m_freem(m);
					goto unlock;
				}
			}
			m_copydata(mb, 0, mb->m_pkthdr.len, mtod(m, void *));
			m->m_pkthdr.len = m->m_len = mb->m_pkthdr.len;
			if (m->m_pkthdr.len < ETHER_MIN_LEN) {
				if (M_TRAILINGSPACE(m) < ETHER_MIN_LEN -
				    m->m_pkthdr.len) {
					panic("emac_ifstart: M_TRAILINGSPACE\n");
				}
				memset(mtod(m, uint8_t *) + m->m_pkthdr.len, 0,
				    ETHER_MIN_LEN - ETHER_CRC_LEN - m->m_pkthdr.len);
				m->m_pkthdr.len = m->m_len = ETHER_MIN_LEN;
			}
		}

		IFQ_DEQUEUE(&ifp->if_snd, mb);
		if (m != NULL) {
			m_freem(mb);
			mb = m;
		}

		/* We have a msg to xmit */
		entry->m = mb;
		map = entry->dmamap;
		error = bus_dmamap_load_mbuf(sc->sc_buft, map, mb,
					     BUS_DMA_NOWAIT);
		if (error == EFBIG) {
			/*
			 * Ok, so our mbuf chain spans across
			 * discontiguous segments, unify them
			 */
			goto remap;
		} else if (error != 0) {
			device_printf(sc->sc_dev, "error\n");
			goto unlock;
		}

		bus_dmamap_sync(sc->sc_buft, map, 0, map->dm_mapsize,
				BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);

		if (__predict_false(map->dm_nsegs > 1))
			panic("%s: Cannot handle more than one segment\n",
			      __func__);

		emac_add_desc_tail(sc, chan, entry);
		bpf_mtap(ifp, mb, BPF_D_OUT);

		IFQ_POLL(&ifp->if_snd, mb);
		if (mb == NULL)
			goto unlock;
	}

	device_printf(sc->sc_dev, "TX desc's full, setting IFF_OACTIVE\n");
	ifp->if_flags |= IFF_OACTIVE;

unlock:
	mutex_exit(chan->lock);
}


static void
emac_ifstop (struct ifnet *ifp, int disable)
{
	struct emac_chain *entry, *next;
	struct emac_softc * const sc = ifp->if_softc;
	struct emac_channel *tx_chan, *rx_chan;
	u_int i;

	if ((ifp->if_flags & IFF_RUNNING) == 0)
		return;

	tx_chan = &sc->tx_chan;
	rx_chan = &sc->rx_chan;

	callout_stop(&sc->sc_mii_callout);
	mii_down(&sc->sc_mii);

	mutex_enter(sc->sc_hwlock);
	emac_int_disable(sc);
	mutex_exit(sc->sc_hwlock);

	EMAC_WRITE(sc, MACTXTEARDOWN, 0);
	EMAC_WRITE(sc, MACRXTEARDOWN, 0);

	i = 0;
	while ((tx_chan->run || rx_chan->run) && i < 10000) {
		delay(10);
		if ((tx_chan->run == true) &&
		    emac_td_check(sc, tx_chan->cp, tx_chan->hdp)) {
			tx_chan->run = false;
		}

		if ((rx_chan->run == true) &&
		    emac_td_check(sc, rx_chan->cp, rx_chan->hdp)) {
			rx_chan->run = false;
		}

		i++;
	}

	mutex_enter(tx_chan->lock);

	/* Release any queued transmit buffers. */
	SIMPLEQ_FOREACH_SAFE(entry, &tx_chan->inuse_head, link, next) {
		bus_dmamap_unload(sc->sc_buft, entry->dmamap);
		m_free(entry->m);
		memset(entry->bd, 0, sizeof(*entry->bd));
		entry->m = NULL;
		SIMPLEQ_REMOVE_HEAD(&tx_chan->inuse_head, link);
		SIMPLEQ_INSERT_TAIL(&tx_chan->free_head, entry, link);
	}

	mutex_exit(tx_chan->lock);


	mutex_enter(rx_chan->lock);

	/* Release any queued rx buffers */
	SIMPLEQ_FOREACH_SAFE(entry, &rx_chan->inuse_head, link, next) {
		bus_dmamap_unload(sc->sc_buft, entry->dmamap);
		m_free(entry->m);
		memset(entry->bd, 0, sizeof(*entry->bd));
		entry->m = NULL;
		SIMPLEQ_REMOVE_HEAD(&rx_chan->inuse_head, link);
		SIMPLEQ_INSERT_TAIL(&rx_chan->free_head, entry, link);
	}

	mutex_exit(rx_chan->lock);

	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
}

static int
emac_ifinit (struct ifnet *ifp)
{
	struct emac_softc * const sc = ifp->if_softc;
	struct emac_channel *tx_chan, *rx_chan;
	struct mii_data * const mii = &sc->sc_mii;
	int i;
	u_int32_t maccontrol, mbp_enable;


	tx_chan = &sc->tx_chan;
	rx_chan = &sc->rx_chan;

	emac_ifstop(ifp, 0);

	EMAC_WRITE(sc, MACHASH1, 0);
	EMAC_WRITE(sc, MACHASH2, 0);

	EMAC_WRITE(sc, MACSOFTRESET, 1);
	while (EMAC_READ(sc, MACSOFTRESET) == 1)
		;

	/* Till we figure out mcast, do this */
	EMAC_WRITE(sc, MACHASH1, 0xffffffff);
	EMAC_WRITE(sc, MACHASH2, 0xffffffff);

	for (i = 0; i < 8; i++) {
		EMAC_WRITE(sc, MAC_TX_HDP(i), 0);
		EMAC_WRITE(sc, MAC_RX_HDP(i), 0);
		EMAC_WRITE(sc, MAC_TX_CP(i), 0);
		EMAC_WRITE(sc, MAC_RX_CP(i), 0);
	}

	EMAC_WRITE(sc, MACCONTROL, 0);

	mii_mediachg(mii);

	emac_setmac(sc, sc->sc_enaddr);

	EMAC_WRITE(sc, MACRXBUFOFFSET, 0);

	EMAC_WRITE(sc, MACRXUNICASTCLEAR, 0xff);

	EMAC_WRITE(sc, MACRXUNICASTCLEAR, 0xff);
	EMAC_WRITE(sc, MACRXUNICASTSET, RXCH0EN);

	mbp_enable = EMAC_RXBROADEN | EMAC_RXMCASTEN |
		((RXBROADCH & RXBROADCH_MASK) << RXBROADCH_SHIFT) |
		((RXMCASTCH & RXMCASTCH_MASK) << RXMCASTCH_SHIFT);
	EMAC_WRITE(sc, MACRXMBPEN, mbp_enable);

	EMAC_WRITE(sc, MACRXMAXLEN, RXMAXLEN);

	emac_rx_chain_create(sc, &sc->rx_chan);

	EMAC_WRITE(sc, MACMDIOCONTROL, __BIT(30) | __BIT(18) |
		   EMAC_MDIO_CLKDIV);

	EMAC_WRITE(sc, MACTXCONTROL, TXEN);
	EMAC_WRITE(sc, MACRXCONTROL, RXEN);

	/* Turn ON mii */
	maccontrol = EMAC_READ(sc, MACCONTROL);
	maccontrol |= EMAC_GMII_EN;
	EMAC_WRITE(sc, MACCONTROL, maccontrol);

	tx_chan->run = true;
	rx_chan->run = true;
	callout_schedule(&sc->sc_mii_callout, hz);
	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	mutex_enter(sc->sc_hwlock);
	emac_int_enable(sc);
	mutex_exit(sc->sc_hwlock);

	return 0;
}

static int
emac_ifioctl (struct ifnet *ifp, u_long cmd, void *data)
{
	const int s = splnet();
	int error = 0;

	switch (cmd) {
	default:
		error = ether_ioctl(ifp, cmd, data);
		if (error == ENETRESET) {
			error = 0;
		}
		break;
	}

	splx(s);

	return error;
}

static void
emac_ifwatchdog (struct ifnet *ifp)
{
}

static int
emac_desc_map (struct emac_softc * const sc, struct emac_channel *chan,
	       size_t memsize)
{
	void **kvap;
	bus_dmamap_t *map;
	bus_dma_tag_t dmat;

	map = &chan->desc_map;
	kvap = &chan->desc_base;
	dmat = sc->sc_desct;

	if (bus_dmamem_alloc(dmat, memsize, PAGE_SIZE, 0, &chan->desc_seg, 1,
			     &chan->desc_rseg, 0)) {
		device_printf(sc->sc_dev, "can't alloc descriptors\n");
		return 1;
	}

	if (bus_dmamem_map(dmat, &chan->desc_seg, chan->desc_rseg, memsize,
			   kvap, 0)) {
		device_printf(sc->sc_dev, "can't map descriptors (%zu bytes)\n",
				 memsize);
		goto fail1;
	}

	if (bus_dmamap_create(dmat, memsize, 1, memsize, 0, 0, map)) {
		device_printf(sc->sc_dev, "can't create descriptor dma map\n");
		goto fail2;
	}

	if (bus_dmamap_load(dmat, *map, *kvap, memsize, NULL, 0)) {
		device_printf(sc->sc_dev, "can't load dma map\n");
		goto fail3;
	}

	return 0;

fail3:
	bus_dmamap_destroy(sc->sc_desct, *map);

fail2:
	bus_dmamem_unmap(dmat, *kvap, memsize);
	*kvap = NULL;

fail1:
	bus_dmamem_free(dmat, &chan->desc_seg, chan->desc_rseg);
	return 1;
}

static void
emac_desc_list_create (struct emac_cppi_bd **desc, void *desc_base_ptr, int ndescs)
{
	int i;
	struct emac_cppi_bd *ptr = desc_base_ptr;

	for (i = 0; i < ndescs; i++)
		desc[i] = ptr + i;
}

static int
emac_match (device_t parent, struct cfdata *match, void *aux)
{
	return 1;
}

static void
emac_attach (device_t parent, device_t self, void *aux)
{
	struct emac_softc * const sc = device_private(self);
	struct ifnet * const ifp = &sc->sc_if;
	struct mii_data *mii = &sc->sc_mii;
	struct tipb_attach_args *tipb = aux;
	const char * const xname = device_xname(self);
	prop_dictionary_t dict = device_properties(self);
	struct emac_channel *tx_chan, *rx_chan;
	bus_dmamap_t dmamap;
	struct emac_chain *entry;
	int i;

	sc->sc_iot = tipb->tipb_iot;
	sc->sc_intr = tipb->tipb_intr;
	sc->sc_addr = tipb->tipb_addr;
	sc->sc_size = OMAPL1X_EMAC_SIZE;

	/* descriptors to be allocated from cppi ram range */
	sc->sc_desct = &omapl1x_desc_dma_tag;
	sc->sc_buft = &omapl1x_bus_dma_tag;

	if (bus_space_map(sc->sc_iot, sc->sc_addr, sc->sc_size, 0, &sc->sc_ioh))
		panic("%s: Cannot map registers", device_xname(self));

	sc->tx_chan.hdp = MAC_TX_HDP(0);
	sc->tx_chan.cp = MAC_TX_CP(0);

	sc->rx_chan.hdp = MAC_RX_HDP(0);
	sc->rx_chan.cp = MAC_RX_CP(0);

	tx_chan = &sc->tx_chan;
	rx_chan = &sc->rx_chan;
	tx_chan->ch = TXCH;
	rx_chan->ch = RXCH;

	tx_chan->lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
	rx_chan->lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
	sc->sc_hwlock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM);
	sc->sc_mii_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM);

	mutex_enter(sc->sc_hwlock);
	emac_int_disable(sc);
	mutex_exit(sc->sc_hwlock);

	callout_init(&sc->sc_mii_callout, 0);
	callout_setfunc(&sc->sc_mii_callout, emac_mii_tick, sc);

	prop_data_t eaprop = prop_dictionary_get(dict, "mac-address");
        if (eaprop == NULL) {
		device_printf(sc->sc_dev,
		"using fake station address\n");
		/* 'N' happens to have the Local bit set */
		sc->sc_enaddr[0] = 'N';
		sc->sc_enaddr[1] = 'e';
		sc->sc_enaddr[2] = 't';
		sc->sc_enaddr[3] = 'B';
		sc->sc_enaddr[4] = 'S';
		sc->sc_enaddr[5] = 'D';
	} else {
		KASSERT(prop_object_type(eaprop) == PROP_TYPE_DATA);
		KASSERT(prop_data_size(eaprop) == ETHER_ADDR_LEN);
		memcpy(sc->sc_enaddr, prop_data_data_nocopy(eaprop),
		    ETHER_ADDR_LEN);
	}

	sc->sc_dev = self;

	/* First map the tx and rx descriptors */
	if (emac_desc_map(sc, tx_chan, EMAC_TXDESCS_SIZE)) {
		aprint_error_dev(self, "Can't map tx desc's\n");
		return;
	}

	emac_desc_list_create(sc->descs.tx_desc, tx_chan->desc_base,
			      EMAC_NTXDESCS);

	if (emac_desc_map(sc, rx_chan, EMAC_RXDESCS_SIZE)) {
		aprint_error_dev(self, "Can't map rx desc's\n");
		return;
	}
	emac_desc_list_create(sc->descs.rx_desc, rx_chan->desc_base,
			      EMAC_NRXDESCS);

	/* Get the dma map's for the tx buffers */
	SIMPLEQ_INIT(&tx_chan->free_head);
	SIMPLEQ_INIT(&tx_chan->inuse_head);
	for (i = 0; i < EMAC_NTXDESCS; i++) {
		/*
		 * Ok, we keep this simple, one dma segment per tx dma map.
		 * This makes the mapping of the desc's and the dma map's
		 * pretty straightforward.
		 * We club together the mbuf chain in the if_start routine
		 * if it's fragmented.
		 */
		if (bus_dmamap_create(sc->sc_buft, MCLBYTES, 1, MCLBYTES, 0,
		    BUS_DMA_WAITOK, &dmamap)) {
			aprint_error_dev(self, "Can't create TX dmamap\n");
			goto fail;
		}

		entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
		entry->dmamap = dmamap;
		entry->bd = sc->descs.tx_desc[i];
		entry->m = NULL;
		sc->tx_chain[i] = entry;
		SIMPLEQ_INSERT_HEAD(&tx_chan->free_head, entry, link);
	}

	/* Now on to the RX buffers */
	SIMPLEQ_INIT(&rx_chan->free_head);
	SIMPLEQ_INIT(&rx_chan->inuse_head);
	for (i = 0; i < EMAC_NRXDESCS; i++) {
		if (bus_dmamap_create(sc->sc_buft, MCLBYTES, 1, MCLBYTES, 0,
		    BUS_DMA_WAITOK, &dmamap)) {
			aprint_error_dev(self, "Can't create RX dmamap\n");
			goto fail;
		}

		entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
		entry->dmamap = dmamap;
		entry->bd = sc->descs.rx_desc[i];
		entry->m = NULL;
		sc->rx_chain[i] = entry;
		SIMPLEQ_INSERT_HEAD(&rx_chan->free_head, entry, link);
	}

	/* mii related stuff */
	mii->mii_ifp = ifp;
	mii->mii_readreg = emac_mii_readreg;
	mii->mii_writereg = emac_mii_writereg;
	mii->mii_statchg = emac_mii_statchg;
	sc->sc_ec.ec_mii = mii;

	EMAC_WRITE(sc, MACMDIOCONTROL, __BIT(30) | __BIT(18) |
		   EMAC_MDIO_CLKDIV);

	ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
	mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, 0, 0);
	if (LIST_FIRST(&mii->mii_phys) == NULL) {
		aprint_error_dev(self, "no PHY found!\n");
		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_MANUAL);
	} else
		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);

	strlcpy(ifp->if_xname, xname, IFNAMSIZ);
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	ifp->if_softc = sc;
	ifp->if_start = emac_ifstart;
	ifp->if_ioctl = emac_ifioctl;
	ifp->if_init = emac_ifinit;
	ifp->if_stop = emac_ifstop;
	ifp->if_watchdog = emac_ifwatchdog;
	ifp->if_mtu = ETHERMTU;
	IFQ_SET_READY(&ifp->if_snd);

	emac_ifstop(ifp, 0);

	sc->sc_soft_ih = softint_establish(SOFTINT_NET, emac_soft_intr, sc);

	/* Register all the emac interrupts */
	intr_establish(sc->sc_intr + EMAC_INTROFF_RXTH, IPL_VM, IST_LEVEL_HIGH,
		       emac_intr, sc);
	intr_establish(sc->sc_intr + EMAC_INTROFF_RX, IPL_VM, IST_LEVEL_HIGH,
		       emac_intr, sc);
	intr_establish(sc->sc_intr + EMAC_INTROFF_TX, IPL_VM, IST_LEVEL_HIGH,
		       emac_intr, sc);
	intr_establish(sc->sc_intr + EMAC_INTROFF_MISC, IPL_VM, IST_LEVEL_HIGH,
		       emac_intr, sc);

	if_attach(ifp);
	ether_ifattach(ifp, sc->sc_enaddr);

	return;

fail:
	while ((entry = SIMPLEQ_FIRST(&sc->tx_chan.free_head)) != NULL) {
		SIMPLEQ_REMOVE_HEAD(&sc->tx_chan.free_head, link);
		bus_dmamap_destroy(sc->sc_desct, entry->dmamap);
	}

	while ((entry = SIMPLEQ_FIRST(&sc->rx_chan.free_head)) != NULL) {
		SIMPLEQ_REMOVE_HEAD(&sc->tx_chan.free_head, link);
		bus_dmamap_destroy(sc->sc_desct, entry->dmamap);
	}

	bus_dmamap_destroy(sc->sc_desct, rx_chan->desc_map);
	bus_dmamap_destroy(sc->sc_desct, tx_chan->desc_map);

	bus_dmamem_unmap(sc->sc_desct, rx_chan->desc_base, EMAC_RXDESCS_SIZE);
	bus_dmamem_unmap(sc->sc_desct, tx_chan->desc_base, EMAC_TXDESCS_SIZE);
	sc->tx_chan.desc_base = NULL;
	sc->rx_chan.desc_base = NULL;

	bus_dmamem_free(sc->sc_desct, &tx_chan->desc_seg, tx_chan->desc_rseg);
	bus_dmamem_free(sc->sc_desct, &rx_chan->desc_seg, rx_chan->desc_rseg);

	return;
}
