Skip to content

Commit

Permalink
wl12xx: Handle platforms without level trigger interrupts
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ido Yariv authored and Luciano Coelho committed Apr 19, 2011
1 parent d2f4d47 commit 341b7cd
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 2 deletions.
9 changes: 9 additions & 0 deletions drivers/net/wireless/wl12xx/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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++)
Expand Down
9 changes: 8 additions & 1 deletion drivers/net/wireless/wl12xx/sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion drivers/net/wireless/wl12xx/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/wl12xx/wl12xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@ struct wl1271 {

/* Quirks of specific hardware revisions */
unsigned int quirks;

/* Platform limitations */
unsigned int platform_quirks;
};

struct wl1271_station {
Expand Down
4 changes: 4 additions & 0 deletions include/linux/wl12xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 341b7cd

Please sign in to comment.