>From 40dbc7400033e9d29af3eddb3105cb1b8668fb6d Mon Sep 17 00:00:00 2001 From: michael <[email protected]> Date: Thu, 30 Jul 2009 12:47:44 +0200 Subject: [PATCH 3/4] 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 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
