diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index 6525f8b0f307..be9047f7dbde 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -448,6 +448,21 @@ int cnss_bus_check_link_status(struct cnss_plat_data *plat_priv) } } +int cnss_bus_recover_link_down(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_recover_link_down(plat_priv->bus_priv); + default: + cnss_pr_dbg("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset, u32 *val) { diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index f7cd54ddebb6..82c463c34d29 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -50,6 +50,7 @@ int cnss_bus_update_status(struct cnss_plat_data *plat_priv, enum cnss_driver_status status); int cnss_bus_is_device_down(struct cnss_plat_data *plat_priv); int cnss_bus_check_link_status(struct cnss_plat_data *plat_priv); +int cnss_bus_recover_link_down(struct cnss_plat_data *plat_priv); int cnss_bus_debug_reg_read(struct cnss_plat_data *plat_priv, u32 offset, u32 *val); int cnss_bus_debug_reg_write(struct cnss_plat_data *plat_priv, u32 offset, diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 588fbea06e10..349c0a9917c6 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -732,7 +732,7 @@ int cnss_idle_shutdown(struct device *dev) reinit_completion(&plat_priv->recovery_complete); ret = wait_for_completion_timeout(&plat_priv->recovery_complete, - RECOVERY_TIMEOUT); + msecs_to_jiffies(RECOVERY_TIMEOUT)); if (!ret) { cnss_pr_err("Timeout waiting for recovery to complete\n"); CNSS_ASSERT(0); @@ -1083,6 +1083,14 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, if (test_bit(LINK_DOWN_SELF_RECOVERY, &plat_priv->ctrl_params.quirks)) goto self_recovery; + if (!cnss_bus_recover_link_down(plat_priv)) { + /* clear recovery bit here to avoid skipping + * the recovery work for RDDM later + */ + clear_bit(CNSS_DRIVER_RECOVERY, + &plat_priv->driver_state); + return 0; + } break; case CNSS_REASON_RDDM: cnss_bus_collect_dump_info(plat_priv, false); diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index fc8f65845901..e63791083de4 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -46,6 +46,7 @@ #define WAKE_MSI_NAME "WAKE" #define DEV_RDDM_TIMEOUT 5000 +#define WAKE_EVENT_TIMEOUT 5000 #ifdef CONFIG_CNSS_EMULATION #define EMULATION_HW 1 @@ -826,6 +827,42 @@ out: return ret; } +int cnss_pci_recover_link_down(struct cnss_pci_data *pci_priv) +{ + int ret; + + switch (pci_priv->device_id) { + case QCA6390_DEVICE_ID: + case QCA6490_DEVICE_ID: + break; + default: + return -EOPNOTSUPP; + } + + /* Always wait here to avoid missing WAKE assert for RDDM + * before link recovery + */ + msleep(WAKE_EVENT_TIMEOUT); + + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + ret = cnss_resume_pci_link(pci_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + del_timer(&pci_priv->dev_rddm_timer); + return ret; + } + + mod_timer(&pci_priv->dev_rddm_timer, + jiffies + msecs_to_jiffies(DEV_RDDM_TIMEOUT)); + + mhi_debug_reg_dump(pci_priv->mhi_ctrl); + + return 0; +} + int cnss_pci_prevent_l1(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -900,6 +937,7 @@ int cnss_pci_link_down(struct device *dev) pci_priv->pci_link_down_ind = true; spin_unlock_irqrestore(&pci_link_down_lock, flags); + reinit_completion(&pci_priv->wake_event); cnss_pr_err("PCI link down is detected, schedule recovery\n"); cnss_schedule_recovery(dev, CNSS_REASON_LINK_DOWN); @@ -2384,23 +2422,25 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify) case MSM_PCIE_EVENT_LINKDOWN: if (test_bit(ENABLE_PCI_LINK_DOWN_PANIC, &plat_priv->ctrl_params.quirks)) - panic("cnss: PCI link is down!\n"); + panic("cnss: PCI link is down\n"); spin_lock_irqsave(&pci_link_down_lock, flags); if (pci_priv->pci_link_down_ind) { - cnss_pr_dbg("PCI link down recovery is in progress, ignore!\n"); + cnss_pr_dbg("PCI link down recovery is in progress, ignore\n"); spin_unlock_irqrestore(&pci_link_down_lock, flags); return; } pci_priv->pci_link_down_ind = true; spin_unlock_irqrestore(&pci_link_down_lock, flags); - cnss_fatal_err("PCI link down, schedule recovery!\n"); + reinit_completion(&pci_priv->wake_event); + cnss_fatal_err("PCI link down, schedule recovery\n"); if (pci_dev->device == QCA6174_DEVICE_ID) disable_irq(pci_dev->irq); cnss_schedule_recovery(&pci_dev->dev, CNSS_REASON_LINK_DOWN); break; case MSM_PCIE_EVENT_WAKEUP: + complete(&pci_priv->wake_event); if (cnss_pci_get_monitor_wake_intr(pci_priv) && cnss_pci_get_auto_suspended(pci_priv)) { cnss_pci_set_monitor_wake_intr(pci_priv, false); @@ -3790,18 +3830,21 @@ static int cnss_pci_enable_bus(struct cnss_pci_data *pci_priv) goto release_region; } - pci_set_master(pci_dev); - pci_priv->bar = pci_iomap(pci_dev, PCI_BAR_NUM, 0); if (!pci_priv->bar) { cnss_pr_err("Failed to do PCI IO map!\n"); ret = -EIO; - goto clear_master; + goto release_region; } + + /* Save default config space without BME enabled */ + pci_save_state(pci_dev); + pci_priv->default_state = pci_store_saved_state(pci_dev); + + pci_set_master(pci_dev); + return 0; -clear_master: - pci_clear_master(pci_dev); release_region: pci_release_region(pci_dev, PCI_BAR_NUM); disable_device: @@ -3814,12 +3857,14 @@ static void cnss_pci_disable_bus(struct cnss_pci_data *pci_priv) { struct pci_dev *pci_dev = pci_priv->pci_dev; + pci_clear_master(pci_dev); + pci_load_and_free_saved_state(pci_dev, &pci_priv->saved_state); + if (pci_priv->bar) { pci_iounmap(pci_dev, pci_priv->bar); pci_priv->bar = NULL; } - pci_clear_master(pci_dev); pci_release_region(pci_dev, PCI_BAR_NUM); if (pci_is_enabled(pci_dev)) pci_disable_device(pci_dev); @@ -4621,9 +4666,6 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, if (ret) goto dereg_pci_event; - pci_save_state(pci_dev); - pci_priv->default_state = pci_store_saved_state(pci_dev); - switch (pci_dev->device) { case QCA6174_DEVICE_ID: pci_read_config_word(pci_dev, QCA6174_REV_ID_OFFSET, @@ -4640,6 +4682,7 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false); timer_setup(&pci_priv->dev_rddm_timer, cnss_dev_rddm_timeout_hdlr, 0); + init_completion(&pci_priv->wake_event); INIT_DELAYED_WORK(&pci_priv->time_sync_work, cnss_pci_time_sync_work_hdlr); @@ -4703,14 +4746,13 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) case QCA6490_DEVICE_ID: cnss_pci_unregister_mhi(pci_priv); cnss_pci_disable_msi(pci_priv); + complete_all(&pci_priv->wake_event); del_timer(&pci_priv->dev_rddm_timer); break; default: break; } - pci_load_and_free_saved_state(pci_dev, &pci_priv->saved_state); - cnss_pci_disable_bus(pci_priv); cnss_dereg_pci_event(pci_priv); cnss_pci_deinit_smmu(pci_priv); diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index a1e1a67f9858..c53036104ad7 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -76,6 +76,7 @@ struct cnss_pci_data { u8 drv_connected_last; u16 def_link_speed; u16 def_link_width; + struct completion wake_event; u8 monitor_wake_intr; struct iommu_domain *iommu_domain; u8 smmu_s1_enable; @@ -164,6 +165,7 @@ static inline int cnss_pci_get_drv_connected(void *bus_priv) int cnss_pci_check_link_status(struct cnss_pci_data *pci_priv); int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv); int cnss_resume_pci_link(struct cnss_pci_data *pci_priv); +int cnss_pci_recover_link_down(struct cnss_pci_data *pci_priv); int cnss_pci_init(struct cnss_plat_data *plat_priv); void cnss_pci_deinit(struct cnss_plat_data *plat_priv); int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv);