summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/cw1200/cw1200_spi.c
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2013-08-27 20:29:46 -0400
committerJohn W. Linville <linville@tuxdriver.com>2013-09-09 14:40:53 -0400
commitaec8e88c947b7017e2b4bbcb68a4bfc4a1f8ad35 (patch)
tree584e2a99402483e16d6145bdf1d29416a1a0abce /drivers/net/wireless/cw1200/cw1200_spi.c
parente7d33bb5ea82922e6ddcfc6b28a630b1a4ced071 (diff)
cw1200: Don't perform SPI transfers in interrupt context
When we get an interrupt from the hardware, the first thing the driver does is tell the device to mask off the interrupt line. Unfortunately this involves a SPI transaction in interrupt context. Some (most?) SPI controllers perform the transfer asynchronously and try to sleep. This is bad, and triggers a BUG(). So, work around this by using adding a hwbus hook for the cw1200 driver core to call. The cw1200_spi driver translates this into irq_disable()/irq_enable() calls instead, which can safely be called in interrupt context. Apparently the platforms I used to develop the cw1200_spi driver used synchronous spi_sync() implementations, which is why this didn't surface until now. Many thanks to Dave Sizeburns for the inital bug report and his services as a tester. Signed-off-by: Solomon Peachy <pizza@shaftnet.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/cw1200/cw1200_spi.c')
-rw-r--r--drivers/net/wireless/cw1200/cw1200_spi.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
index d06376014bcd..c31580ba883b 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -41,6 +41,7 @@ struct hwbus_priv {
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
int claimed;
+ int irq_disabled;
};
#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
@@ -230,6 +231,8 @@ static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
struct hwbus_priv *self = dev_id;
if (self->core) {
+ disable_irq_nosync(self->func->irq);
+ self->irq_disabled = 1;
cw1200_irq_handler(self->core);
return IRQ_HANDLED;
} else {
@@ -263,13 +266,22 @@ exit:
static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self)
{
- int ret = 0;
-
pr_debug("SW IRQ unsubscribe\n");
disable_irq_wake(self->func->irq);
free_irq(self->func->irq, self);
- return ret;
+ return 0;
+}
+
+static int cw1200_spi_irq_enable(struct hwbus_priv *self, int enable)
+{
+ /* Disables are handled by the interrupt handler */
+ if (enable && self->irq_disabled) {
+ enable_irq(self->func->irq);
+ self->irq_disabled = 0;
+ }
+
+ return 0;
}
static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
@@ -349,6 +361,7 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = {
.unlock = cw1200_spi_unlock,
.align_size = cw1200_spi_align_size,
.power_mgmt = cw1200_spi_pm,
+ .irq_enable = cw1200_spi_irq_enable,
};
/* Probe Function to be called by SPI stack when device is discovered */