mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-02-03 05:41:05 +02:00
796a9d1091
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15242 3c298f89-4303-0410-b956-a3cf2f4a3e73
2281 lines
56 KiB
C
2281 lines
56 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#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, ®s->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);
|
|
}
|