mac80211: update brcmfmac including missing boardrev workaround
[openwrt.git] / package / kernel / mac80211 / patches / 600-0034-rt2x00-rt2800mmio-add-a-workaround-for-spurious-TX_F.patch
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 @@ -415,9 +415,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 @@ -438,29 +438,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 -               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
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 +               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
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         rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
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 +               rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &txstatus);
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 @@ -477,7 +525,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 @@ -991,6 +991,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;