diff options
| author | Sundar Subramaniyan <subrams@qti.qualcomm.com> | 2014-01-30 21:29:35 +0530 |
|---|---|---|
| committer | Prakash Dhavali <pdhavali@qca.qualcomm.com> | 2014-02-04 00:02:40 -0800 |
| commit | cc9365f1972d41bd100b8d244fa05444fea42786 (patch) | |
| tree | 81773bef14d06e616a8899fb41b347b336f2d9cd | |
| parent | 1467025969f05ef63c57a9e91540bd08365aaf3e (diff) | |
qcacld: Wake the SoC before accessing interrupt registers
- Before accessing the interrupt enable and clear registers,
wake the SoC in interrupt handler and in tasklet
- Disable IRQs as well while holding spinlock
when waking or putting target to sleep to avoid deadlock.
- Add debug when interrupt cannot be disabled due to loss of
synchronization between host and target states during suspend
Change-Id: I24c1b607e5680bd5a56141b332f705e7705fc54e
CRs-Fixed: 600998
| -rw-r--r-- | CORE/SERVICES/COMMON/adf/adf_os_lock.h | 3 | ||||
| -rw-r--r-- | CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h | 23 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/hif_pci.c | 19 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/if_pci.c | 49 |
4 files changed, 63 insertions, 31 deletions
diff --git a/CORE/SERVICES/COMMON/adf/adf_os_lock.h b/CORE/SERVICES/COMMON/adf/adf_os_lock.h index 16f58dc5d539..24ca01849019 100644 --- a/CORE/SERVICES/COMMON/adf/adf_os_lock.h +++ b/CORE/SERVICES/COMMON/adf/adf_os_lock.h @@ -114,6 +114,9 @@ adf_os_spinlock_destroy(adf_os_spinlock_t *lock) #define adf_os_spin_lock( _lock) __adf_os_spin_lock(_lock) #define adf_os_spin_unlock( _lock ) __adf_os_spin_unlock(_lock) +#define adf_os_spin_lock_irqsave( _lock) __adf_os_spin_lock_irqsave(_lock) +#define adf_os_spin_unlock_irqrestore( _lock ) \ + __adf_os_spin_unlock_irqrestore(_lock) /** * @brief locks the spinlock mutex in soft irq context diff --git a/CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h b/CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h index 5bf81af170d1..e5e96065f149 100644 --- a/CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h +++ b/CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h @@ -41,6 +41,7 @@ typedef struct __adf_os_linux_spinlock { spinlock_t spinlock; unsigned int flags; + unsigned long _flags; } adf_os_linux_spinlock_t; /* define for flag */ @@ -118,6 +119,28 @@ __adf_os_spin_unlock(__adf_os_spinlock_t *lock) spin_unlock(&lock->spinlock); } +/** + * @brief Acquire a Spinlock (SMP) & disable Preemption (Preemptive) + * Disable IRQs + * @param lock (Lock object) + */ +static inline void +__adf_os_spin_lock_irqsave(__adf_os_spinlock_t *lock) +{ + spin_lock_irqsave(&lock->spinlock, lock->_flags); +} + +/** + * @brief Unlock the spinlock and enables the Preemption + * Enable IRQ + * @param lock (Lock object) + */ +static inline void +__adf_os_spin_unlock_irqrestore(__adf_os_spinlock_t *lock) +{ + spin_unlock_irqrestore(&lock->spinlock, lock->_flags); +} + /** * @brief Acquire the spinlock and disable bottom halves * diff --git a/CORE/SERVICES/HIF/PCIe/hif_pci.c b/CORE/SERVICES/HIF/PCIe/hif_pci.c index 74cb50f52d4d..6d7e799dc633 100644 --- a/CORE/SERVICES/HIF/PCIe/hif_pci.c +++ b/CORE/SERVICES/HIF/PCIe/hif_pci.c @@ -52,6 +52,7 @@ #define ATH_MODULE_NAME hif #include <a_debug.h> #include "hif_pci.h" +#include "vos_trace.h" /* use credit flow control over HTC */ unsigned int htc_credit_flow = 1; @@ -1965,7 +1966,7 @@ HIF_sleep_entry(void *arg) struct hif_pci_softc *sc = hif_state->sc; u_int32_t idle_ms; - adf_os_spin_lock(&hif_state->keep_awake_lock); + adf_os_spin_lock_irqsave(&hif_state->keep_awake_lock); if (hif_state->verified_awake == FALSE) { idle_ms = adf_os_ticks_to_msecs(adf_os_ticks() - hif_state->sleep_ticks); @@ -1981,7 +1982,7 @@ HIF_sleep_entry(void *arg) adf_os_timer_start(&hif_state->sleep_timer, HIF_SLEEP_INACTIVITY_TIMER_PERIOD_MS); } - adf_os_spin_unlock(&hif_state->keep_awake_lock); + adf_os_spin_unlock_irqrestore(&hif_state->keep_awake_lock); } void @@ -1991,7 +1992,7 @@ HIFCancelDeferredTargetSleep(HIF_DEVICE *hif_device) A_target_id_t pci_addr = TARGID_TO_PCI_ADDR(hif_state->targid); struct hif_pci_softc *sc = hif_state->sc; - adf_os_spin_lock(&hif_state->keep_awake_lock); + adf_os_spin_lock_irqsave(&hif_state->keep_awake_lock); /* * If the deferred sleep timer is running cancel it * and put the soc into sleep. @@ -2004,7 +2005,7 @@ HIFCancelDeferredTargetSleep(HIF_DEVICE *hif_device) } hif_state->fake_sleep = FALSE; } - adf_os_spin_unlock(&hif_state->keep_awake_lock); + adf_os_spin_unlock_irqrestore(&hif_state->keep_awake_lock); } /* @@ -2323,7 +2324,7 @@ HIFTargetSleepStateAdjust(A_target_id_t targid, if (sleep_ok) { - adf_os_spin_lock(&hif_state->keep_awake_lock); + adf_os_spin_lock_irqsave(&hif_state->keep_awake_lock); hif_state->keep_awake_count--; if (hif_state->keep_awake_count == 0) { /* Allow sleep */ @@ -2339,9 +2340,9 @@ HIFTargetSleepStateAdjust(A_target_id_t targid, adf_os_timer_start(&hif_state->sleep_timer, HIF_SLEEP_INACTIVITY_TIMER_PERIOD_MS); } - adf_os_spin_unlock(&hif_state->keep_awake_lock); + adf_os_spin_unlock_irqrestore(&hif_state->keep_awake_lock); } else { - adf_os_spin_lock(&hif_state->keep_awake_lock); + adf_os_spin_lock_irqsave(&hif_state->keep_awake_lock); if (hif_state->fake_sleep) { hif_state->verified_awake = TRUE; @@ -2353,7 +2354,7 @@ HIFTargetSleepStateAdjust(A_target_id_t targid, } } hif_state->keep_awake_count++; - adf_os_spin_unlock(&hif_state->keep_awake_lock); + adf_os_spin_unlock_irqrestore(&hif_state->keep_awake_lock); if (wait_for_it && !hif_state->verified_awake) { #define PCIE_WAKE_TIMEOUT 5000 /* 5Ms */ @@ -2374,7 +2375,7 @@ HIFTargetSleepStateAdjust(A_target_id_t targid, printk("%s: keep_awake_count %d PCIE_SOC_WAKE_ADDRESS = %x\n",__func__, hif_state->keep_awake_count, A_PCI_READ32(pci_addr + PCIE_LOCAL_BASE_ADDRESS + PCIE_SOC_WAKE_ADDRESS)); - ASSERT(0); + VOS_BUG(0); } OS_DELAY(curr_delay); diff --git a/CORE/SERVICES/HIF/PCIe/if_pci.c b/CORE/SERVICES/HIF/PCIe/if_pci.c index 6de3a42c984c..37cbb090dbaf 100644 --- a/CORE/SERVICES/HIF/PCIe/if_pci.c +++ b/CORE/SERVICES/HIF/PCIe/if_pci.c @@ -31,6 +31,8 @@ #include <linux/interrupt.h> #include <linux/if_arp.h> #include "if_pci.h" +#include "hif_msg_based.h" +#include "hif_pci.h" #include "copy_engine_api.h" #include "bmi_msg.h" /* TARGET_TYPE_ */ #include "regtable.h" @@ -97,9 +99,13 @@ static irqreturn_t hif_pci_interrupt_handler(int irq, void *arg) { struct hif_pci_softc *sc = (struct hif_pci_softc *) arg; + struct HIF_CE_state *hif_state = (struct HIF_CE_state *)sc->hif_device; + A_target_id_t targid = hif_state->targid; volatile int tmp; if (LEGACY_INTERRUPTS(sc)) { + A_TARGET_ACCESS_BEGIN(targid); + /* Clear Legacy PCI line interrupts */ /* IMPORTANT: INTR_CLR regiser has to be set after INTR_ENABLE is set to 0, */ /* otherwise interrupt can not be really cleared */ @@ -107,6 +113,13 @@ hif_pci_interrupt_handler(int irq, void *arg) A_PCI_WRITE32(sc->mem+(SOC_CORE_BASE_ADDRESS | PCIE_INTR_CLR_ADDRESS), PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); /* IMPORTANT: this extra read transaction is required to flush the posted write buffer */ tmp = A_PCI_READ32(sc->mem+(SOC_CORE_BASE_ADDRESS | PCIE_INTR_ENABLE_ADDRESS)); + + if (tmp == 0xdeadbeef) { + printk(KERN_ERR "BUG(%s): SoC returns 0xdeadbeef!!\n", __func__); + VOS_BUG(0); + } + + A_TARGET_ACCESS_END(targid); } /* TBDXXX: Add support for WMAC */ @@ -392,6 +405,8 @@ static void wlan_tasklet(unsigned long data) { struct hif_pci_softc *sc = (struct hif_pci_softc *) data; + struct HIF_CE_state *hif_state = (struct HIF_CE_state *)sc->hif_device; + A_target_id_t targid = hif_state->targid; volatile int tmp; if (sc->hif_init_done == FALSE) { @@ -412,11 +427,15 @@ wlan_tasklet(unsigned long data) } irq_handled: if (LEGACY_INTERRUPTS(sc)) { + A_TARGET_ACCESS_BEGIN(targid); + /* Enable Legacy PCI line interrupts */ A_PCI_WRITE32(sc->mem+(SOC_CORE_BASE_ADDRESS | PCIE_INTR_ENABLE_ADDRESS), PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); /* IMPORTANT: this extra read transaction is required to flush the posted write buffer */ tmp = A_PCI_READ32(sc->mem+(SOC_CORE_BASE_ADDRESS | PCIE_INTR_ENABLE_ADDRESS)); + + A_TARGET_ACCESS_END(targid); } } @@ -1371,6 +1390,8 @@ hif_pci_suspend(struct pci_dev *pdev, pm_message_t state) struct hif_pci_softc *sc = pci_get_drvdata(pdev); void *vos = vos_get_global_context(VOS_MODULE_ID_HIF, NULL); ol_txrx_pdev_handle txrx_pdev = vos_get_context(VOS_MODULE_ID_TXRX, vos); + struct HIF_CE_state *hif_state = (struct HIF_CE_state *)sc->hif_device; + A_target_id_t targid = hif_state->targid; u32 tx_drain_wait_cnt = 0; u32 val; v_VOID_t * temp_module; @@ -1381,18 +1402,9 @@ hif_pci_suspend(struct pci_dev *pdev, pm_message_t state) msleep(3*1000); /* 3 sec */ #endif -#if CONFIG_ATH_PCIE_MAX_PERF - /* Max performance path so no need to wake/poll target */ - A_PCI_WRITE32(sc->mem + FW_INDICATOR_ADDRESS, (state.event << 16)); -#else - /* Make sure to wake Target before accessing Target memory */ - A_PCI_WRITE32(sc->mem + PCIE_LOCAL_BASE_ADDRESS + PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_V_MASK); - while (!hif_pci_targ_is_awake(sc, sc->mem)) { - ; - } + A_TARGET_ACCESS_BEGIN(targid); A_PCI_WRITE32(sc->mem + FW_INDICATOR_ADDRESS, (state.event << 16)); - A_PCI_WRITE32(sc->mem + PCIE_LOCAL_BASE_ADDRESS + PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); -#endif + A_TARGET_ACCESS_END(targid); if (!txrx_pdev) { printk("%s: txrx_pdev is NULL\n", __func__); @@ -1439,6 +1451,8 @@ hif_pci_resume(struct pci_dev *pdev) { struct hif_pci_softc *sc = pci_get_drvdata(pdev); void *vos_context = vos_get_global_context(VOS_MODULE_ID_HIF, NULL); + struct HIF_CE_state *hif_state = (struct HIF_CE_state *)sc->hif_device; + A_target_id_t targid = hif_state->targid; u32 val; int err; v_VOID_t * temp_module; @@ -1464,18 +1478,9 @@ hif_pci_resume(struct pci_dev *pdev) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); } -#if CONFIG_ATH_PCIE_MAX_PERF - /* Max performance patch so no need to wake/poll target */ + A_TARGET_ACCESS_BEGIN(targid); val = A_PCI_READ32(sc->mem + FW_INDICATOR_ADDRESS) >> 16; -#else - /* Make sure to wake Target before accessing Target memory */ - A_PCI_WRITE32(sc->mem + PCIE_LOCAL_BASE_ADDRESS + PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_V_MASK); - while (!hif_pci_targ_is_awake(sc, sc->mem)) { - ; - } - val = A_PCI_READ32(sc->mem + FW_INDICATOR_ADDRESS) >> 16; - A_PCI_WRITE32(sc->mem + PCIE_LOCAL_BASE_ADDRESS + PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); -#endif + A_TARGET_ACCESS_END(targid); /* No need to send WMI_PDEV_RESUME_CMDID to FW if WOW is enabled */ temp_module = vos_get_context(VOS_MODULE_ID_WDA, vos_context); |
