summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSundar Subramaniyan <subrams@qti.qualcomm.com>2014-01-30 21:29:35 +0530
committerPrakash Dhavali <pdhavali@qca.qualcomm.com>2014-02-04 00:02:40 -0800
commitcc9365f1972d41bd100b8d244fa05444fea42786 (patch)
tree81773bef14d06e616a8899fb41b347b336f2d9cd
parent1467025969f05ef63c57a9e91540bd08365aaf3e (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.h3
-rw-r--r--CORE/SERVICES/COMMON/adf/linux/adf_os_lock_pvt.h23
-rw-r--r--CORE/SERVICES/HIF/PCIe/hif_pci.c19
-rw-r--r--CORE/SERVICES/HIF/PCIe/if_pci.c49
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);