aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch')
-rw-r--r--package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch136
1 files changed, 136 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch b/package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch
new file mode 100644
index 0000000..71f7bba
--- /dev/null
+++ b/package/kernel/mac80211/patches/600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch
@@ -0,0 +1,136 @@
+From 5e67d4f8a46d19748b501c2ef86de3f50d3cfd51 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sun, 24 Mar 2013 19:26:27 +0100
+Subject: [PATCH] rt2x00: rt2800mmio: add a workaround for spurious
+ TX_FIFO_STATUS interrupts
+
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/wireless/rt2x00/rt2800mmio.c | 72 +++++++++++++++++++++++++-----
+ drivers/net/wireless/rt2x00/rt2x00.h | 5 +++
+ 2 files changed, 65 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/wireless/rt2x00/rt2800mmio.c
++++ b/drivers/net/wireless/rt2x00/rt2800mmio.c
+@@ -415,9 +415,9 @@ void rt2800mmio_autowake_tasklet(unsigne
+ }
+ EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet);
+
+-static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
++static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev,
++ u32 status)
+ {
+- u32 status;
+ int i;
+
+ /*
+@@ -438,29 +438,77 @@ static void rt2800mmio_txstatus_interrup
+ * Since we have only one producer and one consumer we don't
+ * need to lock the kfifo.
+ */
+- for (i = 0; i < rt2x00dev->tx->limit; i++) {
+- rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
+-
+- if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
+- break;
+-
++ i = 0;
++ do {
+ if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) {
+- rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
++ rt2x00_warn(rt2x00dev,
++ "TX status FIFO overrun, drop TX status report\n");
+ break;
+ }
+- }
++
++ if (++i >= rt2x00dev->tx->limit)
++ break;
++
++ rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
++ } while (rt2x00_get_field32(status, TX_STA_FIFO_VALID));
+
+ /* Schedule the tasklet for processing the tx status. */
+ tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+ }
+
++#define RT2800MMIO_TXSTATUS_IRQ_MAX_RETRIES 4
++
++static bool rt2800mmio_txstatus_is_spurious(struct rt2x00_dev *rt2x00dev,
++ u32 txstatus)
++{
++ if (likely(rt2x00_get_field32(txstatus, TX_STA_FIFO_VALID))) {
++ rt2x00dev->txstatus_irq_retries = 0;
++ return false;
++ }
++
++ rt2x00dev->txstatus_irq_retries++;
++
++ /* Ensure that we don't go into an infinite IRQ loop. */
++ if (rt2x00dev->txstatus_irq_retries >=
++ RT2800MMIO_TXSTATUS_IRQ_MAX_RETRIES) {
++ rt2x00_warn(rt2x00dev,
++ "%u spurious TX_FIFO_STATUS interrupt(s)\n",
++ rt2x00dev->txstatus_irq_retries);
++ rt2x00dev->txstatus_irq_retries = 0;
++ return false;
++ }
++
++ return true;
++}
++
+ irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
+ {
+ struct rt2x00_dev *rt2x00dev = dev_instance;
+ u32 reg, mask;
++ u32 txstatus = 0;
+
+- /* Read status and ACK all interrupts */
++ /* Read status */
+ rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
++
++ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
++ /* Due to unknown reason the hardware generates a
++ * TX_FIFO_STATUS interrupt before the TX_STA_FIFO
++ * register contain valid data. Read the TX status
++ * here to see if we have to process the actual
++ * request.
++ */
++ rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &txstatus);
++ if (rt2800mmio_txstatus_is_spurious(rt2x00dev, txstatus)) {
++ /* Remove the TX_FIFO_STATUS bit so it won't be
++ * processed in this turn. The hardware will
++ * generate another IRQ for us.
++ */
++ rt2x00_set_field32(&reg,
++ INT_SOURCE_CSR_TX_FIFO_STATUS, 0);
++ }
++ }
++
++ /* ACK interrupts */
+ rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+
+ if (!reg)
+@@ -477,7 +525,7 @@ irqreturn_t rt2800mmio_interrupt(int irq
+ mask = ~reg;
+
+ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
+- rt2800mmio_txstatus_interrupt(rt2x00dev);
++ rt2800mmio_txstatus_interrupt(rt2x00dev, txstatus);
+ /*
+ * Never disable the TX_FIFO_STATUS interrupt.
+ */
+--- a/drivers/net/wireless/rt2x00/rt2x00.h
++++ b/drivers/net/wireless/rt2x00/rt2x00.h
+@@ -989,6 +989,11 @@ struct rt2x00_dev {
+ int rf_channel;
+
+ /*
++ * Counter for tx status irq retries (rt2800pci).
++ */
++ unsigned int txstatus_irq_retries;
++
++ /*
+ * Protect the interrupt mask register.
+ */
+ spinlock_t irqmask_lock;