ath10k: make warm reset a bit safer and faster
authorMichal Kazior <michal.kazior@tieto.com>
Tue, 28 Oct 2014 09:32:06 +0000 (10:32 +0100)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 31 Oct 2014 00:26:59 +0000 (02:26 +0200)
One of the problems with warm reset I've found is
that it must be guaranteed that copy engine
registers are not being accessed while being
reset. Otherwise in worst case scenario the host
may lock up.

Instead of using sleeps and hoping the device is
operational in some arbitrary timeframes use
firmware indication register.

As a side effect this makes driver
boot/stop/recovery faster.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/pci.c

index a8a3e1bcf2d547490aae386ac9f6c92115fc3be7..af36730e95dfe7256a819f811264f3cf87b2e4f4 100644 (file)
@@ -1717,89 +1717,75 @@ static void ath10k_pci_warm_reset_si0(struct ath10k *ar)
        msleep(10);
 }
 
-static int ath10k_pci_warm_reset(struct ath10k *ar)
+static void ath10k_pci_warm_reset_cpu(struct ath10k *ar)
 {
        u32 val;
 
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
-
-       spin_lock_bh(&ar->data_lock);
-
-       ar->stats.fw_warm_reset_counter++;
-
-       spin_unlock_bh(&ar->data_lock);
-
-       /* debug */
-       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
-                               PCIE_INTR_CAUSE_ADDRESS);
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
-                  val);
-
-       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
-                               CPU_INTR_ADDRESS);
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
-                  val);
-
-       /* disable pending irqs */
-       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
-                          PCIE_INTR_ENABLE_ADDRESS, 0);
-
-       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
-                          PCIE_INTR_CLR_ADDRESS, ~0);
-
-       msleep(100);
-
-       /* clear fw indicator */
        ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
 
-       /* clear target LF timer interrupts */
        val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
-                               SOC_LF_TIMER_CONTROL0_ADDRESS);
-       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
-                          SOC_LF_TIMER_CONTROL0_ADDRESS,
-                          val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+                               SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+                          val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+}
+
+static void ath10k_pci_warm_reset_ce(struct ath10k *ar)
+{
+       u32 val;
 
-       /* reset CE */
        val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
                                SOC_RESET_CONTROL_ADDRESS);
+
        ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
                           val | SOC_RESET_CONTROL_CE_RST_MASK);
-       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
-                               SOC_RESET_CONTROL_ADDRESS);
        msleep(10);
-
-       /* unreset CE */
        ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
                           val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+}
+
+static void ath10k_pci_warm_reset_clear_lf(struct ath10k *ar)
+{
+       u32 val;
+
        val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
-                               SOC_RESET_CONTROL_ADDRESS);
-       msleep(10);
+                               SOC_LF_TIMER_CONTROL0_ADDRESS);
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
+                          SOC_LF_TIMER_CONTROL0_ADDRESS,
+                          val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+}
 
-       ath10k_pci_warm_reset_si0(ar);
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
 
-       /* debug */
-       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
-                               PCIE_INTR_CAUSE_ADDRESS);
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
-                  val);
+       spin_lock_bh(&ar->data_lock);
+       ar->stats.fw_warm_reset_counter++;
+       spin_unlock_bh(&ar->data_lock);
 
-       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
-                               CPU_INTR_ADDRESS);
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
-                  val);
+       ath10k_pci_irq_disable(ar);
 
-       /* CPU warm reset */
-       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
-                               SOC_RESET_CONTROL_ADDRESS);
-       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
-                          val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+       /* Make sure the target CPU is not doing anything dangerous, e.g. if it
+        * were to access copy engine while host performs copy engine reset
+        * then it is possible for the device to confuse pci-e controller to
+        * the point of bringing host system to a complete stop (i.e. hang).
+        */
+       ath10k_pci_warm_reset_si0(ar);
+       ath10k_pci_warm_reset_cpu(ar);
+       ath10k_pci_init_pipes(ar);
+       ath10k_pci_wait_for_target_init(ar);
 
-       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
-                               SOC_RESET_CONTROL_ADDRESS);
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n",
-                  val);
+       ath10k_pci_warm_reset_clear_lf(ar);
+       ath10k_pci_warm_reset_ce(ar);
+       ath10k_pci_warm_reset_cpu(ar);
+       ath10k_pci_init_pipes(ar);
 
-       msleep(100);
+       ret = ath10k_pci_wait_for_target_init(ar);
+       if (ret) {
+               ath10k_warn(ar, "failed to wait for target init: %d\n", ret);
+               return ret;
+       }
 
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n");