399d5cf532
Some of our local patches have been accepted upstream. And there are some more relevant changes (mostly for rt2800usb). Import them and rebase our remaining local patches on top. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
136 lines
4.1 KiB
Diff
136 lines
4.1 KiB
Diff
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/ralink/rt2x00/rt2800mmio.c | 72 +++++++++++++++++++++++++-----
|
|
drivers/net/wireless/ralink/rt2x00/rt2x00.h | 5 +++
|
|
2 files changed, 65 insertions(+), 12 deletions(-)
|
|
|
|
--- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
|
|
+++ b/drivers/net/wireless/ralink/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, ®);
|
|
+
|
|
+ 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(®,
|
|
+ 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/ralink/rt2x00/rt2x00.h
|
|
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
|
|
@@ -1000,6 +1000,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;
|