342174f8d5dfc74d96b1822dee6b837f883300a3
[openwrt/openwrt.git] /
1 From 5e67d4f8a46d19748b501c2ef86de3f50d3cfd51 Mon Sep 17 00:00:00 2001
2 From: Gabor Juhos <juhosg@openwrt.org>
3 Date: Sun, 24 Mar 2013 19:26:27 +0100
4 Subject: [PATCH] rt2x00: rt2800mmio: add a workaround for spurious
5 TX_FIFO_STATUS interrupts
6
7 Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
8 ---
9 drivers/net/wireless/ralink/rt2x00/rt2800mmio.c | 72 +++++++++++++++++++++++++-----
10 drivers/net/wireless/ralink/rt2x00/rt2x00.h | 5 +++
11 2 files changed, 65 insertions(+), 12 deletions(-)
12
13 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
14 +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
15 @@ -424,9 +424,9 @@ void rt2800mmio_autowake_tasklet(unsigne
16 }
17 EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet);
18
19 -static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
20 +static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev,
21 + u32 status)
22 {
23 - u32 status;
24 int i;
25
26 /*
27 @@ -447,29 +447,77 @@ static void rt2800mmio_txstatus_interrup
28 * Since we have only one producer and one consumer we don't
29 * need to lock the kfifo.
30 */
31 - for (i = 0; i < rt2x00dev->tx->limit; i++) {
32 - status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO);
33 -
34 - if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
35 - break;
36 -
37 + i = 0;
38 + do {
39 if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) {
40 - rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n");
41 + rt2x00_warn(rt2x00dev,
42 + "TX status FIFO overrun, drop TX status report\n");
43 break;
44 }
45 - }
46 +
47 + if (++i >= rt2x00dev->tx->limit)
48 + break;
49 +
50 + status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO);
51 + } while (rt2x00_get_field32(status, TX_STA_FIFO_VALID));
52
53 /* Schedule the tasklet for processing the tx status. */
54 tasklet_schedule(&rt2x00dev->txstatus_tasklet);
55 }
56
57 +#define RT2800MMIO_TXSTATUS_IRQ_MAX_RETRIES 4
58 +
59 +static bool rt2800mmio_txstatus_is_spurious(struct rt2x00_dev *rt2x00dev,
60 + u32 txstatus)
61 +{
62 + if (likely(rt2x00_get_field32(txstatus, TX_STA_FIFO_VALID))) {
63 + rt2x00dev->txstatus_irq_retries = 0;
64 + return false;
65 + }
66 +
67 + rt2x00dev->txstatus_irq_retries++;
68 +
69 + /* Ensure that we don't go into an infinite IRQ loop. */
70 + if (rt2x00dev->txstatus_irq_retries >=
71 + RT2800MMIO_TXSTATUS_IRQ_MAX_RETRIES) {
72 + rt2x00_warn(rt2x00dev,
73 + "%u spurious TX_FIFO_STATUS interrupt(s)\n",
74 + rt2x00dev->txstatus_irq_retries);
75 + rt2x00dev->txstatus_irq_retries = 0;
76 + return false;
77 + }
78 +
79 + return true;
80 +}
81 +
82 irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance)
83 {
84 struct rt2x00_dev *rt2x00dev = dev_instance;
85 u32 reg, mask;
86 + u32 txstatus = 0;
87
88 - /* Read status and ACK all interrupts */
89 + /* Read status */
90 reg = rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR);
91 +
92 + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
93 + /* Due to unknown reason the hardware generates a
94 + * TX_FIFO_STATUS interrupt before the TX_STA_FIFO
95 + * register contain valid data. Read the TX status
96 + * here to see if we have to process the actual
97 + * request.
98 + */
99 + txstatus = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO);
100 + if (rt2800mmio_txstatus_is_spurious(rt2x00dev, txstatus)) {
101 + /* Remove the TX_FIFO_STATUS bit so it won't be
102 + * processed in this turn. The hardware will
103 + * generate another IRQ for us.
104 + */
105 + rt2x00_set_field32(&reg,
106 + INT_SOURCE_CSR_TX_FIFO_STATUS, 0);
107 + }
108 + }
109 +
110 + /* ACK interrupts */
111 rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
112
113 if (!reg)
114 @@ -486,7 +534,7 @@ irqreturn_t rt2800mmio_interrupt(int irq
115 mask = ~reg;
116
117 if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
118 - rt2800mmio_txstatus_interrupt(rt2x00dev);
119 + rt2800mmio_txstatus_interrupt(rt2x00dev, txstatus);
120 /*
121 * Never disable the TX_FIFO_STATUS interrupt.
122 */
123 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
124 +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
125 @@ -1000,6 +1000,11 @@ struct rt2x00_dev {
126 int rf_channel;
127
128 /*
129 + * Counter for tx status irq retries (rt2800pci).
130 + */
131 + unsigned int txstatus_irq_retries;
132 +
133 + /*
134 * Protect the interrupt mask register.
135 */
136 spinlock_t irqmask_lock;