[PATCH 3/4] Little clean up and move the do_irq on a deferrable work
michael
michael at panicking.kicks-ass.org
Thu Jul 30 12:47:44 CEST 2009
Signed-off-by: Michael Trimarchi <michael at panicking.kicks-ass.org>
---
drivers/ar6000/hif/hif2.c | 98 +++++++++++++++++++++++++--------------------
1 files changed, 55 insertions(+), 43 deletions(-)
diff --git a/drivers/ar6000/hif/hif2.c b/drivers/ar6000/hif/hif2.c
index 386d96e..e5cb91f 100644
--- a/drivers/ar6000/hif/hif2.c
+++ b/drivers/ar6000/hif/hif2.c
@@ -15,7 +15,6 @@
* @notice: Copyright (c) 2004-2006 Atheros Communications Inc.
*/
-
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/list.h>
@@ -118,6 +117,13 @@ struct hif_device {
*/
int active;
struct mutex activate_lock;
+
+ int status;
+#define AR_PENDING 1
+#define AR_MASK 2
+#define AR_INPROGRESS 4
+ spinlock_t status_lock;
+ struct work_struct transfer;
};
struct hif_request {
@@ -317,20 +323,20 @@ A_STATUS HIFReadWrite(HIF_DEVICE *hif, A_UINT32 address, A_UCHAR *buffer,
* Warning: this story changes if going SMP/SMT.
*/
-static volatile int masked = 1;
-static volatile int pending;
-static volatile int in_interrupt;
-
-
-static void ar6000_do_irq(struct sdio_func *func)
+static void tasklet_ar6000_do_irq(struct work_struct *work)
{
- HIF_DEVICE *hif = sdio_get_drvdata(func);
+ unsigned long flags;
+ HIF_DEVICE *hif =
+ container_of(work, struct hif_device, transfer);
struct device *dev = HIFGetOSDevice(hif);
A_STATUS status;
dev_dbg(dev, "ar6000_do_irq -> %p\n", htcCallbacks.dsrHandler);
status = htcCallbacks.dsrHandler(hif->htc_handle);
+ spin_lock_irqsave(&hif->status_lock, flags);
+ hif->status &= ~AR_INPROGRESS;
+ spin_unlock_irqrestore(&hif->status_lock, flags);
BUG_ON(status != A_OK);
}
@@ -339,33 +345,23 @@ static void sdio_ar6000_irq(struct sdio_func *func)
{
HIF_DEVICE *hif = sdio_get_drvdata(func);
struct device *dev = HIFGetOSDevice(hif);
+ unsigned long flags;
dev_dbg(dev, "sdio_ar6000_irq\n");
- in_interrupt = 1;
- if (masked) {
- in_interrupt = 0;
- pending++;
- return;
+ spin_lock_irqsave(&hif->status_lock, flags);
+ if (hif->status & AR_MASK) {
+ /* only one pending interrupt */
+ WARN_ON(hif->status & AR_PENDING);
+ hif->status |= AR_PENDING;
+ goto out;
}
- /*
- * @@@ This is ugly. If we don't drop the lock, we'll deadlock when
- * the handler tries to do SDIO. So there are four choices:
- *
- * 1) Break the call chain by calling the callback from a workqueue.
- * Ugh.
- * 2) Make process_request aware that we already have the lock.
- * 3) Drop the lock. Which is ugly but should be safe as long as we're
- * making sure the device doesn't go away.
- * 4) Change the AR6k driver such that it only issues asynchronous
- * quests when called from an interrupt.
- *
- * Solution 2) is probably the best for now. Will try it later.
- */
- sdio_release_host(func);
- ar6000_do_irq(func);
- sdio_claim_host(func);
- in_interrupt = 0;
+ hif->status |= AR_INPROGRESS;
+ spin_unlock_irqrestore(&hif->status_lock, flags);
+ schedule_work(&hif->transfer);
+ return;
+out:
+ spin_unlock_irqrestore(&hif->status_lock, flags);
}
@@ -381,25 +377,28 @@ void HIFAckInterrupt(HIF_DEVICE *hif)
void HIFUnMaskInterrupt(HIF_DEVICE *hif)
{
struct device *dev = HIFGetOSDevice(hif);
+ unsigned long flags;
dev_dbg(dev, "HIFUnMaskInterrupt\n");
- do {
- masked = 1;
- if (pending) {
- pending = 0;
- ar6000_do_irq(hif->func);
- /* We may take an interrupt before unmasking and thus
- get it pending. In this case, we just loop back. */
- }
- masked = 0;
+retry:
+ spin_lock_irqsave(&hif->status_lock, flags);
+ if (hif->status & AR_PENDING) {
+ spin_unlock_irqrestore(&hif->status_lock, flags);
+ schedule_work(&hif->transfer);
+ /* We may take an interrupt before unmasking and thus
+ get it pending. In this case, we just loop back. */
+ hif->status &= ~AR_PENDING;
+ goto retry;
}
- while (pending);
+ hif->status &= ~AR_MASK;
+ spin_unlock_irqrestore(&hif->status_lock, flags);
}
void HIFMaskInterrupt(HIF_DEVICE *hif)
{
struct device *dev = HIFGetOSDevice(hif);
+ unsigned long flags;
dev_dbg(dev, "HIFMaskInterrupt\n");
/*
@@ -411,9 +410,15 @@ void HIFMaskInterrupt(HIF_DEVICE *hif)
* Note: this may be a bit on the paranoid side - the callers may
* actually be nice enough to disable scheduling. Check later.
*/
- masked = 1;
- while (in_interrupt)
+retry:
+ spin_lock_irqsave(&hif->status_lock, flags);
+ if (hif->status & AR_INPROGRESS) {
+ spin_unlock_irqrestore(&hif->status_lock, flags);
yield();
+ goto retry;
+ }
+ hif->status |= AR_MASK;
+ spin_unlock_irqrestore(&hif->status_lock, flags);
}
@@ -484,6 +489,9 @@ static int ar6000_do_activate(struct hif_device *hif)
INIT_LIST_HEAD(&hif->queue);
init_waitqueue_head(&hif->wait);
spin_lock_init(&hif->queue_lock);
+ spin_lock_init(&hif->status_lock);
+
+ hif->status = 0;
ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
if (ret < 0) {
@@ -495,6 +503,8 @@ static int ar6000_do_activate(struct hif_device *hif)
dev_err(dev, "sdio_claim_irq returns %d\n", ret);
goto out_enabled;
}
+ INIT_WORK(&hif->transfer, tasklet_ar6000_do_irq);
+
/* Set SDIO_BUS_CD_DISABLE in SDIO_CCCR_IF ? */
#if 0
sdio_f0_writeb(func, SDIO_CCCR_CAP_E4MI, SDIO_CCCR_CAPS, &ret);
@@ -566,9 +576,11 @@ static void ar6000_do_deactivate(struct hif_device *hif)
mutex_unlock(&shutdown_lock);
}
wait_queue_empty(hif);
+ cancel_work_sync(&hif->transfer);
ret = kthread_stop(hif->io_task);
if (ret)
dev_err(dev, "kthread_stop (ar6000_io): %d\n", ret);
+
sdio_claim_host(func);
sdio_release_irq(func);
sdio_disable_func(func);
--
1.6.3.3
--------------090802000402020207080208--
More information about the openmoko-kernel
mailing list