Little clean up and move the do_irq on a deferrable work

Signed-off-by: Michael Trimarchi <[email protected]>
---
 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 ccfd8e1..b8842b0 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>
@@ -104,6 +103,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 {
@@ -314,20 +320,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);
 }
 
@@ -336,33 +342,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);
 }
 
 
@@ -378,25 +374,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");
        /*
@@ -408,9 +407,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);
 }
 
 
@@ -481,6 +486,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) {
@@ -492,6 +500,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);
@@ -563,9 +573,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.5.6.5


Reply via email to