/*
 * Generic Broadcom Home Networking Division (HND) DMA module.
 * This supports the following chips: BCM42xx, 44xx, 47xx .
 *
 * Copyright 2007, Broadcom Corporation
 * All Rights Reserved.
 * 
 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
 *
 * $Id$
 */

#include <typedefs.h>
#include <bcmdefs.h>
#include <osl.h>
#include "linux_osl.h"
#include <bcmendian.h>
#include <sbconfig.h>
#include "bcmutils.h"
#include <bcmdevs.h>
#include <sbutils.h>

#include "sbhnddma.h"
#include "hnddma.h"

/* debug/trace */
#ifdef BCMDBG
#define	DMA_ERROR(args) if (!(*di->msg_level & 1)); else printf args
#define	DMA_TRACE(args) if (!(*di->msg_level & 2)); else printf args
#else
#define	DMA_ERROR(args)
#define	DMA_TRACE(args)
#endif

/* default dma message level (if input msg_level pointer is null in dma_attach()) */
static uint dma_msg_level = 0;

#define	MAXNAMEL	8	/* 8 char names */

#define	DI_INFO(dmah)	(dma_info_t *)dmah
typedef struct osl_dmainfo osldma_t;

/* dma engine software state */
typedef struct dma_info
{
  struct hnddma_pub hnddma;	/* exported structure, don't use hnddma_t,
				 * which could be const
				 */
  uint *msg_level;		/* message level pointer */
  char name[MAXNAMEL];		/* callers name for diag msgs */

  void *osh;			/* os handle */
  sb_t *sbh;			/* sb handle */

  bool dma64;			/* dma64 enabled */
  bool addrext;			/* this dma engine supports DmaExtendedAddrChanges */

  dma32regs_t *d32txregs;	/* 32 bits dma tx engine registers */
  dma32regs_t *d32rxregs;	/* 32 bits dma rx engine registers */
  dma64regs_t *d64txregs;	/* 64 bits dma tx engine registers */
  dma64regs_t *d64rxregs;	/* 64 bits dma rx engine registers */

  uint32 dma64align;		/* either 8k or 4k depends on number of dd */
  dma32dd_t *txd32;		/* pointer to dma32 tx descriptor ring */
  dma64dd_t *txd64;		/* pointer to dma64 tx descriptor ring */
  uint ntxd;			/* # tx descriptors tunable */
  uint txin;			/* index of next descriptor to reclaim */
  uint txout;			/* index of next descriptor to post */
  void **txp;			/* pointer to parallel array of pointers to packets */
  osldma_t *tx_dmah;		/* DMA TX descriptor ring handle */
  osldma_t **txp_dmah;		/* DMA TX packet data handle */
  ulong txdpa;			/* physical address of descriptor ring */
  uint txdalign;		/* #bytes added to alloc'd mem to align txd */
  uint txdalloc;		/* #bytes allocated for the ring */

  dma32dd_t *rxd32;		/* pointer to dma32 rx descriptor ring */
  dma64dd_t *rxd64;		/* pointer to dma64 rx descriptor ring */
  uint nrxd;			/* # rx descriptors tunable */
  uint rxin;			/* index of next descriptor to reclaim */
  uint rxout;			/* index of next descriptor to post */
  void **rxp;			/* pointer to parallel array of pointers to packets */
  osldma_t *rx_dmah;		/* DMA RX descriptor ring handle */
  osldma_t **rxp_dmah;		/* DMA RX packet data handle */
  ulong rxdpa;			/* physical address of descriptor ring */
  uint rxdalign;		/* #bytes added to alloc'd mem to align rxd */
  uint rxdalloc;		/* #bytes allocated for the ring */

  /* tunables */
  uint rxbufsize;		/* rx buffer size in bytes,
				   not including the extra headroom
				 */
  uint nrxpost;			/* # rx buffers to keep posted */
  uint rxoffset;		/* rxcontrol offset */
  uint ddoffsetlow;		/* add to get dma address of descriptor ring, low 32 bits */
  uint ddoffsethigh;		/*   high 32 bits */
  uint dataoffsetlow;		/* add to get dma address of data buffer, low 32 bits */
  uint dataoffsethigh;		/*   high 32 bits */
} dma_info_t;

#ifdef BCMDMA64
#define	DMA64_ENAB(di)	((di)->dma64)
#define DMA64_CAP	TRUE
#else
#define	DMA64_ENAB(di)	(0)
#define DMA64_CAP	FALSE
#endif

/* descriptor bumping macros */
#define	XXD(x, n)	((x) & ((n) - 1))	/* faster than %, but n must be power of 2 */
#define	TXD(x)		XXD((x), di->ntxd)
#define	RXD(x)		XXD((x), di->nrxd)
#define	NEXTTXD(i)	TXD(i + 1)
#define	PREVTXD(i)	TXD(i - 1)
#define	NEXTRXD(i)	RXD(i + 1)
#define	NTXDACTIVE(h, t)	TXD(t - h)
#define	NRXDACTIVE(h, t)	RXD(t - h)

/* macros to convert between byte offsets and indexes */
#define	B2I(bytes, type)	((bytes) / sizeof(type))
#define	I2B(index, type)	((index) * sizeof(type))

#define	PCI32ADDR_HIGH		0xc0000000	/* address[31:30] */
#define	PCI32ADDR_HIGH_SHIFT	30	/* address[31:30] */


/* common prototypes */
static bool _dma_isaddrext (dma_info_t * di);
static bool _dma_alloc (dma_info_t * di, uint direction);
static void _dma_detach (dma_info_t * di);
static void _dma_ddtable_init (dma_info_t * di, uint direction, ulong pa);
static void _dma_rxinit (dma_info_t * di);
static void *_dma_rx (dma_info_t * di);
static void _dma_rxfill (dma_info_t * di);
static void _dma_rxreclaim (dma_info_t * di);
static void _dma_rxenable (dma_info_t * di);
static void *_dma_getnextrxp (dma_info_t * di, bool forceall);

static void _dma_txblock (dma_info_t * di);
static void _dma_txunblock (dma_info_t * di);
static uint _dma_txactive (dma_info_t * di);

static void *_dma_peeknexttxp (dma_info_t * di);
static uintptr _dma_getvar (dma_info_t * di, const char *name);
static void _dma_counterreset (dma_info_t * di);
static void _dma_fifoloopbackenable (dma_info_t * di);

/* ** 32 bit DMA prototypes */
static bool dma32_alloc (dma_info_t * di, uint direction);
static bool dma32_txreset (dma_info_t * di);
static bool dma32_rxreset (dma_info_t * di);
static bool dma32_txsuspendedidle (dma_info_t * di);
static int dma32_txfast (dma_info_t * di, void *p0, bool commit);
static void *dma32_getnexttxp (dma_info_t * di, bool forceall);
static void *dma32_getnextrxp (dma_info_t * di, bool forceall);
static void dma32_txrotate (dma_info_t * di);
static bool dma32_rxidle (dma_info_t * di);
static void dma32_txinit (dma_info_t * di);
static bool dma32_txenabled (dma_info_t * di);
static void dma32_txsuspend (dma_info_t * di);
static void dma32_txresume (dma_info_t * di);
static bool dma32_txsuspended (dma_info_t * di);
static void dma32_txreclaim (dma_info_t * di, bool forceall);
static bool dma32_txstopped (dma_info_t * di);
static bool dma32_rxstopped (dma_info_t * di);
static bool dma32_rxenabled (dma_info_t * di);
static bool _dma32_addrext (osl_t * osh, dma32regs_t * dma32regs);

/* ** 64 bit DMA prototypes and stubs */
#ifdef BCMDMA64
static bool dma64_alloc (dma_info_t * di, uint direction);
static bool dma64_txreset (dma_info_t * di);
static bool dma64_rxreset (dma_info_t * di);
static bool dma64_txsuspendedidle (dma_info_t * di);
static int dma64_txfast (dma_info_t * di, void *p0, bool commit);
static void *dma64_getnexttxp (dma_info_t * di, bool forceall);
static void *dma64_getnextrxp (dma_info_t * di, bool forceall);
static void dma64_txrotate (dma_info_t * di);

static bool dma64_rxidle (dma_info_t * di);
static void dma64_txinit (dma_info_t * di);
static bool dma64_txenabled (dma_info_t * di);
static void dma64_txsuspend (dma_info_t * di);
static void dma64_txresume (dma_info_t * di);
static bool dma64_txsuspended (dma_info_t * di);
static void dma64_txreclaim (dma_info_t * di, bool forceall);
static bool dma64_txstopped (dma_info_t * di);
static bool dma64_rxstopped (dma_info_t * di);
static bool dma64_rxenabled (dma_info_t * di);
static bool _dma64_addrext (osl_t * osh, dma64regs_t * dma64regs);

#else
static bool
dma64_alloc (dma_info_t * di, uint direction)
{
  return FALSE;
}
static bool
dma64_txreset (dma_info_t * di)
{
  return FALSE;
}
static bool
dma64_rxreset (dma_info_t * di)
{
  return FALSE;
}
static bool
dma64_txsuspendedidle (dma_info_t * di)
{
  return FALSE;
}
static int
dma64_txfast (dma_info_t * di, void *p0, bool commit)
{
  return 0;
}
static void *
dma64_getnexttxp (dma_info_t * di, bool forceall)
{
  return NULL;
}
static void *
dma64_getnextrxp (dma_info_t * di, bool forceall)
{
  return NULL;
}
static void
dma64_txrotate (dma_info_t * di)
{
  return;
}

static bool
dma64_rxidle (dma_info_t * di)
{
  return FALSE;
}
static void
dma64_txinit (dma_info_t * di)
{
  return;
}
static bool
dma64_txenabled (dma_info_t * di)
{
  return FALSE;
}
static void
dma64_txsuspend (dma_info_t * di)
{
  return;
}
static void
dma64_txresume (dma_info_t * di)
{
  return;
}
static bool
dma64_txsuspended (dma_info_t * di)
{
  return FALSE;
}
static void
dma64_txreclaim (dma_info_t * di, bool forceall)
{
  return;
}
static bool
dma64_txstopped (dma_info_t * di)
{
  return FALSE;
}
static bool
dma64_rxstopped (dma_info_t * di)
{
  return FALSE;
}
static bool
dma64_rxenabled (dma_info_t * di)
{
  return FALSE;
}
static bool
_dma64_addrext (osl_t * osh, dma64regs_t * dma64regs)
{
  return FALSE;
}

#endif /* BCMDMA64 */

#ifdef BCMDBG
static void dma32_dumpring (dma_info_t * di, struct bcmstrbuf *b,
			    dma32dd_t * ring, uint start, uint end,
			    uint max_num);
static void dma32_dump (dma_info_t * di, struct bcmstrbuf *b, bool dumpring);
static void dma32_dumptx (dma_info_t * di, struct bcmstrbuf *b,
			  bool dumpring);
static void dma32_dumprx (dma_info_t * di, struct bcmstrbuf *b,
			  bool dumpring);

static void dma64_dumpring (dma_info_t * di, struct bcmstrbuf *b,
			    dma64dd_t * ring, uint start, uint end,
			    uint max_num);
static void dma64_dump (dma_info_t * di, struct bcmstrbuf *b, bool dumpring);
static void dma64_dumptx (dma_info_t * di, struct bcmstrbuf *b,
			  bool dumpring);
static void dma64_dumprx (dma_info_t * di, struct bcmstrbuf *b,
			  bool dumpring);
#endif


static di_fcn_t dma64proc = {
  (di_detach_t) _dma_detach,
  (di_txinit_t) dma64_txinit,
  (di_txreset_t) dma64_txreset,
  (di_txenabled_t) dma64_txenabled,
  (di_txsuspend_t) dma64_txsuspend,
  (di_txresume_t) dma64_txresume,
  (di_txsuspended_t) dma64_txsuspended,
  (di_txsuspendedidle_t) dma64_txsuspendedidle,
  (di_txfast_t) dma64_txfast,
  (di_txstopped_t) dma64_txstopped,
  (di_txreclaim_t) dma64_txreclaim,
  (di_getnexttxp_t) dma64_getnexttxp,
  (di_peeknexttxp_t) _dma_peeknexttxp,
  (di_txblock_t) _dma_txblock,
  (di_txunblock_t) _dma_txunblock,
  (di_txactive_t) _dma_txactive,
  (di_txrotate_t) dma64_txrotate,

  (di_rxinit_t) _dma_rxinit,
  (di_rxreset_t) dma64_rxreset,
  (di_rxidle_t) dma64_rxidle,
  (di_rxstopped_t) dma64_rxstopped,
  (di_rxenable_t) _dma_rxenable,
  (di_rxenabled_t) dma64_rxenabled,
  (di_rx_t) _dma_rx,
  (di_rxfill_t) _dma_rxfill,
  (di_rxreclaim_t) _dma_rxreclaim,
  (di_getnextrxp_t) _dma_getnextrxp,

  (di_fifoloopbackenable_t) _dma_fifoloopbackenable,
  (di_getvar_t) _dma_getvar,
  (di_counterreset_t) _dma_counterreset,

#ifdef BCMDBG
  (di_dump_t) dma64_dump,
  (di_dumptx_t) dma64_dumptx,
  (di_dumprx_t) dma64_dumprx,
#else
  NULL,
  NULL,
  NULL,
#endif
  34
};

static di_fcn_t dma32proc = {
  (di_detach_t) _dma_detach,
  (di_txinit_t) dma32_txinit,
  (di_txreset_t) dma32_txreset,
  (di_txenabled_t) dma32_txenabled,
  (di_txsuspend_t) dma32_txsuspend,
  (di_txresume_t) dma32_txresume,
  (di_txsuspended_t) dma32_txsuspended,
  (di_txsuspendedidle_t) dma32_txsuspendedidle,
  (di_txfast_t) dma32_txfast,
  (di_txstopped_t) dma32_txstopped,
  (di_txreclaim_t) dma32_txreclaim,
  (di_getnexttxp_t) dma32_getnexttxp,
  (di_peeknexttxp_t) _dma_peeknexttxp,
  (di_txblock_t) _dma_txblock,
  (di_txunblock_t) _dma_txunblock,
  (di_txactive_t) _dma_txactive,
  (di_txrotate_t) dma32_txrotate,

  (di_rxinit_t) _dma_rxinit,
  (di_rxreset_t) dma32_rxreset,
  (di_rxidle_t) dma32_rxidle,
  (di_rxstopped_t) dma32_rxstopped,
  (di_rxenable_t) _dma_rxenable,
  (di_rxenabled_t) dma32_rxenabled,
  (di_rx_t) _dma_rx,
  (di_rxfill_t) _dma_rxfill,
  (di_rxreclaim_t) _dma_rxreclaim,
  (di_getnextrxp_t) _dma_getnextrxp,

  (di_fifoloopbackenable_t) _dma_fifoloopbackenable,
  (di_getvar_t) _dma_getvar,
  (di_counterreset_t) _dma_counterreset,

#ifdef BCMDBG
  (di_dump_t) dma32_dump,
  (di_dumptx_t) dma32_dumptx,
  (di_dumprx_t) dma32_dumprx,
#else
  NULL,
  NULL,
  NULL,
#endif
  34
};

hnddma_t *
dma_attach (osl_t * osh, char *name, sb_t * sbh, void *dmaregstx,
	    void *dmaregsrx, uint ntxd, uint nrxd, uint rxbufsize,
	    uint nrxpost, uint rxoffset, uint * msg_level)
{
  dma_info_t *di;
  uint size;

  /* allocate private info structure */
  if ((di = MALLOC (osh, sizeof (dma_info_t))) == NULL)
    {
#ifdef BCMDBG
      printf ("dma_attach: out of memory, malloced %d bytes\n",
	      MALLOCED (osh));
#endif
      return (NULL);
    }
  bzero ((char *) di, sizeof (dma_info_t));

  di->msg_level = msg_level ? msg_level : &dma_msg_level;

  /* old chips w/o sb is no longer supported */
  ASSERT (sbh != NULL);

  di->dma64 = ((sb_coreflagshi (sbh, 0, 0) & SBTMH_DMA64) == SBTMH_DMA64);

#ifndef BCMDMA64
  if (di->dma64)
    {
      DMA_ERROR (("dma_attach: driver doesn't have the capability to support "
		  "64 bits DMA\n"));
      goto fail;
    }
#endif

  /* check arguments */
  ASSERT (ISPOWEROF2 (ntxd));
  ASSERT (ISPOWEROF2 (nrxd));
  if (nrxd == 0)
    ASSERT (dmaregsrx == NULL);
  if (ntxd == 0)
    ASSERT (dmaregstx == NULL);


  /* init dma reg pointer */
  if (di->dma64)
    {
      ASSERT (ntxd <= D64MAXDD);
      ASSERT (nrxd <= D64MAXDD);
      di->d64txregs = (dma64regs_t *) dmaregstx;
      di->d64rxregs = (dma64regs_t *) dmaregsrx;

      di->dma64align = D64RINGALIGN;
      if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2))
	{
	  /* for smaller dd table, HW relax the alignment requirement */
	  di->dma64align = D64RINGALIGN / 2;
	}
    }
  else
    {
      ASSERT (ntxd <= D32MAXDD);
      ASSERT (nrxd <= D32MAXDD);
      di->d32txregs = (dma32regs_t *) dmaregstx;
      di->d32rxregs = (dma32regs_t *) dmaregsrx;
    }

  DMA_TRACE (("%s: dma_attach: %s osh %p ntxd %d nrxd %d rxbufsize %d nrxpost %d " "rxoffset %d dmaregstx %p dmaregsrx %p\n", name, (di->dma64 ? "DMA64" : "DMA32"), osh, ntxd, nrxd, rxbufsize, nrxpost, rxoffset, dmaregstx, dmaregsrx));

  /* make a private copy of our callers name */
  strncpy (di->name, name, MAXNAMEL);
  di->name[MAXNAMEL - 1] = '\0';

  di->osh = osh;
  di->sbh = sbh;

  /* save tunables */
  di->ntxd = ntxd;
  di->nrxd = nrxd;

  /* the actual dma size doesn't include the extra headroom */
  if (rxbufsize > BCMEXTRAHDROOM)
    di->rxbufsize = rxbufsize - BCMEXTRAHDROOM;
  else
    di->rxbufsize = rxbufsize;

  di->nrxpost = nrxpost;
  di->rxoffset = rxoffset;

  /*
   * figure out the DMA physical address offset for dd and data
   *   for old chips w/o sb, use zero
   *   for new chips w sb,
   *     PCI/PCIE: they map silicon backplace address to zero based memory, need offset
   *     Other bus: use zero
   *     SB_BUS BIGENDIAN kludge: use sdram swapped region for data buffer, not descriptor
   */
  di->ddoffsetlow = 0;
  di->dataoffsetlow = 0;
  /* for pci bus, add offset */
  if (sbh->bustype == PCI_BUS)
    {
      if ((sbh->buscoretype == SB_PCIE) && di->dma64)
	{
	  /* pcie with DMA64 */
	  di->ddoffsetlow = 0;
	  di->ddoffsethigh = SB_PCIE_DMA_H32;
	}
      else
	{
	  /* pci(DMA32/DMA64) or pcie with DMA32 */
	  di->ddoffsetlow = SB_PCI_DMA;
	  di->ddoffsethigh = 0;
	}
      di->dataoffsetlow = di->ddoffsetlow;
      di->dataoffsethigh = di->ddoffsethigh;
    }

#if defined(__mips__) && defined(IL_BIGENDIAN)
  di->dataoffsetlow = di->dataoffsetlow + SB_SDRAM_SWAPPED;
#endif

  di->addrext = _dma_isaddrext (di);

  /* allocate tx packet pointer vector */
  if (ntxd)
    {
      size = ntxd * sizeof (void *);
      if ((di->txp = MALLOC (osh, size)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: out of tx memory, malloced %d bytes\n",
		      di->name, MALLOCED (osh)));
	  goto fail;
	}
      bzero ((char *) di->txp, size);
    }

  /* allocate rx packet pointer vector */
  if (nrxd)
    {
      size = nrxd * sizeof (void *);
      if ((di->rxp = MALLOC (osh, size)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: out of rx memory, malloced %d bytes\n",
		      di->name, MALLOCED (osh)));
	  goto fail;
	}
      bzero ((char *) di->rxp, size);
    }

  /* allocate transmit descriptor ring, only need ntxd descriptors but it must be aligned */
  if (ntxd)
    {
      if (!_dma_alloc (di, DMA_TX))
	goto fail;
    }

  /* allocate receive descriptor ring, only need nrxd descriptors but it must be aligned */
  if (nrxd)
    {
      if (!_dma_alloc (di, DMA_RX))
	goto fail;
    }

  if ((di->ddoffsetlow == SB_PCI_DMA) && (di->txdpa > SB_PCI_DMA_SZ)
      && !di->addrext)
    {
      DMA_ERROR (("%s: dma_attach: txdpa 0x%lx: addrext not supported\n",
		  di->name, di->txdpa));
      goto fail;
    }
  if ((di->ddoffsetlow == SB_PCI_DMA) && (di->rxdpa > SB_PCI_DMA_SZ)
      && !di->addrext)
    {
      DMA_ERROR (("%s: dma_attach: rxdpa 0x%lx: addrext not supported\n",
		  di->name, di->rxdpa));
      goto fail;
    }

  DMA_TRACE (("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh " "0x%x addrext %d\n", di->ddoffsetlow, di->ddoffsethigh, di->dataoffsetlow, di->dataoffsethigh, di->addrext));

  /* allocate tx packet pointer vector and DMA mapping vectors */
  if (ntxd)
    {

      size = ntxd * sizeof (osldma_t **);
      if ((di->txp_dmah = (osldma_t **) MALLOC (osh, size)) == NULL)
	goto fail;
      bzero ((char *) di->txp_dmah, size);
    }
  else
    di->txp_dmah = NULL;

  /* allocate rx packet pointer vector and DMA mapping vectors */
  if (nrxd)
    {

      size = nrxd * sizeof (osldma_t **);
      if ((di->rxp_dmah = (osldma_t **) MALLOC (osh, size)) == NULL)
	goto fail;
      bzero ((char *) di->rxp_dmah, size);

    }
  else
    di->rxp_dmah = NULL;

  /* initialize opsvec of function pointers */
  di->hnddma.di_fn = DMA64_ENAB (di) ? dma64proc : dma32proc;

  return ((hnddma_t *) di);

fail:
  _dma_detach (di);
  return (NULL);
}

/* init the tx or rx descriptor */
static INLINE void
dma32_dd_upd (dma_info_t * di, dma32dd_t * ddring, ulong pa, uint outidx,
	      uint32 * flags, uint32 bufcount)
{
  /* dma32 uses 32 bits control to fit both flags and bufcounter */
  *flags = *flags | (bufcount & CTRL_BC_MASK);

  if ((di->dataoffsetlow != SB_PCI_DMA) || !(pa & PCI32ADDR_HIGH))
    {
      W_SM (&ddring[outidx].addr, BUS_SWAP32 (pa + di->dataoffsetlow));
      W_SM (&ddring[outidx].ctrl, BUS_SWAP32 (*flags));
    }
  else
    {
      /* address extension */
      uint32 ae;
      ASSERT (di->addrext);
      ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
      pa &= ~PCI32ADDR_HIGH;

      *flags |= (ae << CTRL_AE_SHIFT);
      W_SM (&ddring[outidx].addr, BUS_SWAP32 (pa + di->dataoffsetlow));
      W_SM (&ddring[outidx].ctrl, BUS_SWAP32 (*flags));
    }
}

static INLINE void
dma64_dd_upd (dma_info_t * di, dma64dd_t * ddring, ulong pa, uint outidx,
	      uint32 * flags, uint32 bufcount)
{
  uint32 ctrl2 = bufcount & D64_CTRL2_BC_MASK;

  /* PCI bus with big(>1G) physical address, use address extension */
  if ((di->dataoffsetlow != SB_PCI_DMA) || !(pa & PCI32ADDR_HIGH))
    {
      W_SM (&ddring[outidx].addrlow, BUS_SWAP32 (pa + di->dataoffsetlow));
      W_SM (&ddring[outidx].addrhigh, BUS_SWAP32 (0 + di->dataoffsethigh));
      W_SM (&ddring[outidx].ctrl1, BUS_SWAP32 (*flags));
      W_SM (&ddring[outidx].ctrl2, BUS_SWAP32 (ctrl2));
    }
  else
    {
      /* address extension */
      uint32 ae;
      ASSERT (di->addrext);

      ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
      pa &= ~PCI32ADDR_HIGH;

      ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE;
      W_SM (&ddring[outidx].addrlow, BUS_SWAP32 (pa + di->dataoffsetlow));
      W_SM (&ddring[outidx].addrhigh, BUS_SWAP32 (0 + di->dataoffsethigh));
      W_SM (&ddring[outidx].ctrl1, BUS_SWAP32 (*flags));
      W_SM (&ddring[outidx].ctrl2, BUS_SWAP32 (ctrl2));
    }
}

static bool
_dma32_addrext (osl_t * osh, dma32regs_t * dma32regs)
{
  uint32 w;

  OR_REG (osh, &dma32regs->control, XC_AE);
  w = R_REG (osh, &dma32regs->control);
  AND_REG (osh, &dma32regs->control, ~XC_AE);
  return ((w & XC_AE) == XC_AE);
}

static bool
_dma_alloc (dma_info_t * di, uint direction)
{
  if (DMA64_ENAB (di))
    {
      return dma64_alloc (di, direction);
    }
  else
    {
      return dma32_alloc (di, direction);
    }
}

/* !! may be called with core in reset */
static void
_dma_detach (dma_info_t * di)
{
  if (di == NULL)
    return;

  DMA_TRACE (("%s: dma_detach\n", di->name));

  /* shouldn't be here if descriptors are unreclaimed */
  ASSERT (di->txin == di->txout);
  ASSERT (di->rxin == di->rxout);

  /* free dma descriptor rings */
  if (DMA64_ENAB (di))
    {
      if (di->txd64)
	DMA_FREE_CONSISTENT (di->osh,
			     ((int8 *) (uintptr) di->txd64 - di->txdalign),
			     di->txdalloc, (di->txdpa - di->txdalign),
			     &di->tx_dmah);
      if (di->rxd64)
	DMA_FREE_CONSISTENT (di->osh,
			     ((int8 *) (uintptr) di->rxd64 - di->rxdalign),
			     di->rxdalloc, (di->rxdpa - di->rxdalign),
			     &di->rx_dmah);
    }
  else
    {
      if (di->txd32)
	DMA_FREE_CONSISTENT (di->osh,
			     ((int8 *) (uintptr) di->txd32 - di->txdalign),
			     di->txdalloc, (di->txdpa - di->txdalign),
			     &di->tx_dmah);
      if (di->rxd32)
	DMA_FREE_CONSISTENT (di->osh,
			     ((int8 *) (uintptr) di->rxd32 - di->rxdalign),
			     di->rxdalloc, (di->rxdpa - di->rxdalign),
			     &di->rx_dmah);
    }

  /* free packet pointer vectors */
  if (di->txp)
    MFREE (di->osh, (void *) di->txp, (di->ntxd * sizeof (void *)));
  if (di->rxp)
    MFREE (di->osh, (void *) di->rxp, (di->nrxd * sizeof (void *)));

  /* free tx packet DMA handles */
  if (di->txp_dmah)
    MFREE (di->osh, (void *) di->txp_dmah, di->ntxd * sizeof (osldma_t **));

  /* free rx packet DMA handles */
  if (di->rxp_dmah)
    MFREE (di->osh, (void *) di->rxp_dmah, di->nrxd * sizeof (osldma_t **));

  /* free our private info structure */
  MFREE (di->osh, (void *) di, sizeof (dma_info_t));

}

/* return TRUE if this dma engine supports DmaExtendedAddrChanges, otherwise FALSE */
static bool
_dma_isaddrext (dma_info_t * di)
{
  if (DMA64_ENAB (di))
    {
      /* DMA64 supports full 32 bits or 64 bits. AE is always valid */

      /* not all tx or rx channel are available */
      if (di->d64txregs != NULL)
	{
	  if (!_dma64_addrext (di->osh, di->d64txregs))
	    {
	      DMA_ERROR (("%s: _dma_isaddrext: DMA64 tx doesn't have AE set\n", di->name));
	      ASSERT (0);
	    }
	  return TRUE;
	}
      else if (di->d64rxregs != NULL)
	{
	  if (!_dma64_addrext (di->osh, di->d64rxregs))
	    {
	      DMA_ERROR (("%s: _dma_isaddrext: DMA64 rx doesn't have AE set\n", di->name));
	      ASSERT (0);
	    }
	  return TRUE;
	}
      return FALSE;
    }
  else if (di->d32txregs)
    return (_dma32_addrext (di->osh, di->d32txregs));
  else if (di->d32rxregs)
    return (_dma32_addrext (di->osh, di->d32rxregs));
  return FALSE;
}

/* initialize descriptor table base address */
static void
_dma_ddtable_init (dma_info_t * di, uint direction, ulong pa)
{
  if (DMA64_ENAB (di))
    {

      if ((di->ddoffsetlow != SB_PCI_DMA) || !(pa & PCI32ADDR_HIGH))
	{
	  if (direction == DMA_TX)
	    {
	      W_REG (di->osh, &di->d64txregs->addrlow,
		     (pa + di->ddoffsetlow));
	      W_REG (di->osh, &di->d64txregs->addrhigh, di->ddoffsethigh);
	    }
	  else
	    {
	      W_REG (di->osh, &di->d64rxregs->addrlow,
		     (pa + di->ddoffsetlow));
	      W_REG (di->osh, &di->d64rxregs->addrhigh, di->ddoffsethigh);
	    }
	}
      else
	{
	  /* DMA64 32bits address extension */
	  uint32 ae;
	  ASSERT (di->addrext);

	  /* shift the high bit(s) from pa to ae */
	  ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
	  pa &= ~PCI32ADDR_HIGH;

	  if (direction == DMA_TX)
	    {
	      W_REG (di->osh, &di->d64txregs->addrlow,
		     (pa + di->ddoffsetlow));
	      W_REG (di->osh, &di->d64txregs->addrhigh, di->ddoffsethigh);
	      SET_REG (di->osh, &di->d64txregs->control, D64_XC_AE,
		       (ae << D64_XC_AE_SHIFT));
	    }
	  else
	    {
	      W_REG (di->osh, &di->d64rxregs->addrlow,
		     (pa + di->ddoffsetlow));
	      W_REG (di->osh, &di->d64rxregs->addrhigh, di->ddoffsethigh);
	      SET_REG (di->osh, &di->d64rxregs->control, D64_RC_AE,
		       (ae << D64_RC_AE_SHIFT));
	    }
	}

    }
  else
    {
      if ((di->ddoffsetlow != SB_PCI_DMA) || !(pa & PCI32ADDR_HIGH))
	{
	  if (direction == DMA_TX)
	    W_REG (di->osh, &di->d32txregs->addr, (pa + di->ddoffsetlow));
	  else
	    W_REG (di->osh, &di->d32rxregs->addr, (pa + di->ddoffsetlow));
	}
      else
	{
	  /* dma32 address extension */
	  uint32 ae;
	  ASSERT (di->addrext);

	  /* shift the high bit(s) from pa to ae */
	  ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
	  pa &= ~PCI32ADDR_HIGH;

	  if (direction == DMA_TX)
	    {
	      W_REG (di->osh, &di->d32txregs->addr, (pa + di->ddoffsetlow));
	      SET_REG (di->osh, &di->d32txregs->control, XC_AE,
		       ae << XC_AE_SHIFT);
	    }
	  else
	    {
	      W_REG (di->osh, &di->d32rxregs->addr, (pa + di->ddoffsetlow));
	      SET_REG (di->osh, &di->d32rxregs->control, RC_AE,
		       ae << RC_AE_SHIFT);
	    }
	}
    }
}

static void
_dma_fifoloopbackenable (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_fifoloopbackenable\n", di->name));
  if (DMA64_ENAB (di))
    OR_REG (di->osh, &di->d64txregs->control, D64_XC_LE);
  else
    OR_REG (di->osh, &di->d32txregs->control, XC_LE);
}

static void
_dma_rxinit (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_rxinit\n", di->name));

  if (di->nrxd == 0)
    return;

  di->rxin = di->rxout = 0;

  /* clear rx descriptor ring */
  if (DMA64_ENAB (di))
    BZERO_SM ((void *) (uintptr) di->rxd64, (di->nrxd * sizeof (dma64dd_t)));
  else
    BZERO_SM ((void *) (uintptr) di->rxd32, (di->nrxd * sizeof (dma32dd_t)));

  _dma_rxenable (di);
  _dma_ddtable_init (di, DMA_RX, di->rxdpa);
}

static void
_dma_rxenable (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_rxenable\n", di->name));

  if (DMA64_ENAB (di))
    W_REG (di->osh, &di->d64rxregs->control,
	   ((di->rxoffset << D64_RC_RO_SHIFT) | D64_RC_RE));
  else
    W_REG (di->osh, &di->d32rxregs->control,
	   ((di->rxoffset << RC_RO_SHIFT) | RC_RE));
}

/* !! rx entry routine, returns a pointer to the next frame received,
 * or NULL if there are no more
 */
static void *
_dma_rx (dma_info_t * di)
{
  void *p;
  uint len;
  int skiplen = 0;

  while ((p = _dma_getnextrxp (di, FALSE)))
    {
      /* skip giant packets which span multiple rx descriptors */
      if (skiplen > 0)
	{
	  skiplen -= di->rxbufsize;
	  if (skiplen < 0)
	    skiplen = 0;
	  PKTFREE (di->osh, p, FALSE);
	  continue;
	}

      len = ltoh16 (*(uint16 *) (PKTDATA (di->osh, p)));
      DMA_TRACE (("%s: dma_rx len %d\n", di->name, len));

      /* bad frame length check */
      if (len > (di->rxbufsize - di->rxoffset))
	{
	  DMA_ERROR (("%s: dma_rx: bad frame length (%d)\n", di->name, len));
	  if (len > 0)
	    skiplen = len - (di->rxbufsize - di->rxoffset);
	  PKTFREE (di->osh, p, FALSE);
	  di->hnddma.rxgiants++;
	  continue;
	}

      /* set actual length */
      PKTSETLEN (di->osh, p, (di->rxoffset + len));

      break;
    }

  return (p);
}

/* post receive buffers */
static void
_dma_rxfill (dma_info_t * di)
{
  void *p;
  uint rxin, rxout;
  uint32 flags = 0;
  uint n;
  uint i;
  uint32 pa;
  uint extra_offset = 0;

  /*
   * Determine how many receive buffers we're lacking
   * from the full complement, allocate, initialize,
   * and post them, then update the chip rx lastdscr.
   */

  rxin = di->rxin;
  rxout = di->rxout;

  n = di->nrxpost - NRXDACTIVE (rxin, rxout);

  DMA_TRACE (("%s: dma_rxfill: post %d\n", di->name, n));

  if (di->rxbufsize > BCMEXTRAHDROOM)
    extra_offset = BCMEXTRAHDROOM;

  for (i = 0; i < n; i++)
    {
      /* the di->rxbufsize doesn't include the extra headroom, we need to add it to the
         size to be allocated
       */
      if ((p = PKTGET (di->osh, di->rxbufsize + extra_offset, FALSE)) == NULL)
	{
	  DMA_ERROR (("%s: dma_rxfill: out of rxbufs\n", di->name));
	  di->hnddma.rxnobuf++;
	  break;
	}
      /* reserve an extra headroom, if applicable */
      if (extra_offset)
	PKTPULL (di->osh, p, extra_offset);

      /* Do a cached write instead of uncached write since DMA_MAP
       * will flush the cache.
       */
      *(uint32 *) (PKTDATA (di->osh, p)) = 0;

      pa = (uint32) DMA_MAP (di->osh, PKTDATA (di->osh, p),
			     di->rxbufsize, DMA_RX, p, &di->rxp_dmah[rxout]);

      ASSERT (ISALIGNED (pa, 4));

      /* save the free packet pointer */
      ASSERT (di->rxp[rxout] == NULL);
      di->rxp[rxout] = p;

      /* reset flags for each descriptor */
      flags = 0;
      if (DMA64_ENAB (di))
	{
	  if (rxout == (di->nrxd - 1))
	    flags = D64_CTRL1_EOT;

	  dma64_dd_upd (di, di->rxd64, pa, rxout, &flags, di->rxbufsize);
	}
      else
	{
	  if (rxout == (di->nrxd - 1))
	    flags = CTRL_EOT;

	  dma32_dd_upd (di, di->rxd32, pa, rxout, &flags, di->rxbufsize);
	}
      rxout = NEXTRXD (rxout);
    }

  di->rxout = rxout;

  /* update the chip lastdscr pointer */
  if (DMA64_ENAB (di))
    {
      W_REG (di->osh, &di->d64rxregs->ptr, I2B (rxout, dma64dd_t));
    }
  else
    {
      W_REG (di->osh, &di->d32rxregs->ptr, I2B (rxout, dma32dd_t));
    }
}

/* like getnexttxp but no reclaim */
static void *
_dma_peeknexttxp (dma_info_t * di)
{
  uint end, i;

  if (di->ntxd == 0)
    return (NULL);

  if (DMA64_ENAB (di))
    {
      end =
	B2I (R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_CD_MASK,
	     dma64dd_t);
    }
  else
    {
      end =
	B2I (R_REG (di->osh, &di->d32txregs->status) & XS_CD_MASK, dma32dd_t);
    }

  for (i = di->txin; i != end; i = NEXTTXD (i))
    if (di->txp[i])
      return (di->txp[i]);

  return (NULL);
}

static void
_dma_rxreclaim (dma_info_t * di)
{
  void *p;

  /* "unused local" warning suppression for OSLs that
   * define PKTFREE() without using the di->osh arg
   */
  di = di;

  DMA_TRACE (("%s: dma_rxreclaim\n", di->name));

  while ((p = _dma_getnextrxp (di, TRUE)))
    PKTFREE (di->osh, p, FALSE);
}

static void *
_dma_getnextrxp (dma_info_t * di, bool forceall)
{
  if (di->nrxd == 0)
    return (NULL);

  if (DMA64_ENAB (di))
    {
      return dma64_getnextrxp (di, forceall);
    }
  else
    {
      return dma32_getnextrxp (di, forceall);
    }
}

static void
_dma_txblock (dma_info_t * di)
{
  di->hnddma.txavail = 0;
}

static void
_dma_txunblock (dma_info_t * di)
{
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;
}

static uint
_dma_txactive (dma_info_t * di)
{
  return (NTXDACTIVE (di->txin, di->txout));
}

static void
_dma_counterreset (dma_info_t * di)
{
  /* reset all software counter */
  di->hnddma.rxgiants = 0;
  di->hnddma.rxnobuf = 0;
  di->hnddma.txnobuf = 0;
}

/* get the address of the var in order to change later */
static uintptr
_dma_getvar (dma_info_t * di, const char *name)
{
  if (!strcmp (name, "&txavail"))
    return ((uintptr) & (di->hnddma.txavail));
  else
    {
      ASSERT (0);
    }
  return (0);
}

void
dma_txpioloopback (osl_t * osh, dma32regs_t * regs)
{
  OR_REG (osh, &regs->control, XC_LE);
}

#ifdef BCMDBG
static void
dma32_dumpring (dma_info_t * di, struct bcmstrbuf *b, dma32dd_t * ring,
		uint start, uint end, uint max_num)
{
  uint i;

  for (i = start; i != end; i = XXD ((i + 1), max_num))
    {
      /* in the format of high->low 8 bytes */
      bcm_bprintf (b, "ring index %d: 0x%x %x\n", i, ring[i].addr,
		   ring[i].ctrl);
    }
}

static void
dma32_dumptx (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  if (di->ntxd == 0)
    return;

  bcm_bprintf (b, "DMA32: txd32 %p txdpa 0x%lx txp %p txin %d txout %d "
	       "txavail %d\n", di->txd32, di->txdpa, di->txp, di->txin,
	       di->txout, di->hnddma.txavail);

  bcm_bprintf (b, "xmtcontrol 0x%x xmtaddr 0x%x xmtptr 0x%x xmtstatus 0x%x\n",
	       R_REG (di->osh, &di->d32txregs->control),
	       R_REG (di->osh, &di->d32txregs->addr),
	       R_REG (di->osh, &di->d32txregs->ptr),
	       R_REG (di->osh, &di->d32txregs->status));

  if (dumpring && di->txd32)
    dma32_dumpring (di, b, di->txd32, di->txin, di->txout, di->ntxd);
}

static void
dma32_dumprx (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  if (di->nrxd == 0)
    return;

  bcm_bprintf (b, "DMA32: rxd32 %p rxdpa 0x%lx rxp %p rxin %d rxout %d\n",
	       di->rxd32, di->rxdpa, di->rxp, di->rxin, di->rxout);

  bcm_bprintf (b, "rcvcontrol 0x%x rcvaddr 0x%x rcvptr 0x%x rcvstatus 0x%x\n",
	       R_REG (di->osh, &di->d32rxregs->control),
	       R_REG (di->osh, &di->d32rxregs->addr),
	       R_REG (di->osh, &di->d32rxregs->ptr),
	       R_REG (di->osh, &di->d32rxregs->status));
  if (di->rxd32 && dumpring)
    dma32_dumpring (di, b, di->rxd32, di->rxin, di->rxout, di->nrxd);
}

static void
dma32_dump (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  dma32_dumptx (di, b, dumpring);
  dma32_dumprx (di, b, dumpring);
}

static void
dma64_dumpring (dma_info_t * di, struct bcmstrbuf *b, dma64dd_t * ring,
		uint start, uint end, uint max_num)
{
  uint i;

  for (i = start; i != end; i = XXD ((i + 1), max_num))
    {
      /* in the format of high->low 16 bytes */
      bcm_bprintf (b, "ring index %d: 0x%x %x %x %x\n",
		   i, ring[i].addrhigh, ring[i].addrlow, ring[i].ctrl2,
		   ring[i].ctrl1);
    }
}

static void
dma64_dumptx (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  if (di->ntxd == 0)
    return;

  bcm_bprintf (b, "DMA64: txd64 %p txdpa 0x%lx txp %p txin %d txout %d "
	       "txavail %d\n", di->txd64, di->txdpa, di->txp, di->txin,
	       di->txout, di->hnddma.txavail);

  bcm_bprintf (b, "xmtcontrol 0x%x xmtaddrlow 0x%x xmtaddrhigh 0x%x "
	       "xmtptr 0x%x xmtstatus0 0x%x xmtstatus1 0x%x\n",
	       R_REG (di->osh, &di->d64txregs->control),
	       R_REG (di->osh, &di->d64txregs->addrlow),
	       R_REG (di->osh, &di->d64txregs->addrhigh),
	       R_REG (di->osh, &di->d64txregs->ptr),
	       R_REG (di->osh, &di->d64txregs->status0),
	       R_REG (di->osh, &di->d64txregs->status1));

  if (dumpring && di->txd64)
    {
      dma64_dumpring (di, b, di->txd64, di->txin, di->txout, di->ntxd);
    }
}

static void
dma64_dumprx (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  if (di->nrxd == 0)
    return;

  bcm_bprintf (b, "DMA64: rxd64 %p rxdpa 0x%lx rxp %p rxin %d rxout %d\n",
	       di->rxd64, di->rxdpa, di->rxp, di->rxin, di->rxout);

  bcm_bprintf (b, "rcvcontrol 0x%x rcvaddrlow 0x%x rcvaddrhigh 0x%x rcvptr "
	       "0x%x rcvstatus0 0x%x rcvstatus1 0x%x\n",
	       R_REG (di->osh, &di->d64rxregs->control),
	       R_REG (di->osh, &di->d64rxregs->addrlow),
	       R_REG (di->osh, &di->d64rxregs->addrhigh),
	       R_REG (di->osh, &di->d64rxregs->ptr),
	       R_REG (di->osh, &di->d64rxregs->status0),
	       R_REG (di->osh, &di->d64rxregs->status1));
  if (di->rxd64 && dumpring)
    {
      dma64_dumpring (di, b, di->rxd64, di->rxin, di->rxout, di->nrxd);
    }
}

static void
dma64_dump (dma_info_t * di, struct bcmstrbuf *b, bool dumpring)
{
  dma64_dumptx (di, b, dumpring);
  dma64_dumprx (di, b, dumpring);
}

#endif /* BCMDBG */


/* 32 bits DMA functions */
static void
dma32_txinit (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txinit\n", di->name));

  if (di->ntxd == 0)
    return;

  di->txin = di->txout = 0;
  di->hnddma.txavail = di->ntxd - 1;

  /* clear tx descriptor ring */
  BZERO_SM ((void *) (uintptr) di->txd32, (di->ntxd * sizeof (dma32dd_t)));
  W_REG (di->osh, &di->d32txregs->control, XC_XE);
  _dma_ddtable_init (di, DMA_TX, di->txdpa);
}

static bool
dma32_txenabled (dma_info_t * di)
{
  uint32 xc;

  /* If the chip is dead, it is not enabled :-) */
  xc = R_REG (di->osh, &di->d32txregs->control);
  return ((xc != 0xffffffff) && (xc & XC_XE));
}

static void
dma32_txsuspend (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txsuspend\n", di->name));

  if (di->ntxd == 0)
    return;

  OR_REG (di->osh, &di->d32txregs->control, XC_SE);
}

static void
dma32_txresume (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txresume\n", di->name));

  if (di->ntxd == 0)
    return;

  AND_REG (di->osh, &di->d32txregs->control, ~XC_SE);
}

static bool
dma32_txsuspended (dma_info_t * di)
{
  return (di->ntxd == 0)
    || ((R_REG (di->osh, &di->d32txregs->control) & XC_SE) == XC_SE);
}

static void
dma32_txreclaim (dma_info_t * di, bool forceall)
{
  void *p;

  DMA_TRACE (("%s: dma_txreclaim %s\n", di->name, forceall ? "all" : ""));

  while ((p = dma32_getnexttxp (di, forceall)))
    PKTFREE (di->osh, p, TRUE);
}

static bool
dma32_txstopped (dma_info_t * di)
{
  return ((R_REG (di->osh, &di->d32txregs->status) & XS_XS_MASK) ==
	  XS_XS_STOPPED);
}

static bool
dma32_rxstopped (dma_info_t * di)
{
  return ((R_REG (di->osh, &di->d32rxregs->status) & RS_RS_MASK) ==
	  RS_RS_STOPPED);
}

static bool
dma32_alloc (dma_info_t * di, uint direction)
{
  uint size;
  uint ddlen;
  void *va;

  ddlen = sizeof (dma32dd_t);

  size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen);

  if (!ISALIGNED (DMA_CONSISTENT_ALIGN, D32RINGALIGN))
    size += D32RINGALIGN;


  if (direction == DMA_TX)
    {
      if ((va =
	   DMA_ALLOC_CONSISTENT (di->osh, size, &di->txdpa,
				 &di->tx_dmah)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: DMA_ALLOC_CONSISTENT(ntxd) failed\n",
		      di->name));
	  return FALSE;
	}

      di->txd32 = (dma32dd_t *) ROUNDUP ((uintptr) va, D32RINGALIGN);
      di->txdalign = (uint) ((int8 *) (uintptr) di->txd32 - (int8 *) va);
      di->txdpa += di->txdalign;
      di->txdalloc = size;
      ASSERT (ISALIGNED ((uintptr) di->txd32, D32RINGALIGN));
    }
  else
    {
      if ((va =
	   DMA_ALLOC_CONSISTENT (di->osh, size, &di->rxdpa,
				 &di->rx_dmah)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: DMA_ALLOC_CONSISTENT(nrxd) failed\n",
		      di->name));
	  return FALSE;
	}
      di->rxd32 = (dma32dd_t *) ROUNDUP ((uintptr) va, D32RINGALIGN);
      di->rxdalign = (uint) ((int8 *) (uintptr) di->rxd32 - (int8 *) va);
      di->rxdpa += di->rxdalign;
      di->rxdalloc = size;
      ASSERT (ISALIGNED ((uintptr) di->rxd32, D32RINGALIGN));
    }

  return TRUE;
}

static bool
dma32_txreset (dma_info_t * di)
{
  uint32 status;

  if (di->ntxd == 0)
    return TRUE;

  /* suspend tx DMA first */
  W_REG (di->osh, &di->d32txregs->control, XC_SE);
  SPINWAIT (((status = (R_REG (di->osh, &di->d32txregs->status) & XS_XS_MASK))
	     != XS_XS_DISABLED) &&
	    (status != XS_XS_IDLE) && (status != XS_XS_STOPPED), (10000));

  W_REG (di->osh, &di->d32txregs->control, 0);
  SPINWAIT (((status = (R_REG (di->osh,
			       &di->d32txregs->status) & XS_XS_MASK)) !=
	     XS_XS_DISABLED), 10000);

  /* wait for the last transaction to complete */
  OSL_DELAY (300);

  return (status == XS_XS_DISABLED);
}

static bool
dma32_rxidle (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_rxidle\n", di->name));

  if (di->nrxd == 0)
    return TRUE;

  return ((R_REG (di->osh, &di->d32rxregs->status) & RS_CD_MASK) ==
	  R_REG (di->osh, &di->d32rxregs->ptr));
}

static bool
dma32_rxreset (dma_info_t * di)
{
  uint32 status;

  if (di->nrxd == 0)
    return TRUE;

  W_REG (di->osh, &di->d32rxregs->control, 0);
  SPINWAIT (((status = (R_REG (di->osh,
			       &di->d32rxregs->status) & RS_RS_MASK)) !=
	     RS_RS_DISABLED), 10000);

  return (status == RS_RS_DISABLED);
}

static bool
dma32_rxenabled (dma_info_t * di)
{
  uint32 rc;

  rc = R_REG (di->osh, &di->d32rxregs->control);
  return ((rc != 0xffffffff) && (rc & RC_RE));
}

static bool
dma32_txsuspendedidle (dma_info_t * di)
{
  if (di->ntxd == 0)
    return TRUE;

  if (!(R_REG (di->osh, &di->d32txregs->control) & XC_SE))
    return 0;

  if ((R_REG (di->osh, &di->d32txregs->status) & XS_XS_MASK) != XS_XS_IDLE)
    return 0;

  OSL_DELAY (2);
  return ((R_REG (di->osh, &di->d32txregs->status) & XS_XS_MASK) ==
	  XS_XS_IDLE);
}

/* !! tx entry routine
 * supports full 32bit dma engine buffer addressing so
 * dma buffers can cross 4 Kbyte page boundaries.
 */
static int
dma32_txfast (dma_info_t * di, void *p0, bool commit)
{
  void *p, *next;
  uchar *data;
  uint len;
  uint txout;
  uint32 flags = 0;
  uint32 pa;

  DMA_TRACE (("%s: dma_txfast\n", di->name));

  txout = di->txout;

  /*
   * Walk the chain of packet buffers
   * allocating and initializing transmit descriptor entries.
   */
  for (p = p0; p; p = next)
    {
      data = PKTDATA (di->osh, p);
      len = PKTLEN (di->osh, p);
      next = PKTNEXT (di->osh, p);

      /* return nonzero if out of tx descriptors */
      if (NEXTTXD (txout) == di->txin)
	goto outoftxd;

      if (len == 0)
	continue;

      /* get physical address of buffer start */
      pa =
	(uint32) DMA_MAP (di->osh, data, len, DMA_TX, p,
			  &di->txp_dmah[txout]);

      flags = 0;
      if (p == p0)
	flags |= CTRL_SOF;
      if (next == NULL)
	flags |= (CTRL_IOC | CTRL_EOF);
      if (txout == (di->ntxd - 1))
	flags |= CTRL_EOT;

      dma32_dd_upd (di, di->txd32, pa, txout, &flags, len);
      ASSERT (di->txp[txout] == NULL);

      txout = NEXTTXD (txout);
    }

  /* if last txd eof not set, fix it */
  if (!(flags & CTRL_EOF))
    W_SM (&di->txd32[PREVTXD (txout)].ctrl,
	  BUS_SWAP32 (flags | CTRL_IOC | CTRL_EOF));

  /* save the packet */
  di->txp[PREVTXD (txout)] = p0;

  /* bump the tx descriptor index */
  di->txout = txout;

  /* kick the chip */
  if (commit)
    W_REG (di->osh, &di->d32txregs->ptr, I2B (txout, dma32dd_t));

  /* tx flow control */
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  return (0);

outoftxd:
  DMA_ERROR (("%s: dma_txfast: out of txds\n", di->name));
  PKTFREE (di->osh, p0, TRUE);
  di->hnddma.txavail = 0;
  di->hnddma.txnobuf++;
  return (-1);
}

/*
 * Reclaim next completed txd (txds if using chained buffers) and
 * return associated packet.
 * If 'force' is true, reclaim txd(s) and return associated packet
 * regardless of the value of the hardware "curr" pointer.
 */
static void *
dma32_getnexttxp (dma_info_t * di, bool forceall)
{
  uint start, end, i;
  void *txp;

  DMA_TRACE (("%s: dma_getnexttxp %s\n", di->name, forceall ? "all" : ""));

  if (di->ntxd == 0)
    return (NULL);

  txp = NULL;

  start = di->txin;
  if (forceall)
    end = di->txout;
  else
    end =
      B2I (R_REG (di->osh, &di->d32txregs->status) & XS_CD_MASK, dma32dd_t);

  if ((start == 0) && (end > di->txout))
    goto bogus;

  for (i = start; i != end && !txp; i = NEXTTXD (i))
    {
      DMA_UNMAP (di->osh,
		 (BUS_SWAP32 (R_SM (&di->txd32[i].addr)) - di->dataoffsetlow),
		 (BUS_SWAP32 (R_SM (&di->txd32[i].ctrl)) & CTRL_BC_MASK),
		 DMA_TX, di->txp[i], &di->txp_dmah[i]);

      W_SM (&di->txd32[i].addr, 0xdeadbeef);
      txp = di->txp[i];
      di->txp[i] = NULL;
    }

  di->txin = i;

  /* tx flow control */
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  return (txp);

bogus:
/*
	DMA_ERROR(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n",
		start, end, di->txout, forceall));
*/
  return (NULL);
}

static void *
dma32_getnextrxp (dma_info_t * di, bool forceall)
{
  uint i;
  void *rxp;

  /* if forcing, dma engine must be disabled */
  ASSERT (!forceall || !dma32_rxenabled (di));

  i = di->rxin;

  /* return if no packets posted */
  if (i == di->rxout)
    return (NULL);

  /* ignore curr if forceall */
  if (!forceall
      && (i ==
	  B2I (R_REG (di->osh, &di->d32rxregs->status) & RS_CD_MASK,
	       dma32dd_t)))
    return (NULL);

  /* get the packet pointer that corresponds to the rx descriptor */
  rxp = di->rxp[i];
  ASSERT (rxp);
  di->rxp[i] = NULL;

  /* clear this packet from the descriptor ring */
  DMA_UNMAP (di->osh,
	     (BUS_SWAP32 (R_SM (&di->rxd32[i].addr)) - di->dataoffsetlow),
	     di->rxbufsize, DMA_RX, rxp, &di->rxp_dmah[i]);

  W_SM (&di->rxd32[i].addr, 0xdeadbeef);

  di->rxin = NEXTRXD (i);

  return (rxp);
}

/*
 * Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
 */
static void
dma32_txrotate (dma_info_t * di)
{
  uint ad;
  uint nactive;
  uint rot;
  uint old, new;
  uint32 w;
  uint first, last;

  ASSERT (dma32_txsuspendedidle (di));

  nactive = _dma_txactive (di);
  ad =
    B2I (((R_REG (di->osh, &di->d32txregs->status) & XS_AD_MASK) >>
	  XS_AD_SHIFT), dma32dd_t);
  rot = TXD (ad - di->txin);

  ASSERT (rot < di->ntxd);

  /* full-ring case is a lot harder - don't worry about this */
  if (rot >= (di->ntxd - nactive))
    {
      DMA_ERROR (("%s: dma_txrotate: ring full - punt\n", di->name));
      return;
    }

  first = di->txin;
  last = PREVTXD (di->txout);

  /* move entries starting at last and moving backwards to first */
  for (old = last; old != PREVTXD (first); old = PREVTXD (old))
    {
      new = TXD (old + rot);

      /*
       * Move the tx dma descriptor.
       * EOT is set only in the last entry in the ring.
       */
      w = BUS_SWAP32 (R_SM (&di->txd32[old].ctrl)) & ~CTRL_EOT;
      if (new == (di->ntxd - 1))
	w |= CTRL_EOT;
      W_SM (&di->txd32[new].ctrl, BUS_SWAP32 (w));
      W_SM (&di->txd32[new].addr, R_SM (&di->txd32[old].addr));

      /* zap the old tx dma descriptor address field */
      W_SM (&di->txd32[old].addr, BUS_SWAP32 (0xdeadbeef));

      /* move the corresponding txp[] entry */
      ASSERT (di->txp[new] == NULL);
      di->txp[new] = di->txp[old];
      di->txp[old] = NULL;
    }

  /* update txin and txout */
  di->txin = ad;
  di->txout = TXD (di->txout + rot);
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  /* kick the chip */
  W_REG (di->osh, &di->d32txregs->ptr, I2B (di->txout, dma32dd_t));
}

/* 64 bits DMA functions */

#ifdef BCMDMA64
static void
dma64_txinit (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txinit\n", di->name));

  if (di->ntxd == 0)
    return;

  di->txin = di->txout = 0;
  di->hnddma.txavail = di->ntxd - 1;

  /* clear tx descriptor ring */
  BZERO_SM ((void *) (uintptr) di->txd64, (di->ntxd * sizeof (dma64dd_t)));
  W_REG (di->osh, &di->d64txregs->control, D64_XC_XE);
  _dma_ddtable_init (di, DMA_TX, di->txdpa);
}

static bool
dma64_txenabled (dma_info_t * di)
{
  uint32 xc;

  /* If the chip is dead, it is not enabled :-) */
  xc = R_REG (di->osh, &di->d64txregs->control);
  return ((xc != 0xffffffff) && (xc & D64_XC_XE));
}

static void
dma64_txsuspend (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txsuspend\n", di->name));

  if (di->ntxd == 0)
    return;

  OR_REG (di->osh, &di->d64txregs->control, D64_XC_SE);
}

static void
dma64_txresume (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_txresume\n", di->name));

  if (di->ntxd == 0)
    return;

  AND_REG (di->osh, &di->d64txregs->control, ~D64_XC_SE);
}

static bool
dma64_txsuspended (dma_info_t * di)
{
  return (di->ntxd == 0)
    || ((R_REG (di->osh, &di->d64txregs->control) & D64_XC_SE) == D64_XC_SE);
}

static void
dma64_txreclaim (dma_info_t * di, bool forceall)
{
  void *p;

  DMA_TRACE (("%s: dma_txreclaim %s\n", di->name, forceall ? "all" : ""));

  while ((p = dma64_getnexttxp (di, forceall)))
    PKTFREE (di->osh, p, TRUE);
}

static bool
dma64_txstopped (dma_info_t * di)
{
  return ((R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_XS_MASK) ==
	  D64_XS0_XS_STOPPED);
}

static bool
dma64_rxstopped (dma_info_t * di)
{
  return ((R_REG (di->osh, &di->d64rxregs->status0) & D64_RS0_RS_MASK) ==
	  D64_RS0_RS_STOPPED);
}

static bool
dma64_alloc (dma_info_t * di, uint direction)
{
  uint size;
  uint ddlen;
  uint32 alignbytes;
  void *va;

  ddlen = sizeof (dma64dd_t);

  size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen);

  alignbytes = di->dma64align;

  if (!ISALIGNED (DMA_CONSISTENT_ALIGN, alignbytes))
    size += alignbytes;

  if (direction == DMA_TX)
    {
      if ((va =
	   DMA_ALLOC_CONSISTENT (di->osh, size, &di->txdpa,
				 &di->tx_dmah)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: DMA_ALLOC_CONSISTENT(ntxd) failed\n",
		      di->name));
	  return FALSE;
	}

      di->txd64 = (dma64dd_t *) ROUNDUP ((uintptr) va, alignbytes);
      di->txdalign = (uint) ((int8 *) (uintptr) di->txd64 - (int8 *) va);
      di->txdpa += di->txdalign;
      di->txdalloc = size;
      ASSERT (ISALIGNED ((uintptr) di->txd64, alignbytes));
    }
  else
    {
      if ((va =
	   DMA_ALLOC_CONSISTENT (di->osh, size, &di->rxdpa,
				 &di->rx_dmah)) == NULL)
	{
	  DMA_ERROR (("%s: dma_attach: DMA_ALLOC_CONSISTENT(nrxd) failed\n",
		      di->name));
	  return FALSE;
	}
      di->rxd64 = (dma64dd_t *) ROUNDUP ((uintptr) va, alignbytes);
      di->rxdalign = (uint) ((int8 *) (uintptr) di->rxd64 - (int8 *) va);
      di->rxdpa += di->rxdalign;
      di->rxdalloc = size;
      ASSERT (ISALIGNED ((uintptr) di->rxd64, alignbytes));
    }

  return TRUE;
}

static bool
dma64_txreset (dma_info_t * di)
{
  uint32 status;

  if (di->ntxd == 0)
    return TRUE;

  /* suspend tx DMA first */
  W_REG (di->osh, &di->d64txregs->control, D64_XC_SE);
  SPINWAIT (((status =
	      (R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_XS_MASK)) !=
	     D64_XS0_XS_DISABLED) && (status != D64_XS0_XS_IDLE)
	    && (status != D64_XS0_XS_STOPPED), 10000);

  W_REG (di->osh, &di->d64txregs->control, 0);
  SPINWAIT (((status =
	      (R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_XS_MASK)) !=
	     D64_XS0_XS_DISABLED), 10000);

  /* wait for the last transaction to complete */
  OSL_DELAY (300);

  return (status == D64_XS0_XS_DISABLED);
}

static bool
dma64_rxidle (dma_info_t * di)
{
  DMA_TRACE (("%s: dma_rxidle\n", di->name));

  if (di->nrxd == 0)
    return TRUE;

  return ((R_REG (di->osh, &di->d64rxregs->status0) & D64_RS0_CD_MASK) ==
	  R_REG (di->osh, &di->d64rxregs->ptr));
}

static bool
dma64_rxreset (dma_info_t * di)
{
  uint32 status;

  if (di->nrxd == 0)
    return TRUE;

  W_REG (di->osh, &di->d64rxregs->control, 0);
  SPINWAIT (((status =
	      (R_REG (di->osh, &di->d64rxregs->status0) & D64_RS0_RS_MASK)) !=
	     D64_RS0_RS_DISABLED), 10000);

  return (status == D64_RS0_RS_DISABLED);
}

static bool
dma64_rxenabled (dma_info_t * di)
{
  uint32 rc;

  rc = R_REG (di->osh, &di->d64rxregs->control);
  return ((rc != 0xffffffff) && (rc & D64_RC_RE));
}

static bool
dma64_txsuspendedidle (dma_info_t * di)
{

  if (di->ntxd == 0)
    return TRUE;

  if (!(R_REG (di->osh, &di->d64txregs->control) & D64_XC_SE))
    return 0;

  if ((R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_XS_MASK) ==
      D64_XS0_XS_IDLE)
    return 1;

  return 0;
}


/* !! tx entry routine */
static int
dma64_txfast (dma_info_t * di, void *p0, bool commit)
{
  void *p, *next;
  uchar *data;
  uint len;
  uint txout;
  uint32 flags = 0;
  uint32 pa;

  DMA_TRACE (("%s: dma_txfast\n", di->name));

  txout = di->txout;

  /*
   * Walk the chain of packet buffers
   * allocating and initializing transmit descriptor entries.
   */
  for (p = p0; p; p = next)
    {
      data = PKTDATA (di->osh, p);
      len = PKTLEN (di->osh, p);
      next = PKTNEXT (di->osh, p);

      /* return nonzero if out of tx descriptors */
      if (NEXTTXD (txout) == di->txin)
	goto outoftxd;

      if (len == 0)
	continue;

      /* get physical address of buffer start */
      pa =
	(uint32) DMA_MAP (di->osh, data, len, DMA_TX, p,
			  &di->txp_dmah[txout]);

      flags = 0;
      if (p == p0)
	flags |= D64_CTRL1_SOF;
      if (next == NULL)
	flags |= (D64_CTRL1_IOC | D64_CTRL1_EOF);
      if (txout == (di->ntxd - 1))
	flags |= D64_CTRL1_EOT;

      dma64_dd_upd (di, di->txd64, pa, txout, &flags, len);
      ASSERT (di->txp[txout] == NULL);

      txout = NEXTTXD (txout);
    }

  /* if last txd eof not set, fix it */
  if (!(flags & D64_CTRL1_EOF))
    W_SM (&di->txd64[PREVTXD (txout)].ctrl1,
	  BUS_SWAP32 (flags | D64_CTRL1_IOC | D64_CTRL1_EOF));

  /* save the packet */
  di->txp[PREVTXD (txout)] = p0;

  /* bump the tx descriptor index */
  di->txout = txout;

  /* kick the chip */
  if (commit)
    W_REG (di->osh, &di->d64txregs->ptr, I2B (txout, dma64dd_t));

  /* tx flow control */
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  return (0);

outoftxd:
  DMA_ERROR (("%s: dma_txfast: out of txds\n", di->name));
  PKTFREE (di->osh, p0, TRUE);
  di->hnddma.txavail = 0;
  di->hnddma.txnobuf++;
  return (-1);
}

/*
 * Reclaim next completed txd (txds if using chained buffers) and
 * return associated packet.
 * If 'force' is true, reclaim txd(s) and return associated packet
 * regardless of the value of the hardware "curr" pointer.
 */
static void *
dma64_getnexttxp (dma_info_t * di, bool forceall)
{
  uint start, end, i;
  void *txp;

  DMA_TRACE (("%s: dma_getnexttxp %s\n", di->name, forceall ? "all" : ""));

  if (di->ntxd == 0)
    return (NULL);

  txp = NULL;

  start = di->txin;
  if (forceall)
    end = di->txout;
  else
    end =
      B2I (R_REG (di->osh, &di->d64txregs->status0) & D64_XS0_CD_MASK,
	   dma64dd_t);

  if ((start == 0) && (end > di->txout))
    goto bogus;

  for (i = start; i != end && !txp; i = NEXTTXD (i))
    {
      DMA_UNMAP (di->osh,
		 (BUS_SWAP32 (R_SM (&di->txd64[i].addrlow)) -
		  di->dataoffsetlow),
		 (BUS_SWAP32 (R_SM (&di->txd64[i].ctrl2)) &
		  D64_CTRL2_BC_MASK), DMA_TX, di->txp[i], &di->txp_dmah[i]);

      W_SM (&di->txd64[i].addrlow, 0xdeadbeef);
      W_SM (&di->txd64[i].addrhigh, 0xdeadbeef);

      txp = di->txp[i];
      di->txp[i] = NULL;
    }

  di->txin = i;

  /* tx flow control */
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  return (txp);

bogus:
/*
	DMA_ERROR(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n",
		start, end, di->txout, forceall));
*/
  return (NULL);
}

static void *
dma64_getnextrxp (dma_info_t * di, bool forceall)
{
  uint i;
  void *rxp;

  /* if forcing, dma engine must be disabled */
  ASSERT (!forceall || !dma64_rxenabled (di));

  i = di->rxin;

  /* return if no packets posted */
  if (i == di->rxout)
    return (NULL);

  /* ignore curr if forceall */
  if (!forceall &&
      (i ==
       B2I (R_REG (di->osh, &di->d64rxregs->status0) & D64_RS0_CD_MASK,
	    dma64dd_t)))
    return (NULL);

  /* get the packet pointer that corresponds to the rx descriptor */
  rxp = di->rxp[i];
  ASSERT (rxp);
  di->rxp[i] = NULL;

  /* clear this packet from the descriptor ring */
  DMA_UNMAP (di->osh,
	     (BUS_SWAP32 (R_SM (&di->rxd64[i].addrlow)) - di->dataoffsetlow),
	     di->rxbufsize, DMA_RX, rxp, &di->rxp_dmah[i]);

  W_SM (&di->rxd64[i].addrlow, 0xdeadbeef);
  W_SM (&di->rxd64[i].addrhigh, 0xdeadbeef);

  di->rxin = NEXTRXD (i);

  return (rxp);
}

static bool
_dma64_addrext (osl_t * osh, dma64regs_t * dma64regs)
{
  uint32 w;
  OR_REG (osh, &dma64regs->control, D64_XC_AE);
  w = R_REG (osh, &dma64regs->control);
  AND_REG (osh, &dma64regs->control, ~D64_XC_AE);
  return ((w & D64_XC_AE) == D64_XC_AE);
}

/*
 * Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
 */
static void
dma64_txrotate (dma_info_t * di)
{
  uint ad;
  uint nactive;
  uint rot;
  uint old, new;
  uint32 w;
  uint first, last;

  ASSERT (dma64_txsuspendedidle (di));

  nactive = _dma_txactive (di);
  ad =
    B2I ((R_REG (di->osh, &di->d64txregs->status1) & D64_XS1_AD_MASK),
	 dma64dd_t);
  rot = TXD (ad - di->txin);

  ASSERT (rot < di->ntxd);

  /* full-ring case is a lot harder - don't worry about this */
  if (rot >= (di->ntxd - nactive))
    {
      DMA_ERROR (("%s: dma_txrotate: ring full - punt\n", di->name));
      return;
    }

  first = di->txin;
  last = PREVTXD (di->txout);

  /* move entries starting at last and moving backwards to first */
  for (old = last; old != PREVTXD (first); old = PREVTXD (old))
    {
      new = TXD (old + rot);

      /*
       * Move the tx dma descriptor.
       * EOT is set only in the last entry in the ring.
       */
      w = BUS_SWAP32 (R_SM (&di->txd64[old].ctrl1)) & ~D64_CTRL1_EOT;
      if (new == (di->ntxd - 1))
	w |= D64_CTRL1_EOT;
      W_SM (&di->txd64[new].ctrl1, BUS_SWAP32 (w));

      w = BUS_SWAP32 (R_SM (&di->txd64[old].ctrl2));
      W_SM (&di->txd64[new].ctrl2, BUS_SWAP32 (w));

      W_SM (&di->txd64[new].addrlow, R_SM (&di->txd64[old].addrlow));
      W_SM (&di->txd64[new].addrhigh, R_SM (&di->txd64[old].addrhigh));

      /* zap the old tx dma descriptor address field */
      W_SM (&di->txd64[old].addrlow, BUS_SWAP32 (0xdeadbeef));
      W_SM (&di->txd64[old].addrhigh, BUS_SWAP32 (0xdeadbeef));

      /* move the corresponding txp[] entry */
      ASSERT (di->txp[new] == NULL);
      di->txp[new] = di->txp[old];
      di->txp[old] = NULL;
    }

  /* update txin and txout */
  di->txin = ad;
  di->txout = TXD (di->txout + rot);
  di->hnddma.txavail = di->ntxd - NTXDACTIVE (di->txin, di->txout) - 1;

  /* kick the chip */
  W_REG (di->osh, &di->d64txregs->ptr, I2B (di->txout, dma64dd_t));
}

#endif /* BCMDMA64 */

uint
dma_addrwidth (sb_t * sbh, void *dmaregs)
{
  dma32regs_t *dma32regs;
  osl_t *osh;

  osh = sb_osh (sbh);

  if (DMA64_CAP)
    {
      /* DMA engine is 64-bit capable */
      if (((sb_coreflagshi (sbh, 0, 0) & SBTMH_DMA64) == SBTMH_DMA64))
	{
	  /* backplane are 64 bits capable */
	  if (sb_backplane64 (sbh))
	    /* If bus is System Backplane or PCIE then we can access 64-bits */
	    if ((BUSTYPE (sbh->bustype) == SB_BUS) ||
		((BUSTYPE (sbh->bustype) == PCI_BUS) &&
		 sbh->buscoretype == SB_PCIE))
	      return (DMADDRWIDTH_64);

	  /* DMA64 is always 32 bits capable, AE is always TRUE */
#ifdef BCMDMA64
	  ASSERT (_dma64_addrext (osh, (dma64regs_t *) dmaregs));
#endif
	  return (DMADDRWIDTH_32);
	}
    }

  /* Start checking for 32-bit / 30-bit addressing */
  dma32regs = (dma32regs_t *) dmaregs;

  /* For System Backplane, PCIE bus or addrext feature, 32-bits ok */
  if ((BUSTYPE (sbh->bustype) == SB_BUS) ||
      ((BUSTYPE (sbh->bustype) == PCI_BUS) && sbh->buscoretype == SB_PCIE) ||
      (_dma32_addrext (osh, dma32regs)))
    return (DMADDRWIDTH_32);

  /* Fallthru */
  return (DMADDRWIDTH_30);
}
