aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath5k/dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/dma.c')
-rw-r--r--drivers/net/wireless/ath/ath5k/dma.c180
1 files changed, 168 insertions, 12 deletions
diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
index 923c9ca5c4f0..0064be7ce5c9 100644
--- a/drivers/net/wireless/ath/ath5k/dma.c
+++ b/drivers/net/wireless/ath/ath5k/dma.c
@@ -37,6 +37,7 @@
#include "debug.h"
#include "base.h"
+
/*********\
* Receive *
\*********/
@@ -57,7 +58,7 @@ void ath5k_hw_start_rx_dma(struct ath5k_hw *ah)
*
* @ah: The &struct ath5k_hw
*/
-int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
+static int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
{
unsigned int i;
@@ -69,7 +70,11 @@ int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
for (i = 1000; i > 0 &&
(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
i--)
- udelay(10);
+ udelay(100);
+
+ if (!i)
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "failed to stop RX DMA !\n");
return i ? 0 : -EBUSY;
}
@@ -90,11 +95,18 @@ u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah)
* @ah: The &struct ath5k_hw
* @phys_addr: RX descriptor address
*
- * XXX: Should we check if rx is enabled before setting rxdp ?
+ * Returns -EIO if rx is active
*/
-void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
+int ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
{
+ if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "tried to set RXDP while rx was active !\n");
+ return -EIO;
+ }
+
ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
+ return 0;
}
@@ -125,7 +137,7 @@ int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
- return -EIO;
+ return -EINVAL;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
@@ -173,10 +185,10 @@ int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
*
* Stop DMA transmit on a specific hw queue and drain queue so we don't
* have any pending frames. Returns -EBUSY if we still have pending frames,
- * -EINVAL if queue number is out of range.
+ * -EINVAL if queue number is out of range or inactive.
*
*/
-int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
+static int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
unsigned int i = 40;
u32 tx_queue, pending;
@@ -185,7 +197,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
- return -EIO;
+ return -EINVAL;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
@@ -211,12 +223,31 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
} else {
+
+ /*
+ * Enable DCU early termination to quickly
+ * flush any pending frames from QCU
+ */
+ AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_DCU_EARLY);
+
/*
* Schedule TX disable and wait until queue is empty
*/
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
- /*Check for pending frames*/
+ /* Wait for queue to stop */
+ for (i = 1000; i > 0 &&
+ (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0);
+ i--)
+ udelay(100);
+
+ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "queue %i didn't stop !\n", queue);
+
+ /* Check for pending frames */
+ i = 1000;
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
@@ -247,12 +278,12 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
/* Wait a while and disable mechanism */
- udelay(200);
+ udelay(400);
AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
AR5K_QUIET_CTL1_QT_EN);
/* Re-check for pending frames */
- i = 40;
+ i = 100;
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
@@ -262,12 +293,27 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
+
+ if (pending)
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "quiet mechanism didn't work q:%i !\n",
+ queue);
}
+ /*
+ * Disable DCU early termination
+ */
+ AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
+ AR5K_QCU_MISC_DCU_EARLY);
+
/* Clear register */
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
- if (pending)
+ if (pending) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "tx dma didn't stop (q:%i, frm:%i) !\n",
+ queue, pending);
return -EBUSY;
+ }
}
/* TODO: Check for success on 5210 else return error */
@@ -275,6 +321,26 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
}
/**
+ * ath5k_hw_stop_beacon_queue - Stop beacon queue
+ *
+ * @ah The &struct ath5k_hw
+ * @queue The queue number
+ *
+ * Returns -EIO if queue didn't stop
+ */
+int ath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue)
+{
+ int ret;
+ ret = ath5k_hw_stop_tx_dma(ah, queue);
+ if (ret) {
+ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_DMA,
+ "beacon queue didn't stop !\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
* ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue
*
* @ah: The &struct ath5k_hw
@@ -427,6 +493,7 @@ done:
return ret;
}
+
/*******************\
* Interrupt masking *
\*******************/
@@ -688,3 +755,92 @@ enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
return old_mask;
}
+
+/********************\
+ Init/Stop functions
+\********************/
+
+/**
+ * ath5k_hw_dma_init - Initialize DMA unit
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Set DMA size and pre-enable interrupts
+ * (driver handles tx/rx buffer setup and
+ * dma start/stop)
+ *
+ * XXX: Save/restore RXDP/TXDP registers ?
+ */
+void ath5k_hw_dma_init(struct ath5k_hw *ah)
+{
+ /*
+ * Set Rx/Tx DMA Configuration
+ *
+ * Set standard DMA size (128). Note that
+ * a DMA size of 512 causes rx overruns and tx errors
+ * on pci-e cards (tested on 5424 but since rx overruns
+ * also occur on 5416/5418 with madwifi we set 128
+ * for all PCI-E cards to be safe).
+ *
+ * XXX: need to check 5210 for this
+ * TODO: Check out tx triger level, it's always 64 on dumps but I
+ * guess we can tweak it and see how it goes ;-)
+ */
+ if (ah->ah_version != AR5K_AR5210) {
+ AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
+ AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B);
+ AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG,
+ AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B);
+ }
+
+ /* Pre-enable interrupts on 5211/5212*/
+ if (ah->ah_version != AR5K_AR5210)
+ ath5k_hw_set_imr(ah, ah->ah_imr);
+
+}
+
+/**
+ * ath5k_hw_dma_stop - stop DMA unit
+ *
+ * @ah: The &struct ath5k_hw
+ *
+ * Stop tx/rx DMA and interrupts. Returns
+ * -EBUSY if tx or rx dma failed to stop.
+ *
+ * XXX: Sometimes DMA unit hangs and we have
+ * stuck frames on tx queues, only a reset
+ * can fix that.
+ */
+int ath5k_hw_dma_stop(struct ath5k_hw *ah)
+{
+ int i, qmax, err;
+ err = 0;
+
+ /* Disable interrupts */
+ ath5k_hw_set_imr(ah, 0);
+
+ /* Stop rx dma */
+ err = ath5k_hw_stop_rx_dma(ah);
+ if (err)
+ return err;
+
+ /* Clear any pending interrupts
+ * and disable tx dma */
+ if (ah->ah_version != AR5K_AR5210) {
+ ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR);
+ qmax = AR5K_NUM_TX_QUEUES;
+ } else {
+ /* PISR/SISR Not available on 5210 */
+ ath5k_hw_reg_read(ah, AR5K_ISR);
+ qmax = AR5K_NUM_TX_QUEUES_NOQCU;
+ }
+
+ for (i = 0; i < qmax; i++) {
+ err = ath5k_hw_stop_tx_dma(ah, i);
+ /* -EINVAL -> queue inactive */
+ if (err != -EINVAL)
+ return err;
+ }
+
+ return err;
+}