svn commit: r219792 - head/sys/dev/ath/ath_hal/ar5416

Adrian Chadd adrian at FreeBSD.org
Sun Mar 20 08:42:57 UTC 2011


Author: adrian
Date: Sun Mar 20 08:42:56 2011
New Revision: 219792
URL: http://svn.freebsd.org/changeset/base/219792

Log:
  Bring over a copy of the AR5212 TX queue reset and setup routines, in preparation
  for fixing them based on the ath9k related TXQ fixes.
  
  I've done this so people can go over the history of the diffs to the original
  AR5212 routines (which AR5416 and later chips use) to see what's changed.

Modified:
  head/sys/dev/ath/ath_hal/ar5416/ar5416.h
  head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
  head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c

Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416.h
==============================================================================
--- head/sys/dev/ath/ath_hal/ar5416/ar5416.h	Sun Mar 20 08:35:00 2011	(r219791)
+++ head/sys/dev/ath/ath_hal/ar5416/ar5416.h	Sun Mar 20 08:42:56 2011	(r219792)
@@ -275,6 +275,10 @@ extern	HAL_STATUS ar5416ProcTxDesc(struc
 extern	HAL_BOOL ar5416GetTxCompletionRates(struct ath_hal *ah,
 		const struct ath_desc *ds0, int *rates, int *tries);
 
+extern	HAL_BOOL ar5416ResetTxQueue(struct ath_hal *ah, u_int q);
+extern	int ar5416SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type,
+	        const HAL_TXQ_INFO *qInfo);
+
 extern	HAL_BOOL ar5416ChainTxDesc(struct ath_hal *ah, struct ath_desc *ds,
 		u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx,
 		HAL_CIPHER cipher, uint8_t delims, u_int segLen, HAL_BOOL firstSeg,

Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c
==============================================================================
--- head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Sun Mar 20 08:35:00 2011	(r219791)
+++ head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c	Sun Mar 20 08:42:56 2011	(r219792)
@@ -112,6 +112,8 @@ ar5416InitState(struct ath_hal_5416 *ahp
 	ah->ah_fillTxDesc		= ar5416FillTxDesc;
 	ah->ah_procTxDesc		= ar5416ProcTxDesc;
 	ah->ah_getTxCompletionRates	= ar5416GetTxCompletionRates;
+	ah->ah_setupTxQueue		= ar5416SetupTxQueue;
+	ah->ah_resetTxQueue		= ar5416ResetTxQueue;
 
 	/* Receive Functions */
 	ah->ah_startPcuReceive		= ar5416StartPcuReceive;

Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c
==============================================================================
--- head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c	Sun Mar 20 08:35:00 2011	(r219791)
+++ head/sys/dev/ath/ath_hal/ar5416/ar5416_xmit.c	Sun Mar 20 08:42:56 2011	(r219792)
@@ -740,3 +740,341 @@ ar5416GetTxCompletionRates(struct ath_ha
 	return AH_TRUE;
 }
 
+
+/*
+ * TX queue management routines - AR5416 and later chipsets
+ */
+
+/*
+ * Allocate and initialize a tx DCU/QCU combination.
+ */
+int
+ar5416SetupTxQueue(struct ath_hal *ah, HAL_TX_QUEUE type,
+	const HAL_TXQ_INFO *qInfo)
+{
+	struct ath_hal_5212 *ahp = AH5212(ah);
+	HAL_TX_QUEUE_INFO *qi;
+	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
+	int q, defqflags;
+
+	/* by default enable OK+ERR+DESC+URN interrupts */
+	defqflags = HAL_TXQ_TXOKINT_ENABLE
+		  | HAL_TXQ_TXERRINT_ENABLE
+		  | HAL_TXQ_TXDESCINT_ENABLE
+		  | HAL_TXQ_TXURNINT_ENABLE;
+	/* XXX move queue assignment to driver */
+	switch (type) {
+	case HAL_TX_QUEUE_BEACON:
+		q = pCap->halTotalQueues-1;	/* highest priority */
+		defqflags |= HAL_TXQ_DBA_GATED
+		       | HAL_TXQ_CBR_DIS_QEMPTY
+		       | HAL_TXQ_ARB_LOCKOUT_GLOBAL
+		       | HAL_TXQ_BACKOFF_DISABLE;
+		break;
+	case HAL_TX_QUEUE_CAB:
+		q = pCap->halTotalQueues-2;	/* next highest priority */
+		defqflags |= HAL_TXQ_DBA_GATED
+		       | HAL_TXQ_CBR_DIS_QEMPTY
+		       | HAL_TXQ_CBR_DIS_BEMPTY
+		       | HAL_TXQ_ARB_LOCKOUT_GLOBAL
+		       | HAL_TXQ_BACKOFF_DISABLE;
+		break;
+	case HAL_TX_QUEUE_UAPSD:
+		q = pCap->halTotalQueues-3;	/* nextest highest priority */
+		if (ahp->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE) {
+			HALDEBUG(ah, HAL_DEBUG_ANY,
+			    "%s: no available UAPSD tx queue\n", __func__);
+			return -1;
+		}
+		break;
+	case HAL_TX_QUEUE_DATA:
+		for (q = 0; q < pCap->halTotalQueues; q++)
+			if (ahp->ah_txq[q].tqi_type == HAL_TX_QUEUE_INACTIVE)
+				break;
+		if (q == pCap->halTotalQueues) {
+			HALDEBUG(ah, HAL_DEBUG_ANY,
+			    "%s: no available tx queue\n", __func__);
+			return -1;
+		}
+		break;
+	default:
+		HALDEBUG(ah, HAL_DEBUG_ANY,
+		    "%s: bad tx queue type %u\n", __func__, type);
+		return -1;
+	}
+
+	HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: queue %u\n", __func__, q);
+
+	qi = &ahp->ah_txq[q];
+	if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) {
+		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: tx queue %u already active\n",
+		    __func__, q);
+		return -1;
+	}
+	OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO));
+	qi->tqi_type = type;
+	if (qInfo == AH_NULL) {
+		qi->tqi_qflags = defqflags;
+		qi->tqi_aifs = INIT_AIFS;
+		qi->tqi_cwmin = HAL_TXQ_USEDEFAULT;	/* NB: do at reset */
+		qi->tqi_cwmax = INIT_CWMAX;
+		qi->tqi_shretry = INIT_SH_RETRY;
+		qi->tqi_lgretry = INIT_LG_RETRY;
+		qi->tqi_physCompBuf = 0;
+	} else {
+		qi->tqi_physCompBuf = qInfo->tqi_compBuf;
+		(void) ar5212SetTxQueueProps(ah, q, qInfo);
+	}
+	/* NB: must be followed by ar5212ResetTxQueue */
+	return q;
+}
+
+/*
+ * Update the h/w interrupt registers to reflect a tx q's configuration.
+ */
+static void
+setTxQInterrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi)
+{
+	struct ath_hal_5212 *ahp = AH5212(ah);
+
+	HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
+	    "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", __func__,
+	    ahp->ah_txOkInterruptMask, ahp->ah_txErrInterruptMask,
+	    ahp->ah_txDescInterruptMask, ahp->ah_txEolInterruptMask,
+	    ahp->ah_txUrnInterruptMask);
+
+	OS_REG_WRITE(ah, AR_IMR_S0,
+		  SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK)
+		| SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC)
+	);
+	OS_REG_WRITE(ah, AR_IMR_S1,
+		  SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR)
+		| SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL)
+	);
+	OS_REG_RMW_FIELD(ah, AR_IMR_S2,
+		AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask);
+}
+
+/*
+ * Set the retry, aifs, cwmin/max, readyTime regs for specified queue
+ * Assumes:
+ *  phwChannel has been set to point to the current channel
+ */
+HAL_BOOL
+ar5416ResetTxQueue(struct ath_hal *ah, u_int q)
+{
+	struct ath_hal_5212 *ahp = AH5212(ah);
+	HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
+	const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
+	HAL_TX_QUEUE_INFO *qi;
+	uint32_t cwMin, chanCwMin, value, qmisc, dmisc;
+
+	if (q >= pCap->halTotalQueues) {
+		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid queue num %u\n",
+		    __func__, q);
+		return AH_FALSE;
+	}
+	qi = &ahp->ah_txq[q];
+	if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
+		HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: inactive queue %u\n",
+		    __func__, q);
+		return AH_TRUE;		/* XXX??? */
+	}
+
+	HALDEBUG(ah, HAL_DEBUG_TXQUEUE, "%s: reset queue %u\n", __func__, q);
+
+	if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) {
+		/*
+		 * Select cwmin according to channel type.
+		 * NB: chan can be NULL during attach
+		 */
+		if (chan && IEEE80211_IS_CHAN_B(chan))
+			chanCwMin = INIT_CWMIN_11B;
+		else
+			chanCwMin = INIT_CWMIN;
+		/* make sure that the CWmin is of the form (2^n - 1) */
+		for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1)
+			;
+	} else
+		cwMin = qi->tqi_cwmin;
+
+	/* set cwMin/Max and AIFS values */
+	OS_REG_WRITE(ah, AR_DLCL_IFS(q),
+		  SM(cwMin, AR_D_LCL_IFS_CWMIN)
+		| SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX)
+		| SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS));
+
+	/* Set retry limit values */
+	OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q), 
+		   SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH)
+		 | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG)
+		 | SM(qi->tqi_lgretry, AR_D_RETRY_LIMIT_FR_LG)
+		 | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH)
+	);
+
+	/* NB: always enable early termination on the QCU */
+	qmisc = AR_Q_MISC_DCU_EARLY_TERM_REQ
+	      | SM(AR_Q_MISC_FSP_ASAP, AR_Q_MISC_FSP);
+
+	/* NB: always enable DCU to wait for next fragment from QCU */
+	dmisc = AR_D_MISC_FRAG_WAIT_EN;
+
+#ifdef AH_SUPPORT_5311
+	if (AH_PRIVATE(ah)->ah_macVersion < AR_SREV_VERSION_OAHU) {
+		/* Configure DCU to use the global sequence count */
+		dmisc |= AR5311_D_MISC_SEQ_NUM_CONTROL;
+	}
+#endif
+	/* multiqueue support */
+	if (qi->tqi_cbrPeriod) {
+		OS_REG_WRITE(ah, AR_QCBRCFG(q), 
+			  SM(qi->tqi_cbrPeriod,AR_Q_CBRCFG_CBR_INTERVAL)
+			| SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_CBR_OVF_THRESH));
+		qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_CBR;
+		if (qi->tqi_cbrOverflowLimit)
+			qmisc |= AR_Q_MISC_CBR_EXP_CNTR_LIMIT;
+	}
+	if (qi->tqi_readyTime) {
+		OS_REG_WRITE(ah, AR_QRDYTIMECFG(q),
+			  SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_INT)
+			| AR_Q_RDYTIMECFG_ENA);
+	}
+	
+	OS_REG_WRITE(ah, AR_DCHNTIME(q),
+		  SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR)
+		| (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0));
+
+	if (qi->tqi_readyTime &&
+	    (qi->tqi_qflags & HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE))
+		qmisc |= AR_Q_MISC_RDYTIME_EXP_POLICY;
+	if (qi->tqi_qflags & HAL_TXQ_DBA_GATED)
+		qmisc = (qmisc &~ AR_Q_MISC_FSP) | AR_Q_MISC_FSP_DBA_GATED;
+	if (MS(qmisc, AR_Q_MISC_FSP) != AR_Q_MISC_FSP_ASAP) {
+		/*
+		 * These are meangingful only when not scheduled asap.
+		 */
+		if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_BEMPTY)
+			qmisc |= AR_Q_MISC_CBR_INCR_DIS0;
+		else
+			qmisc &= ~AR_Q_MISC_CBR_INCR_DIS0;
+		if (qi->tqi_qflags & HAL_TXQ_CBR_DIS_QEMPTY)
+			qmisc |= AR_Q_MISC_CBR_INCR_DIS1;
+		else
+			qmisc &= ~AR_Q_MISC_CBR_INCR_DIS1;
+	}
+
+	if (qi->tqi_qflags & HAL_TXQ_BACKOFF_DISABLE)
+		dmisc |= AR_D_MISC_POST_FR_BKOFF_DIS;
+	if (qi->tqi_qflags & HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE)
+		dmisc |= AR_D_MISC_FRAG_BKOFF_EN;
+	if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_GLOBAL)
+		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
+			    AR_D_MISC_ARB_LOCKOUT_CNTRL);
+	else if (qi->tqi_qflags & HAL_TXQ_ARB_LOCKOUT_INTRA)
+		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR,
+			    AR_D_MISC_ARB_LOCKOUT_CNTRL);
+	if (qi->tqi_qflags & HAL_TXQ_IGNORE_VIRTCOL)
+		dmisc |= SM(AR_D_MISC_VIR_COL_HANDLING_IGNORE,
+			    AR_D_MISC_VIR_COL_HANDLING);
+	if (qi->tqi_qflags & HAL_TXQ_SEQNUM_INC_DIS)
+		dmisc |= AR_D_MISC_SEQ_NUM_INCR_DIS;
+
+	/*
+	 * Fillin type-dependent bits.  Most of this can be
+	 * removed by specifying the queue parameters in the
+	 * driver; it's here for backwards compatibility.
+	 */
+	switch (qi->tqi_type) {
+	case HAL_TX_QUEUE_BEACON:		/* beacon frames */
+		qmisc |= AR_Q_MISC_FSP_DBA_GATED
+		      |  AR_Q_MISC_BEACON_USE
+		      |  AR_Q_MISC_CBR_INCR_DIS1;
+
+		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
+			    AR_D_MISC_ARB_LOCKOUT_CNTRL)
+		      |  AR_D_MISC_BEACON_USE
+		      |  AR_D_MISC_POST_FR_BKOFF_DIS;
+		break;
+	case HAL_TX_QUEUE_CAB:			/* CAB  frames */
+		/* 
+		 * No longer Enable AR_Q_MISC_RDYTIME_EXP_POLICY,
+		 * There is an issue with the CAB Queue
+		 * not properly refreshing the Tx descriptor if
+		 * the TXE clear setting is used.
+		 */
+		qmisc |= AR_Q_MISC_FSP_DBA_GATED
+		      |  AR_Q_MISC_CBR_INCR_DIS1
+		      |  AR_Q_MISC_CBR_INCR_DIS0;
+
+		if (!qi->tqi_readyTime) {
+			/*
+			 * NB: don't set default ready time if driver
+			 * has explicitly specified something.  This is
+			 * here solely for backwards compatibility.
+			 */
+			value = (ahp->ah_beaconInterval
+				- (ath_hal_sw_beacon_response_time -
+					ath_hal_dma_beacon_response_time)
+				- ath_hal_additional_swba_backoff) * 1024;
+			OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_ENA);
+		}
+		dmisc |= SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
+			    AR_D_MISC_ARB_LOCKOUT_CNTRL);
+		break;
+	default:			/* NB: silence compiler */
+		break;
+	}
+
+	OS_REG_WRITE(ah, AR_QMISC(q), qmisc);
+	OS_REG_WRITE(ah, AR_DMISC(q), dmisc);
+
+	/* Setup compression scratchpad buffer */
+	/* 
+	 * XXX: calling this asynchronously to queue operation can
+	 *      cause unexpected behavior!!!
+	 */
+	if (qi->tqi_physCompBuf) {
+		HALASSERT(qi->tqi_type == HAL_TX_QUEUE_DATA ||
+			  qi->tqi_type == HAL_TX_QUEUE_UAPSD);
+		OS_REG_WRITE(ah, AR_Q_CBBS, (80 + 2*q));
+		OS_REG_WRITE(ah, AR_Q_CBBA, qi->tqi_physCompBuf);
+		OS_REG_WRITE(ah, AR_Q_CBC,  HAL_COMP_BUF_MAX_SIZE/1024);
+		OS_REG_WRITE(ah, AR_Q0_MISC + 4*q,
+			     OS_REG_READ(ah, AR_Q0_MISC + 4*q)
+			     | AR_Q_MISC_QCU_COMP_EN);
+	}
+	
+	/*
+	 * Always update the secondary interrupt mask registers - this
+	 * could be a new queue getting enabled in a running system or
+	 * hw getting re-initialized during a reset!
+	 *
+	 * Since we don't differentiate between tx interrupts corresponding
+	 * to individual queues - secondary tx mask regs are always unmasked;
+	 * tx interrupts are enabled/disabled for all queues collectively
+	 * using the primary mask reg
+	 */
+	if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE)
+		ahp->ah_txOkInterruptMask |= 1 << q;
+	else
+		ahp->ah_txOkInterruptMask &= ~(1 << q);
+	if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE)
+		ahp->ah_txErrInterruptMask |= 1 << q;
+	else
+		ahp->ah_txErrInterruptMask &= ~(1 << q);
+	if (qi->tqi_qflags & HAL_TXQ_TXDESCINT_ENABLE)
+		ahp->ah_txDescInterruptMask |= 1 << q;
+	else
+		ahp->ah_txDescInterruptMask &= ~(1 << q);
+	if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE)
+		ahp->ah_txEolInterruptMask |= 1 << q;
+	else
+		ahp->ah_txEolInterruptMask &= ~(1 << q);
+	if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE)
+		ahp->ah_txUrnInterruptMask |= 1 << q;
+	else
+		ahp->ah_txUrnInterruptMask &= ~(1 << q);
+	setTxQInterrupts(ah, qi);
+
+	return AH_TRUE;
+}


More information about the svn-src-head mailing list