ath10k: make warm reset a bit safer and faster
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>
This commit is contained in:
parent
099ac7ce2e
commit
61c1648bd7
@ -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_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;
|
||||
|
||||
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);
|
||||
msleep(10);
|
||||
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_LF_TIMER_CONTROL0_ADDRESS);
|
||||
ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
|
||||
SOC_LF_TIMER_CONTROL0_ADDRESS,
|
||||
val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
static int ath10k_pci_warm_reset(struct ath10k *ar)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* unreset CE */
|
||||
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);
|
||||
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);
|
||||
|
||||
ath10k_pci_irq_disable(ar);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
ath10k_pci_warm_reset_clear_lf(ar);
|
||||
ath10k_pci_warm_reset_ce(ar);
|
||||
ath10k_pci_warm_reset_cpu(ar);
|
||||
ath10k_pci_init_pipes(ar);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user