From 0d77d5e6655c53003fa10c45e332adabbb745bb2 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Fri, 12 Jun 2020 18:47:24 -0700 Subject: [PATCH 1/2] cnss2: Save default config space without BME enabled BME should be enabled after config space has been restored. Make sure that the default config space saved which is restored during PCIe link down recovery doesn't have BME enabled. Change-Id: I227d09620128cbb3232f63a026bc6eb789a0b70c Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/pci.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 1257b3ffe0ec..5efbba214790 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -3789,18 +3789,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: @@ -3813,12 +3816,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); @@ -4620,9 +4625,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, @@ -4708,8 +4710,6 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) 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); From 08b5c57b4faed81310e947080612bfba6979c918 Mon Sep 17 00:00:00 2001 From: Yue Ma Date: Mon, 2 Mar 2020 17:07:13 -0800 Subject: [PATCH 2/2] cnss2: Try to recover PCIe link to collect dump after link down Add the support to recover PCIe link after link down so that it can try to collect firmware RAM dump to debug. Change-Id: I227373b2285b3ea415a315ace7db907c477297e8 Signed-off-by: Yue Ma --- drivers/net/wireless/cnss2/bus.c | 15 ++++++++++ drivers/net/wireless/cnss2/bus.h | 1 + drivers/net/wireless/cnss2/main.c | 10 ++++++- drivers/net/wireless/cnss2/pci.c | 48 +++++++++++++++++++++++++++++-- drivers/net/wireless/cnss2/pci.h | 2 ++ 5 files changed, 72 insertions(+), 4 deletions(-) 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 aec49bf1478b..9d84607a564b 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); @@ -1067,6 +1067,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 5efbba214790..57ceee858961 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -47,6 +47,7 @@ #define FW_ASSERT_TIMEOUT 5000 #define DEV_RDDM_TIMEOUT 5000 +#define WAKE_EVENT_TIMEOUT 5000 #ifdef CONFIG_CNSS_EMULATION #define EMULATION_HW 1 @@ -827,6 +828,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); @@ -901,6 +938,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); @@ -2383,23 +2421,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); @@ -4641,6 +4681,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); @@ -4704,6 +4745,7 @@ 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: 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);