|
@@ -183,226 +183,25 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-void ar9002_hw_attach_mac_ops(struct ath_hw *ah)
|
|
|
-{
|
|
|
- struct ath_hw_ops *ops = ath9k_hw_ops(ah);
|
|
|
-
|
|
|
- ops->rx_enable = ar9002_hw_rx_enable;
|
|
|
- ops->set_desc_link = ar9002_hw_set_desc_link;
|
|
|
- ops->get_desc_link = ar9002_hw_get_desc_link;
|
|
|
- ops->get_isr = ar9002_hw_get_isr;
|
|
|
-}
|
|
|
-
|
|
|
-static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
|
|
|
- struct ath9k_tx_queue_info *qi)
|
|
|
-{
|
|
|
- ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT,
|
|
|
- "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
|
|
|
- ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
|
|
|
- ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
|
|
|
- ah->txurn_interrupt_mask);
|
|
|
-
|
|
|
- REG_WRITE(ah, AR_IMR_S0,
|
|
|
- SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK)
|
|
|
- | SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC));
|
|
|
- REG_WRITE(ah, AR_IMR_S1,
|
|
|
- SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR)
|
|
|
- | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL));
|
|
|
-
|
|
|
- ah->imrs2_reg &= ~AR_IMR_S2_QCU_TXURN;
|
|
|
- ah->imrs2_reg |= (ah->txurn_interrupt_mask & AR_IMR_S2_QCU_TXURN);
|
|
|
- REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
|
|
|
-}
|
|
|
-
|
|
|
-u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)
|
|
|
-{
|
|
|
- return REG_READ(ah, AR_QTXDP(q));
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_gettxbuf);
|
|
|
-
|
|
|
-void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
|
|
|
-{
|
|
|
- REG_WRITE(ah, AR_QTXDP(q), txdp);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_puttxbuf);
|
|
|
-
|
|
|
-void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
|
|
|
-{
|
|
|
- ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE,
|
|
|
- "Enable TXE on queue: %u\n", q);
|
|
|
- REG_WRITE(ah, AR_Q_TXE, 1 << q);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_txstart);
|
|
|
-
|
|
|
-u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
|
|
|
-{
|
|
|
- u32 npend;
|
|
|
-
|
|
|
- npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
|
|
|
- if (npend == 0) {
|
|
|
-
|
|
|
- if (REG_READ(ah, AR_Q_TXE) & (1 << q))
|
|
|
- npend = 1;
|
|
|
- }
|
|
|
-
|
|
|
- return npend;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_numtxpending);
|
|
|
-
|
|
|
-/**
|
|
|
- * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level
|
|
|
- *
|
|
|
- * @ah: atheros hardware struct
|
|
|
- * @bIncTrigLevel: whether or not the frame trigger level should be updated
|
|
|
- *
|
|
|
- * The frame trigger level specifies the minimum number of bytes,
|
|
|
- * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO
|
|
|
- * before the PCU will initiate sending the frame on the air. This can
|
|
|
- * mean we initiate transmit before a full frame is on the PCU TX FIFO.
|
|
|
- * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs
|
|
|
- * first)
|
|
|
- *
|
|
|
- * Caution must be taken to ensure to set the frame trigger level based
|
|
|
- * on the DMA request size. For example if the DMA request size is set to
|
|
|
- * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because
|
|
|
- * there need to be enough space in the tx FIFO for the requested transfer
|
|
|
- * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set
|
|
|
- * the threshold to a value beyond 6, then the transmit will hang.
|
|
|
- *
|
|
|
- * Current dual stream devices have a PCU TX FIFO size of 8 KB.
|
|
|
- * Current single stream devices have a PCU TX FIFO size of 4 KB, however,
|
|
|
- * there is a hardware issue which forces us to use 2 KB instead so the
|
|
|
- * frame trigger level must not exceed 2 KB for these chipsets.
|
|
|
- */
|
|
|
-bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
|
|
|
-{
|
|
|
- u32 txcfg, curLevel, newLevel;
|
|
|
- enum ath9k_int omask;
|
|
|
-
|
|
|
- if (ah->tx_trig_level >= ah->config.max_txtrig_level)
|
|
|
- return false;
|
|
|
-
|
|
|
- omask = ath9k_hw_set_interrupts(ah, ah->imask & ~ATH9K_INT_GLOBAL);
|
|
|
-
|
|
|
- txcfg = REG_READ(ah, AR_TXCFG);
|
|
|
- curLevel = MS(txcfg, AR_FTRIG);
|
|
|
- newLevel = curLevel;
|
|
|
- if (bIncTrigLevel) {
|
|
|
- if (curLevel < ah->config.max_txtrig_level)
|
|
|
- newLevel++;
|
|
|
- } else if (curLevel > MIN_TX_FIFO_THRESHOLD)
|
|
|
- newLevel--;
|
|
|
- if (newLevel != curLevel)
|
|
|
- REG_WRITE(ah, AR_TXCFG,
|
|
|
- (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
|
|
|
-
|
|
|
- ath9k_hw_set_interrupts(ah, omask);
|
|
|
-
|
|
|
- ah->tx_trig_level = newLevel;
|
|
|
-
|
|
|
- return newLevel != curLevel;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
|
|
|
-
|
|
|
-bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
|
|
|
-{
|
|
|
-#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */
|
|
|
-#define ATH9K_TIME_QUANTUM 100 /* usec */
|
|
|
- struct ath_common *common = ath9k_hw_common(ah);
|
|
|
- struct ath9k_hw_capabilities *pCap = &ah->caps;
|
|
|
- struct ath9k_tx_queue_info *qi;
|
|
|
- u32 tsfLow, j, wait;
|
|
|
- u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
|
|
|
-
|
|
|
- if (q >= pCap->total_queues) {
|
|
|
- ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
|
|
|
- "invalid queue: %u\n", q);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- qi = &ah->txq[q];
|
|
|
- if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
|
|
|
- ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
|
|
|
- "inactive queue: %u\n", q);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- REG_WRITE(ah, AR_Q_TXD, 1 << q);
|
|
|
-
|
|
|
- for (wait = wait_time; wait != 0; wait--) {
|
|
|
- if (ath9k_hw_numtxpending(ah, q) == 0)
|
|
|
- break;
|
|
|
- udelay(ATH9K_TIME_QUANTUM);
|
|
|
- }
|
|
|
-
|
|
|
- if (ath9k_hw_numtxpending(ah, q)) {
|
|
|
- ath_print(common, ATH_DBG_QUEUE,
|
|
|
- "%s: Num of pending TX Frames %d on Q %d\n",
|
|
|
- __func__, ath9k_hw_numtxpending(ah, q), q);
|
|
|
-
|
|
|
- for (j = 0; j < 2; j++) {
|
|
|
- tsfLow = REG_READ(ah, AR_TSF_L32);
|
|
|
- REG_WRITE(ah, AR_QUIET2,
|
|
|
- SM(10, AR_QUIET2_QUIET_DUR));
|
|
|
- REG_WRITE(ah, AR_QUIET_PERIOD, 100);
|
|
|
- REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
|
|
|
- REG_SET_BIT(ah, AR_TIMER_MODE,
|
|
|
- AR_QUIET_TIMER_EN);
|
|
|
-
|
|
|
- if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
|
|
|
- break;
|
|
|
-
|
|
|
- ath_print(common, ATH_DBG_QUEUE,
|
|
|
- "TSF has moved while trying to set "
|
|
|
- "quiet time TSF: 0x%08x\n", tsfLow);
|
|
|
- }
|
|
|
-
|
|
|
- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
|
|
|
-
|
|
|
- udelay(200);
|
|
|
- REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
|
|
|
-
|
|
|
- wait = wait_time;
|
|
|
- while (ath9k_hw_numtxpending(ah, q)) {
|
|
|
- if ((--wait) == 0) {
|
|
|
- ath_print(common, ATH_DBG_FATAL,
|
|
|
- "Failed to stop TX DMA in 100 "
|
|
|
- "msec after killing last frame\n");
|
|
|
- break;
|
|
|
- }
|
|
|
- udelay(ATH9K_TIME_QUANTUM);
|
|
|
- }
|
|
|
-
|
|
|
- REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
|
|
|
- }
|
|
|
-
|
|
|
- REG_WRITE(ah, AR_Q_TXD, 0);
|
|
|
- return wait != 0;
|
|
|
-
|
|
|
-#undef ATH9K_TX_STOP_DMA_TIMEOUT
|
|
|
-#undef ATH9K_TIME_QUANTUM
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_stoptxdma);
|
|
|
-
|
|
|
-void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 segLen, bool firstSeg,
|
|
|
- bool lastSeg, const struct ath_desc *ds0,
|
|
|
- dma_addr_t buf_addr)
|
|
|
+static void ar9002_hw_fill_txdesc(struct ath_hw *ah, void *ds, u32 seglen,
|
|
|
+ bool is_firstseg, bool is_lastseg,
|
|
|
+ const void *ds0, dma_addr_t buf_addr,
|
|
|
+ unsigned int qcu)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
|
ads->ds_data = buf_addr;
|
|
|
|
|
|
- if (firstSeg) {
|
|
|
- ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore);
|
|
|
- } else if (lastSeg) {
|
|
|
+ if (is_firstseg) {
|
|
|
+ ads->ds_ctl1 |= seglen | (is_lastseg ? 0 : AR_TxMore);
|
|
|
+ } else if (is_lastseg) {
|
|
|
ads->ds_ctl0 = 0;
|
|
|
- ads->ds_ctl1 = segLen;
|
|
|
+ ads->ds_ctl1 = seglen;
|
|
|
ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
|
|
|
ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
|
|
|
} else {
|
|
|
ads->ds_ctl0 = 0;
|
|
|
- ads->ds_ctl1 = segLen | AR_TxMore;
|
|
|
+ ads->ds_ctl1 = seglen | AR_TxMore;
|
|
|
ads->ds_ctl2 = 0;
|
|
|
ads->ds_ctl3 = 0;
|
|
|
}
|
|
@@ -412,22 +211,9 @@ void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
|
|
|
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_filltxdesc);
|
|
|
|
|
|
-void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds)
|
|
|
-{
|
|
|
- struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
-
|
|
|
- ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
|
|
|
- ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
|
|
|
- ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
|
|
|
- ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
|
|
|
- ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_cleartxdesc);
|
|
|
-
|
|
|
-int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- struct ath_tx_status *ts)
|
|
|
+static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
|
|
|
+ struct ath_tx_status *ts)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
@@ -503,11 +289,11 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_txprocdesc);
|
|
|
|
|
|
-void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
|
|
|
- u32 keyIx, enum ath9k_key_type keyType, u32 flags)
|
|
|
+static void ar9002_hw_set11n_txdesc(struct ath_hw *ah, void *ds,
|
|
|
+ u32 pktLen, enum ath9k_pkt_type type,
|
|
|
+ u32 txPower, u32 keyIx,
|
|
|
+ enum ath9k_key_type keyType, u32 flags)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
@@ -539,14 +325,13 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
ads->ds_ctl11 = 0;
|
|
|
}
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_txdesc);
|
|
|
-
|
|
|
-void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- struct ath_desc *lastds,
|
|
|
- u32 durUpdateEn, u32 rtsctsRate,
|
|
|
- u32 rtsctsDuration,
|
|
|
- struct ath9k_11n_rate_series series[],
|
|
|
- u32 nseries, u32 flags)
|
|
|
+
|
|
|
+static void ar9002_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
|
|
|
+ void *lastds,
|
|
|
+ u32 durUpdateEn, u32 rtsctsRate,
|
|
|
+ u32 rtsctsDuration,
|
|
|
+ struct ath9k_11n_rate_series series[],
|
|
|
+ u32 nseries, u32 flags)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
struct ar5416_desc *last_ads = AR5416DESC(lastds);
|
|
@@ -595,10 +380,9 @@ void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
last_ads->ds_ctl2 = ads->ds_ctl2;
|
|
|
last_ads->ds_ctl3 = ads->ds_ctl3;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_ratescenario);
|
|
|
|
|
|
-void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 aggrLen)
|
|
|
+static void ar9002_hw_set11n_aggr_first(struct ath_hw *ah, void *ds,
|
|
|
+ u32 aggrLen)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
@@ -606,10 +390,9 @@ void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
ads->ds_ctl6 &= ~AR_AggrLen;
|
|
|
ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_aggr_first);
|
|
|
|
|
|
-void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 numDelims)
|
|
|
+static void ar9002_hw_set11n_aggr_middle(struct ath_hw *ah, void *ds,
|
|
|
+ u32 numDelims)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
unsigned int ctl6;
|
|
@@ -621,9 +404,8 @@ void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
ctl6 |= SM(numDelims, AR_PadDelim);
|
|
|
ads->ds_ctl6 = ctl6;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_aggr_middle);
|
|
|
|
|
|
-void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds)
|
|
|
+static void ar9002_hw_set11n_aggr_last(struct ath_hw *ah, void *ds)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
@@ -631,28 +413,25 @@ void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds)
|
|
|
ads->ds_ctl1 &= ~AR_MoreAggr;
|
|
|
ads->ds_ctl6 &= ~AR_PadDelim;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_aggr_last);
|
|
|
|
|
|
-void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds)
|
|
|
+static void ar9002_hw_clr11n_aggr(struct ath_hw *ah, void *ds)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
|
ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_clr11n_aggr);
|
|
|
|
|
|
-void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 burstDuration)
|
|
|
+static void ar9002_hw_set11n_burstduration(struct ath_hw *ah, void *ds,
|
|
|
+ u32 burstDuration)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
|
ads->ds_ctl2 &= ~AR_BurstDur;
|
|
|
ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
|
|
|
}
|
|
|
-EXPORT_SYMBOL(ath9k_hw_set11n_burstduration);
|
|
|
|
|
|
-void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
- u32 vmf)
|
|
|
+static void ar9002_hw_set11n_virtualmorefrag(struct ath_hw *ah, void *ds,
|
|
|
+ u32 vmf)
|
|
|
{
|
|
|
struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
|
|
@@ -662,6 +441,229 @@ void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds,
|
|
|
ads->ds_ctl0 &= ~AR_VirtMoreFrag;
|
|
|
}
|
|
|
|
|
|
+void ar9002_hw_attach_mac_ops(struct ath_hw *ah)
|
|
|
+{
|
|
|
+ struct ath_hw_ops *ops = ath9k_hw_ops(ah);
|
|
|
+
|
|
|
+ ops->rx_enable = ar9002_hw_rx_enable;
|
|
|
+ ops->set_desc_link = ar9002_hw_set_desc_link;
|
|
|
+ ops->get_desc_link = ar9002_hw_get_desc_link;
|
|
|
+ ops->get_isr = ar9002_hw_get_isr;
|
|
|
+ ops->fill_txdesc = ar9002_hw_fill_txdesc;
|
|
|
+ ops->proc_txdesc = ar9002_hw_proc_txdesc;
|
|
|
+ ops->set11n_txdesc = ar9002_hw_set11n_txdesc;
|
|
|
+ ops->set11n_ratescenario = ar9002_hw_set11n_ratescenario;
|
|
|
+ ops->set11n_aggr_first = ar9002_hw_set11n_aggr_first;
|
|
|
+ ops->set11n_aggr_middle = ar9002_hw_set11n_aggr_middle;
|
|
|
+ ops->set11n_aggr_last = ar9002_hw_set11n_aggr_last;
|
|
|
+ ops->clr11n_aggr = ar9002_hw_clr11n_aggr;
|
|
|
+ ops->set11n_burstduration = ar9002_hw_set11n_burstduration;
|
|
|
+ ops->set11n_virtualmorefrag = ar9002_hw_set11n_virtualmorefrag;
|
|
|
+}
|
|
|
+
|
|
|
+static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
|
|
|
+ struct ath9k_tx_queue_info *qi)
|
|
|
+{
|
|
|
+ ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT,
|
|
|
+ "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
|
|
|
+ ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
|
|
|
+ ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
|
|
|
+ ah->txurn_interrupt_mask);
|
|
|
+
|
|
|
+ REG_WRITE(ah, AR_IMR_S0,
|
|
|
+ SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK)
|
|
|
+ | SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC));
|
|
|
+ REG_WRITE(ah, AR_IMR_S1,
|
|
|
+ SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR)
|
|
|
+ | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL));
|
|
|
+
|
|
|
+ ah->imrs2_reg &= ~AR_IMR_S2_QCU_TXURN;
|
|
|
+ ah->imrs2_reg |= (ah->txurn_interrupt_mask & AR_IMR_S2_QCU_TXURN);
|
|
|
+ REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
|
|
|
+}
|
|
|
+
|
|
|
+u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)
|
|
|
+{
|
|
|
+ return REG_READ(ah, AR_QTXDP(q));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_gettxbuf);
|
|
|
+
|
|
|
+void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
|
|
|
+{
|
|
|
+ REG_WRITE(ah, AR_QTXDP(q), txdp);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_puttxbuf);
|
|
|
+
|
|
|
+void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
|
|
|
+{
|
|
|
+ ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE,
|
|
|
+ "Enable TXE on queue: %u\n", q);
|
|
|
+ REG_WRITE(ah, AR_Q_TXE, 1 << q);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_txstart);
|
|
|
+
|
|
|
+void ath9k_hw_cleartxdesc(struct ath_hw *ah, void *ds)
|
|
|
+{
|
|
|
+ struct ar5416_desc *ads = AR5416DESC(ds);
|
|
|
+
|
|
|
+ ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
|
|
|
+ ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
|
|
|
+ ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
|
|
|
+ ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
|
|
|
+ ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_cleartxdesc);
|
|
|
+
|
|
|
+u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
|
|
|
+{
|
|
|
+ u32 npend;
|
|
|
+
|
|
|
+ npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
|
|
|
+ if (npend == 0) {
|
|
|
+
|
|
|
+ if (REG_READ(ah, AR_Q_TXE) & (1 << q))
|
|
|
+ npend = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return npend;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_numtxpending);
|
|
|
+
|
|
|
+/**
|
|
|
+ * ath9k_hw_updatetxtriglevel - adjusts the frame trigger level
|
|
|
+ *
|
|
|
+ * @ah: atheros hardware struct
|
|
|
+ * @bIncTrigLevel: whether or not the frame trigger level should be updated
|
|
|
+ *
|
|
|
+ * The frame trigger level specifies the minimum number of bytes,
|
|
|
+ * in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO
|
|
|
+ * before the PCU will initiate sending the frame on the air. This can
|
|
|
+ * mean we initiate transmit before a full frame is on the PCU TX FIFO.
|
|
|
+ * Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs
|
|
|
+ * first)
|
|
|
+ *
|
|
|
+ * Caution must be taken to ensure to set the frame trigger level based
|
|
|
+ * on the DMA request size. For example if the DMA request size is set to
|
|
|
+ * 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because
|
|
|
+ * there need to be enough space in the tx FIFO for the requested transfer
|
|
|
+ * size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set
|
|
|
+ * the threshold to a value beyond 6, then the transmit will hang.
|
|
|
+ *
|
|
|
+ * Current dual stream devices have a PCU TX FIFO size of 8 KB.
|
|
|
+ * Current single stream devices have a PCU TX FIFO size of 4 KB, however,
|
|
|
+ * there is a hardware issue which forces us to use 2 KB instead so the
|
|
|
+ * frame trigger level must not exceed 2 KB for these chipsets.
|
|
|
+ */
|
|
|
+bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
|
|
|
+{
|
|
|
+ u32 txcfg, curLevel, newLevel;
|
|
|
+ enum ath9k_int omask;
|
|
|
+
|
|
|
+ if (ah->tx_trig_level >= ah->config.max_txtrig_level)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ omask = ath9k_hw_set_interrupts(ah, ah->imask & ~ATH9K_INT_GLOBAL);
|
|
|
+
|
|
|
+ txcfg = REG_READ(ah, AR_TXCFG);
|
|
|
+ curLevel = MS(txcfg, AR_FTRIG);
|
|
|
+ newLevel = curLevel;
|
|
|
+ if (bIncTrigLevel) {
|
|
|
+ if (curLevel < ah->config.max_txtrig_level)
|
|
|
+ newLevel++;
|
|
|
+ } else if (curLevel > MIN_TX_FIFO_THRESHOLD)
|
|
|
+ newLevel--;
|
|
|
+ if (newLevel != curLevel)
|
|
|
+ REG_WRITE(ah, AR_TXCFG,
|
|
|
+ (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
|
|
|
+
|
|
|
+ ath9k_hw_set_interrupts(ah, omask);
|
|
|
+
|
|
|
+ ah->tx_trig_level = newLevel;
|
|
|
+
|
|
|
+ return newLevel != curLevel;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
|
|
|
+
|
|
|
+bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
|
|
|
+{
|
|
|
+#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */
|
|
|
+#define ATH9K_TIME_QUANTUM 100 /* usec */
|
|
|
+ struct ath_common *common = ath9k_hw_common(ah);
|
|
|
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
|
|
|
+ struct ath9k_tx_queue_info *qi;
|
|
|
+ u32 tsfLow, j, wait;
|
|
|
+ u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
|
|
|
+
|
|
|
+ if (q >= pCap->total_queues) {
|
|
|
+ ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
|
|
|
+ "invalid queue: %u\n", q);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ qi = &ah->txq[q];
|
|
|
+ if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
|
|
|
+ ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, "
|
|
|
+ "inactive queue: %u\n", q);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ REG_WRITE(ah, AR_Q_TXD, 1 << q);
|
|
|
+
|
|
|
+ for (wait = wait_time; wait != 0; wait--) {
|
|
|
+ if (ath9k_hw_numtxpending(ah, q) == 0)
|
|
|
+ break;
|
|
|
+ udelay(ATH9K_TIME_QUANTUM);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ath9k_hw_numtxpending(ah, q)) {
|
|
|
+ ath_print(common, ATH_DBG_QUEUE,
|
|
|
+ "%s: Num of pending TX Frames %d on Q %d\n",
|
|
|
+ __func__, ath9k_hw_numtxpending(ah, q), q);
|
|
|
+
|
|
|
+ for (j = 0; j < 2; j++) {
|
|
|
+ tsfLow = REG_READ(ah, AR_TSF_L32);
|
|
|
+ REG_WRITE(ah, AR_QUIET2,
|
|
|
+ SM(10, AR_QUIET2_QUIET_DUR));
|
|
|
+ REG_WRITE(ah, AR_QUIET_PERIOD, 100);
|
|
|
+ REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
|
|
|
+ REG_SET_BIT(ah, AR_TIMER_MODE,
|
|
|
+ AR_QUIET_TIMER_EN);
|
|
|
+
|
|
|
+ if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
|
|
|
+ break;
|
|
|
+
|
|
|
+ ath_print(common, ATH_DBG_QUEUE,
|
|
|
+ "TSF has moved while trying to set "
|
|
|
+ "quiet time TSF: 0x%08x\n", tsfLow);
|
|
|
+ }
|
|
|
+
|
|
|
+ REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
|
|
|
+
|
|
|
+ udelay(200);
|
|
|
+ REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
|
|
|
+
|
|
|
+ wait = wait_time;
|
|
|
+ while (ath9k_hw_numtxpending(ah, q)) {
|
|
|
+ if ((--wait) == 0) {
|
|
|
+ ath_print(common, ATH_DBG_FATAL,
|
|
|
+ "Failed to stop TX DMA in 100 "
|
|
|
+ "msec after killing last frame\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ udelay(ATH9K_TIME_QUANTUM);
|
|
|
+ }
|
|
|
+
|
|
|
+ REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
|
|
|
+ }
|
|
|
+
|
|
|
+ REG_WRITE(ah, AR_Q_TXD, 0);
|
|
|
+ return wait != 0;
|
|
|
+
|
|
|
+#undef ATH9K_TX_STOP_DMA_TIMEOUT
|
|
|
+#undef ATH9K_TIME_QUANTUM
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(ath9k_hw_stoptxdma);
|
|
|
+
|
|
|
void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs)
|
|
|
{
|
|
|
*txqs &= ah->intr_txqs;
|