[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