wl12xx: Handle platforms without level trigger interrupts
authorIdo Yariv <ido@wizery.com>
Thu, 31 Mar 2011 08:07:01 +0000 (10:07 +0200)
committerLuciano Coelho <coelho@ti.com>
Tue, 19 Apr 2011 13:49:20 +0000 (16:49 +0300)
Some platforms are incapable of triggering on level interrupts. Add a
platform quirks member in the platform data structure, as well as an
edge interrupt quirk which can be set on such platforms.

When the interrupt is requested with IRQF_TRIGGER_RISING, IRQF_ONESHOT
cannot be used, as we might miss interrupts that occur after the FW
status is cleared and before the threaded interrupt handler exits.

Moreover, when IRQF_ONESHOT is not set, iterating more than once in the
threaded interrupt handler introduces a few race conditions between this
handler and the hardirq handler. Currently this is worked around by
limiting the loop to one iteration only. This workaround has an impact
on performance. To remove to this restriction, the race conditions will
need to be addressed.

Signed-off-by: Ido Yariv <ido@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/sdio.c
drivers/net/wireless/wl12xx/spi.c
drivers/net/wireless/wl12xx/wl12xx.h
include/linux/wl12xx.h

index 1feb9551ef8faa9a2f148531bc8fd7a97e2555c9..7126506611c11b3db0adea7fa0661e0ff4d38351 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/vmalloc.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/wl12xx.h>
 
 #include "wl12xx.h"
 #include "wl12xx_80211.h"
@@ -719,6 +720,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
        set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
        cancel_work_sync(&wl->tx_work);
 
+       /*
+        * In case edge triggered interrupt must be used, we cannot iterate
+        * more than once without introducing race conditions with the hardirq.
+        */
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+               loopcount = 1;
+
        mutex_lock(&wl->mutex);
 
        wl1271_debug(DEBUG_IRQ, "IRQ work");
@@ -3648,6 +3656,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
+       wl->platform_quirks = 0;
 
        memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
index 8246e9de4306f4bab00f24c84a7c8c61607391c6..bcd4ad7ba90df55f7c9416e63ca56903695f27dd 100644 (file)
@@ -220,6 +220,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        struct ieee80211_hw *hw;
        const struct wl12xx_platform_data *wlan_data;
        struct wl1271 *wl;
+       unsigned long irqflags;
        int ret;
 
        /* We are only able to handle the wlan function */
@@ -251,9 +252,15 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        wl->irq = wlan_data->irq;
        wl->ref_clock = wlan_data->board_ref_clock;
        wl->tcxo_clock = wlan_data->board_tcxo_clock;
+       wl->platform_quirks = wlan_data->platform_quirks;
+
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+               irqflags = IRQF_TRIGGER_RISING;
+       else
+               irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
 
        ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
-                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                  irqflags,
                                   DRIVER_NAME, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
index 7b82b5f0e4901ab008b9c5869b67a94bafbc9340..51662bb680197a5585335f231364718ce9b2d3e8 100644 (file)
@@ -364,6 +364,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
        struct wl12xx_platform_data *pdata;
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
+       unsigned long irqflags;
        int ret;
 
        pdata = spi->dev.platform_data;
@@ -402,6 +403,12 @@ static int __devinit wl1271_probe(struct spi_device *spi)
 
        wl->ref_clock = pdata->board_ref_clock;
        wl->tcxo_clock = pdata->board_tcxo_clock;
+       wl->platform_quirks = pdata->platform_quirks;
+
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+               irqflags = IRQF_TRIGGER_RISING;
+       else
+               irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
 
        wl->irq = spi->irq;
        if (wl->irq < 0) {
@@ -411,7 +418,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
        }
 
        ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
-                                  IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                  irqflags,
                                   DRIVER_NAME, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
index 9ccbcfdd08028c676257b8f2a2589c051e18acaf..fb2b79fa42b494f32da4a528e9fc13a463248e0d 100644 (file)
@@ -579,6 +579,9 @@ struct wl1271 {
 
        /* Quirks of specific hardware revisions */
        unsigned int quirks;
+
+       /* Platform limitations */
+       unsigned int platform_quirks;
 };
 
 struct wl1271_station {
index c1a743ea74704bcb0e118adfda955cd91f4419c4..4b697395326eb4e660a099faf352a91c0c8df915 100644 (file)
@@ -53,8 +53,12 @@ struct wl12xx_platform_data {
        bool use_eeprom;
        int board_ref_clock;
        int board_tcxo_clock;
+       unsigned long platform_quirks;
 };
 
+/* Platform does not support level trigger interrupts */
+#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0)
+
 #ifdef CONFIG_WL12XX_PLATFORM_DATA
 
 int wl12xx_set_platform_data(const struct wl12xx_platform_data *data);