diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2006-12-07 21:42:03 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2006-12-07 21:42:03 +0000 | 
| commit | 77fde4490cbe96d74fbe79cee1a84db43bfc998d (patch) | |
| tree | 0a61df54111a9a0245fdba2346850f7d09a0e876 /package/broadcom-wl/src/kmod | |
| parent | fe2127621f7775b35dfa135a91d50784ae726941 (diff) | |
add 64 bit dma support to broadcom-wl, fixes wrt300n wifi support
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@5717 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/broadcom-wl/src/kmod')
| -rw-r--r-- | package/broadcom-wl/src/kmod/Makefile | 2 | ||||
| -rw-r--r-- | package/broadcom-wl/src/kmod/hnddma.c | 840 | 
2 files changed, 789 insertions, 53 deletions
| diff --git a/package/broadcom-wl/src/kmod/Makefile b/package/broadcom-wl/src/kmod/Makefile index 450700f5c..0a16bdcec 100644 --- a/package/broadcom-wl/src/kmod/Makefile +++ b/package/broadcom-wl/src/kmod/Makefile @@ -11,7 +11,7 @@  #  # $Id: Makefile,v 1.2 2005/03/29 03:32:18 mbm Exp $ -EXTRA_CFLAGS += -I$(TOPDIR)/arch/mips/bcm947xx/include -DBCMDRIVER +EXTRA_CFLAGS += -I$(TOPDIR)/arch/mips/bcm947xx/include -DBCMDRIVER=1 -DBCMDMA64=1  O_TARGET	:= wl$(MOD_NAME).o diff --git a/package/broadcom-wl/src/kmod/hnddma.c b/package/broadcom-wl/src/kmod/hnddma.c index 433656088..1b79dff4c 100644 --- a/package/broadcom-wl/src/kmod/hnddma.c +++ b/package/broadcom-wl/src/kmod/hnddma.c @@ -94,6 +94,14 @@ typedef struct dma_info {  	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) @@ -114,7 +122,7 @@ typedef struct dma_info {  /* common prototypes */  static bool _dma_isaddrext(dma_info_t *di); -static bool dma32_alloc(dma_info_t *di, uint direction); +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); @@ -129,7 +137,7 @@ 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, char *name); +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); @@ -154,6 +162,94 @@ 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 */ + + + +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, + +	NULL, +	NULL, +	NULL, +	34 +};  static di_fcn_t dma32proc = {  	(di_detach_t)_dma_detach, @@ -212,6 +308,16 @@ dma_attach(osl_t *osh, char *name, sb_t *sbh, void *dmaregstx, void *dmaregsrx,  	/* 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)); @@ -223,14 +329,27 @@ dma_attach(osl_t *osh, char *name, sb_t *sbh, void *dmaregstx, void *dmaregsrx,  	/* init dma reg pointer */ -	ASSERT(ntxd <= D32MAXDD); -	ASSERT(nrxd <= D32MAXDD); -	di->d32txregs = (dma32regs_t *)dmaregstx; -	di->d32rxregs = (dma32regs_t *)dmaregsrx; +	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, "DMA32", osh, ntxd, nrxd, rxbufsize, +	           name, (di->dma64 ? "DMA64" : "DMA32"), osh, ntxd, nrxd, rxbufsize,  	           nrxpost, rxoffset, dmaregstx, dmaregsrx));  	/* make a private copy of our callers name */ @@ -265,8 +384,15 @@ dma_attach(osl_t *osh, char *name, sb_t *sbh, void *dmaregstx, void *dmaregsrx,  	di->dataoffsetlow = 0;  	/* for pci bus, add offset */  	if (sbh->bustype == PCI_BUS) { -		di->ddoffsetlow = SB_PCI_DMA; -		di->ddoffsethigh = 0; +		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;  	} @@ -301,13 +427,13 @@ dma_attach(osl_t *osh, char *name, sb_t *sbh, void *dmaregstx, void *dmaregsrx,  	/* allocate transmit descriptor ring, only need ntxd descriptors but it must be aligned */  	if (ntxd) { -		if (!dma32_alloc(di, DMA_TX)) +		if (!_dma_alloc(di, DMA_TX))  			goto fail;  	}  	/* allocate receive descriptor ring, only need nrxd descriptors but it must be aligned */  	if (nrxd) { -		if (!dma32_alloc(di, DMA_RX)) +		if (!_dma_alloc(di, DMA_RX))  			goto fail;  	} @@ -348,7 +474,7 @@ dma_attach(osl_t *osh, char *name, sb_t *sbh, void *dmaregstx, void *dmaregsrx,  		di->rxp_dmah = NULL;  	/* initialize opsvec of function pointers */ -	di->hnddma.di_fn = dma32proc; +	di->hnddma.di_fn = DMA64_ENAB(di) ? dma64proc : dma32proc;  	return ((hnddma_t *)di); @@ -381,6 +507,34 @@ dma32_dd_upd(dma_info_t *di, dma32dd_t *ddring, ulong pa, uint outidx, uint32 *f  	}  } +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)  { @@ -392,6 +546,16 @@ _dma32_addrext(osl_t *osh, dma32regs_t *dma32regs)  	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) @@ -406,12 +570,21 @@ _dma_detach(dma_info_t *di)  	ASSERT(di->rxin == di->rxout);  	/* free dma descriptor rings */ -	if (di->txd32) -		DMA_FREE_CONSISTENT(di->osh, ((int8*)di->txd32 - di->txdalign), -		                    di->txdalloc, (di->txdpa - di->txdalign), &di->tx_dmah); -	if (di->rxd32) -		DMA_FREE_CONSISTENT(di->osh, ((int8*)di->rxd32 - di->rxdalign), -		                    di->rxdalloc, (di->rxdpa - di->rxdalign), &di->rx_dmah); +	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) @@ -436,7 +609,27 @@ _dma_detach(dma_info_t *di)  static bool  _dma_isaddrext(dma_info_t *di)  { -	if (di->d32txregs) +	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)); @@ -447,26 +640,60 @@ _dma_isaddrext(dma_info_t *di)  static void  _dma_ddtable_init(dma_info_t *di, uint direction, ulong pa)  { -	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 (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)); +			} +		} -		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 { +		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 { -			W_REG(di->osh, &di->d32rxregs->addr, (pa + di->ddoffsetlow)); -			SET_REG(di->osh, &di->d32rxregs->control, RC_AE, ae <<RC_AE_SHIFT); +			/* 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); +			}  		}  	}  } @@ -475,7 +702,10 @@ static void  _dma_fifoloopbackenable(dma_info_t *di)  {  	DMA_TRACE(("%s: dma_fifoloopbackenable\n", di->name)); -	OR_REG(di->osh, &di->d32txregs->control, XC_LE); +	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 @@ -489,9 +719,15 @@ _dma_rxinit(dma_info_t *di)  	di->rxin = di->rxout = 0;  	/* clear rx descriptor ring */ -	BZERO_SM((void *)di->rxd32, (di->nrxd * sizeof(dma32dd_t))); -	_dma_rxenable(di); -	_dma_ddtable_init(di, DMA_RX, di->rxdpa); +	if (DMA64_ENAB(di)) { +		BZERO_SM((void *)(uintptr)di->rxd64, (di->nrxd * sizeof(dma64dd_t))); +		_dma_rxenable(di); +		_dma_ddtable_init(di, DMA_RX, di->rxdpa); +	} 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 @@ -499,7 +735,11 @@ _dma_rxenable(dma_info_t *di)  {  	DMA_TRACE(("%s: dma_rxenable\n", di->name)); -	W_REG(di->osh, &di->d32rxregs->control, ((di->rxoffset << RC_RO_SHIFT) | RC_RE)); +	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, @@ -602,16 +842,28 @@ _dma_rxfill(dma_info_t *di)  		/* reset flags for each descriptor */  		flags = 0; -		if (rxout == (di->nrxd - 1)) -			flags = CTRL_EOT; -		dma32_dd_upd(di, di->rxd32, pa, rxout, &flags, di->rxbufsize); +		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 */ -	W_REG(di->osh, &di->d32rxregs->ptr, I2B(rxout, dma32dd_t)); +	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 */ @@ -623,7 +875,11 @@ _dma_peeknexttxp(dma_info_t *di)  	if (di->ntxd == 0)  		return (NULL); -	end = B2I(R_REG(di->osh, &di->d32txregs->status) & XS_CD_MASK, dma32dd_t); +	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]) @@ -654,7 +910,11 @@ _dma_getnextrxp(dma_info_t *di, bool forceall)  	if (di->nrxd == 0)  		return (NULL); -	return dma32_getnextrxp(di, forceall); +	if (DMA64_ENAB(di)) { +		return dma64_getnextrxp(di, forceall); +	} else { +		return dma32_getnextrxp(di, forceall); +	}  }  static void @@ -686,7 +946,7 @@ _dma_counterreset(dma_info_t *di)  /* get the address of the var in order to change later */  static uintptr -_dma_getvar(dma_info_t *di, char *name) +_dma_getvar(dma_info_t *di, const char *name)  {  	if (!strcmp(name, "&txavail"))  		return ((uintptr) &(di->hnddma.txavail)); @@ -717,7 +977,7 @@ dma32_txinit(dma_info_t *di)  	di->hnddma.txavail = di->ntxd - 1;  	/* clear tx descriptor ring */ -	BZERO_SM((void *)di->txd32, (di->ntxd * sizeof(dma32dd_t))); +	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);  } @@ -806,7 +1066,7 @@ dma32_alloc(dma_info_t *di, uint direction)  		}  		di->txd32 = (dma32dd_t *) ROUNDUP((uintptr)va, D32RINGALIGN); -		di->txdalign = (uint)((int8*)di->txd32 - (int8*)va); +		di->txdalign = (uint)((int8*)(uintptr)di->txd32 - (int8*)va);  		di->txdpa += di->txdalign;  		di->txdalloc = size;  		ASSERT(ISALIGNED((uintptr)di->txd32, D32RINGALIGN)); @@ -817,7 +1077,7 @@ dma32_alloc(dma_info_t *di, uint direction)  			return FALSE;  		}  		di->rxd32 = (dma32dd_t *) ROUNDUP((uintptr)va, D32RINGALIGN); -		di->rxdalign = (uint)((int8*)di->rxd32 - (int8*)va); +		di->rxdalign = (uint)((int8*)(uintptr)di->rxd32 - (int8*)va);  		di->rxdpa += di->rxdalign;  		di->rxdalloc = size;  		ASSERT(ISALIGNED((uintptr)di->rxd32, D32RINGALIGN)); @@ -1134,6 +1394,461 @@ dma32_txrotate(dma_info_t *di)  	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); + +		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]); + +		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); + +	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) @@ -1143,6 +1858,27 @@ dma_addrwidth(sb_t *sbh, void *dmaregs)  	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 0 +			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); +#endif + +			/* 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; | 
