From 4e43ba3d70caf703f444308b05796b3e47609fe7 Mon Sep 17 00:00:00 2001 From: Lin Bai Date: Fri, 5 Jun 2020 12:15:54 +0800 Subject: [PATCH] cnss2: Add synchronized force wake support Device is still possible to enter low power state with current unsync force wake method, and it may cause later failure when accessing SoC register. Introduce sync force wake API and expose it to WLAN host driver, which will ensure device won't enter low power state after device_wake is asserted. Change-Id: Iaa7a7bdac910c162600bbf03ac97af83b2f68f3f Signed-off-by: Lin Bai --- drivers/net/wireless/cnss2/pci.c | 50 +++++++++++++++++++++++--------- include/net/cnss2.h | 1 + 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 523e472ac0d9..b9450154b6dd 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -475,27 +475,22 @@ static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset, static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv) { struct device *dev = &pci_priv->pci_dev->dev; - u32 timeout = 0; int ret; - ret = cnss_pci_force_wake_request(dev); + ret = cnss_pci_force_wake_request_sync(dev, + FORCE_WAKE_DELAY_TIMEOUT_US); if (ret) { if (ret != -EAGAIN) cnss_pr_err("Failed to request force wake\n"); return ret; } - while (!cnss_pci_is_device_awake(dev) && - timeout <= FORCE_WAKE_DELAY_TIMEOUT_US) { - usleep_range(FORCE_WAKE_DELAY_MIN_US, FORCE_WAKE_DELAY_MAX_US); - timeout += FORCE_WAKE_DELAY_MAX_US; - } - - if (cnss_pci_is_device_awake(dev) != true) { - cnss_pr_err("Timed out to request force wake\n"); - cnss_pci_force_wake_release(dev); - return -ETIMEDOUT; - } + /* If device's M1 state-change event races here, it can be ignored, + * as the device is expected to immediately move from M2 to M0 + * without entering low power state. + */ + if (cnss_pci_is_device_awake(dev) != true) + cnss_pr_warn("MHI not in M0, while reg still accessible\n"); return 0; } @@ -2938,6 +2933,35 @@ int cnss_auto_resume(struct device *dev) } EXPORT_SYMBOL(cnss_auto_resume); +int cnss_pci_force_wake_request_sync(struct device *dev, int timeout_us) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; + struct mhi_controller *mhi_ctrl; + + if (!pci_priv) + return -ENODEV; + + if (pci_priv->device_id != QCA6390_DEVICE_ID && + pci_priv->device_id != QCA6490_DEVICE_ID) + return 0; + + mhi_ctrl = pci_priv->mhi_ctrl; + if (!mhi_ctrl) + return -EINVAL; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + return -EAGAIN; + + return mhi_device_get_sync_atomic(mhi_ctrl->mhi_dev, timeout_us); +} +EXPORT_SYMBOL(cnss_pci_force_wake_request_sync); + int cnss_pci_force_wake_request(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); diff --git a/include/net/cnss2.h b/include/net/cnss2.h index eb37edc647f5..ac786ced79ce 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -212,6 +212,7 @@ extern int cnss_wlan_pm_control(struct device *dev, bool vote); extern int cnss_auto_suspend(struct device *dev); extern int cnss_auto_resume(struct device *dev); extern int cnss_pci_is_drv_connected(struct device *dev); +extern int cnss_pci_force_wake_request_sync(struct device *dev, int timeout); extern int cnss_pci_force_wake_request(struct device *dev); extern int cnss_pci_is_device_awake(struct device *dev); extern int cnss_pci_force_wake_release(struct device *dev);