From 0e0c3c54aaeec4441b1e92c6ac1984868498d9a9 Mon Sep 17 00:00:00 2001 From: "Kumar, Anand" Date: Fri, 1 Jul 2016 12:37:33 +0530 Subject: [PATCH 1/3] cnss: Use Different Dump Names for QCA PCIe and SDIO targets On Dual-WiFi platforms, SDIO and PCIe dumps are overwritten by the crashscope, as they use the same dump name. Use different dump names to distinguish the dump between SDIO and PCIe. CRs-Fixed: 1037346 Change-Id: I59c0ee5d3c5f206ae09d6b813f1e5c1ce1434cc1 Signed-off-by: Anand Kumar --- drivers/net/wireless/cnss/cnss_pci.c | 45 ++++++++++++++++----------- drivers/net/wireless/cnss/cnss_sdio.c | 2 +- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 801c94859084..5d9329168699 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -2761,14 +2761,8 @@ static void cnss_crash_shutdown(const struct subsys_desc *subsys) wdrv = penv->driver; pdev = penv->pdev; - penv->dump_data.version = CNSS_DUMP_FORMAT_VER; - strlcpy(penv->dump_data.name, CNSS_DUMP_NAME, - sizeof(penv->dump_data.name)); - if (pdev && wdrv && wdrv->crash_shutdown) wdrv->crash_shutdown(pdev); - - penv->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2; } void cnss_device_self_recovery(void) @@ -2829,6 +2823,28 @@ static struct notifier_block mnb = { .notifier_call = cnss_modem_notifier_nb, }; +static int cnss_init_dump_entry(void) +{ + struct msm_dump_entry dump_entry; + + if (!penv) + return -ENODEV; + + if (!penv->ramdump_dynamic) + return 0; + + penv->dump_data.addr = penv->ramdump_phys; + penv->dump_data.len = penv->ramdump_size; + penv->dump_data.version = CNSS_DUMP_FORMAT_VER; + penv->dump_data.magic = CNSS_DUMP_MAGIC_VER_V2; + strlcpy(penv->dump_data.name, CNSS_DUMP_NAME, + sizeof(penv->dump_data.name)); + dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN; + dump_entry.addr = virt_to_phys(&penv->dump_data); + + return msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); +} + static int cnss_probe(struct platform_device *pdev) { int ret = 0; @@ -2836,7 +2852,6 @@ static int cnss_probe(struct platform_device *pdev) const char *client_desc; struct device *dev = &pdev->dev; u32 rc_num; - struct msm_dump_entry dump_entry; struct resource *res; u32 ramdump_size = 0; u32 smmu_iova_address[2]; @@ -2952,18 +2967,10 @@ static int cnss_probe(struct platform_device *pdev) goto skip_ramdump; } - if (penv->ramdump_dynamic) { - penv->dump_data.addr = penv->ramdump_phys; - penv->dump_data.len = penv->ramdump_size; - dump_entry.id = MSM_DUMP_DATA_CNSS_WLAN; - dump_entry.addr = virt_to_phys(&penv->dump_data); - - ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); - if (ret) { - pr_err("%s: Dump table setup failed: %d\n", - __func__, ret); - goto err_ramdump_create; - } + ret = cnss_init_dump_entry(); + if (ret) { + pr_err("%s: Dump table setup failed: %d\n", __func__, ret); + goto err_ramdump_create; } penv->ramdump_dev = create_ramdump_device(penv->subsysdesc.name, diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index f773c5993d44..6ad0c7065f7a 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -46,7 +46,7 @@ /* Values for Dynamic Ramdump Collection*/ #define CNSS_DUMP_FORMAT_VER 0x11 #define CNSS_DUMP_MAGIC_VER_V2 0x42445953 -#define CNSS_DUMP_NAME "CNSS_WLAN" +#define CNSS_DUMP_NAME "CNSS_WLAN_SDIO" #define CNSS_PINCTRL_SLEEP_STATE "sleep" #define CNSS_PINCTRL_ACTIVE_STATE "active" From 9bb7d6728fd1956bb3e3328d6f6dabfee0f57ae7 Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Tue, 26 Jul 2016 16:58:00 +0530 Subject: [PATCH 2/3] net: cnss: Release QCA chip resources when Wi-Fi is turned off When WiFi is turned off from userspace, save power by toggling WLAN_EN gpio and restore power when wifi is loaded again. CRs-Fixed: 1058794 Change-Id: I0257698d9d168d7c889436a05693061cafe5ea7c Signed-off-by: Komal Seelam --- .../bindings/cnss/cnss-sdio-wlan.txt | 3 +- drivers/net/wireless/cnss/cnss_sdio.c | 174 ++++++++++++++++-- 2 files changed, 158 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt b/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt index 72bbb6180258..187c5604b521 100644 --- a/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt +++ b/Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt @@ -29,7 +29,8 @@ Optional properties: - qcom,msm-bus,num-cases: number of cases for bus scaling. - qcom,msm-bus,num-paths: number of paths for bus scale vector. - qcom,msm-bus,vectors-KBps: bus scale vector table. - + - qcom,skip-wlan-en-toggle: Boolean property to be enabled for platforms where + wlan_en toggling is not supported. Example: qcom,cnss-sdio { compatible = "qcom,cnss_sdio"; diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index 6ad0c7065f7a..cd8b8538bc96 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -60,7 +62,11 @@ struct cnss_sdio_regulator { struct cnss_sdio_info { struct cnss_sdio_wlan_driver *wdrv; struct sdio_func *func; + struct mmc_card *card; + struct mmc_host *host; + struct device *dev; const struct sdio_device_id *id; + bool skip_wlan_en_toggle; }; struct cnss_ssr_info { @@ -212,6 +218,80 @@ void cnss_sdio_remove_pm_qos(void) } EXPORT_SYMBOL(cnss_sdio_remove_pm_qos); +static int cnss_put_hw_resources(struct device *dev) +{ + int ret = -EINVAL; + struct cnss_sdio_info *info; + struct mmc_host *host; + + if (!cnss_pdata) + return ret; + + info = &cnss_pdata->cnss_sdio_info; + + if (info->skip_wlan_en_toggle) { + pr_debug("%s: HW doesn't support wlan toggling\n", __func__); + return 0; + } + + host = info->host; + + if (!host) { + pr_err("%s: MMC host is invalid\n", __func__); + return 0; + } + + ret = mmc_power_save_host(host); + if (ret) { + pr_err("%s: Failed to Power Save Host err:%d\n", __func__, + ret); + return ret; + } + + if (!cnss_pdata->regulator.wlan_vreg) { + pr_debug("%s: wlan_vreg regulator is invalid\n", __func__); + return ret; + } + + regulator_disable(cnss_pdata->regulator.wlan_vreg); + + return ret; +} + +static int cnss_get_hw_resources(struct device *dev) +{ + int ret = -EINVAL; + struct mmc_host *host; + struct cnss_sdio_info *info; + + if (!cnss_pdata) + return ret; + + info = &cnss_pdata->cnss_sdio_info; + + if (info->skip_wlan_en_toggle) { + pr_debug("%s: HW doesn't support wlan toggling\n", __func__); + return 0; + } + + host = info->host; + + ret = regulator_enable(cnss_pdata->regulator.wlan_vreg); + if (ret) { + pr_err("%s: Failed to enable wlan vreg\n", __func__); + return ret; + } + + ret = mmc_power_restore_host(host); + if (ret) { + pr_err("%s: Failed to restore host power ret:%d\n", __func__, + ret); + regulator_disable(cnss_pdata->regulator.wlan_vreg); + } + + return ret; +} + static int cnss_sdio_shutdown(const struct subsys_desc *subsys, bool force_stop) { struct cnss_sdio_info *cnss_info; @@ -551,25 +631,41 @@ int cnss_get_restart_level(void) } EXPORT_SYMBOL(cnss_get_restart_level); -static int cnss_sdio_wlan_inserted( - struct sdio_func *func, - const struct sdio_device_id *id) +static int cnss_sdio_wlan_inserted(struct sdio_func *func, + const struct sdio_device_id *id) { + struct cnss_sdio_info *info; + if (!cnss_pdata) return -ENODEV; - cnss_pdata->cnss_sdio_info.func = func; - cnss_pdata->cnss_sdio_info.id = id; + info = &cnss_pdata->cnss_sdio_info; + + info->func = func; + info->card = func->card; + info->host = func->card->host; + info->id = id; + info->dev = &func->dev; + + cnss_put_hw_resources(cnss_pdata->cnss_sdio_info.dev); + + pr_info("%s: SDIO Device is Probed\n", __func__); return 0; } static void cnss_sdio_wlan_removed(struct sdio_func *func) { + struct cnss_sdio_info *info; + if (!cnss_pdata) return; - cnss_pdata->cnss_sdio_info.func = NULL; - cnss_pdata->cnss_sdio_info.id = NULL; + info = &cnss_pdata->cnss_sdio_info; + + info->host = NULL; + info->card = NULL; + info->func = NULL; + info->id = NULL; } #if defined(CONFIG_PM) @@ -577,6 +673,8 @@ static int cnss_sdio_wlan_suspend(struct device *dev) { struct cnss_sdio_wlan_driver *wdrv; struct cnss_sdio_bus_bandwidth *bus_bandwidth; + struct sdio_func *func; + int error = 0; if (!cnss_pdata) @@ -588,11 +686,13 @@ static int cnss_sdio_wlan_suspend(struct device *dev) bus_bandwidth->bus_client, CNSS_BUS_WIDTH_NONE); } + func = cnss_pdata->cnss_sdio_info.func; wdrv = cnss_pdata->cnss_sdio_info.wdrv; if (!wdrv) { /* This can happen when no wlan driver loaded (no register to * platform driver). */ + sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); pr_debug("wlan driver not registered\n"); return 0; } @@ -692,29 +792,49 @@ EXPORT_SYMBOL(cnss_sdio_configure_spdt); int cnss_sdio_wlan_register_driver(struct cnss_sdio_wlan_driver *driver) { struct cnss_sdio_info *cnss_info; - int error = 0; + struct device *dev; + int error = -EINVAL; if (!cnss_pdata) return -ENODEV; cnss_info = &cnss_pdata->cnss_sdio_info; + dev = cnss_info->dev; + if (cnss_info->wdrv) pr_debug("%s:wdrv already exists wdrv(%p)\n", __func__, cnss_info->wdrv); + cnss_info->wdrv = driver; + + if (!driver) + return error; + + error = cnss_get_hw_resources(dev); + if (error) { + pr_err("%s: Failed to restore power err:%d\n", __func__, error); + return error; + } + error = cnss_set_pinctrl_state(cnss_pdata, PINCTRL_ACTIVE); if (error) { pr_err("%s: Fail to set pinctrl to active state\n", __func__); - return -EFAULT; + goto put_hw; } - cnss_info->wdrv = driver; - if (driver->probe) { - error = driver->probe(cnss_info->func, cnss_info->id); - if (error) - pr_err("%s: wlan probe failed error=%d\n", __func__, - error); + error = driver->probe ? driver->probe(cnss_info->func, + cnss_info->id) : error; + if (error) { + pr_err("%s: wlan probe failed error=%d\n", __func__, error); + goto pinctrl_sleep; } + + return error; + +pinctrl_sleep: + cnss_set_pinctrl_state(cnss_pdata, PINCTRL_SLEEP); +put_hw: + cnss_put_hw_resources(dev); return error; } EXPORT_SYMBOL(cnss_sdio_wlan_register_driver); @@ -746,10 +866,17 @@ cnss_sdio_wlan_unregister_driver(struct cnss_sdio_wlan_driver *driver) pr_err("%s: driver not registered\n", __func__); return; } - if (cnss_info->wdrv->remove) - cnss_info->wdrv->remove(cnss_info->func); + + if (!driver) + return; + + if (!driver->remove) + return; + + driver->remove(cnss_info->func); cnss_info->wdrv = NULL; cnss_set_pinctrl_state(cnss_pdata, PINCTRL_SLEEP); + cnss_put_hw_resources(cnss_info->dev); } EXPORT_SYMBOL(cnss_sdio_wlan_unregister_driver); @@ -1051,6 +1178,8 @@ static int cnss_sdio_init_bus_bandwidth(void) static int cnss_sdio_probe(struct platform_device *pdev) { int error; + struct device *dev = &pdev->dev; + struct cnss_sdio_info *info; if (pdev->dev.of_node) { cnss_pdata = devm_kzalloc( @@ -1065,6 +1194,7 @@ static int cnss_sdio_probe(struct platform_device *pdev) return -EINVAL; cnss_pdata->pdev = pdev; + info = &cnss_pdata->cnss_sdio_info; error = cnss_sdio_pinctrl_init(cnss_pdata, pdev); if (error) { @@ -1103,6 +1233,9 @@ static int cnss_sdio_probe(struct platform_device *pdev) } } + info->skip_wlan_en_toggle = of_property_read_bool(dev->of_node, + "qcom,skip-wlan-en-toggle"); + error = cnss_sdio_wlan_init(); if (error) { dev_err(&pdev->dev, "cnss wlan init failed error=%d\n", error); @@ -1152,15 +1285,20 @@ err_wlan_enable_regulator: static int cnss_sdio_remove(struct platform_device *pdev) { + struct cnss_sdio_info *info; + if (!cnss_pdata) return -ENODEV; + info = &cnss_pdata->cnss_sdio_info; + cnss_sdio_deinit_bus_bandwidth(); cnss_sdio_wlan_exit(); cnss_subsys_exit(); cnss_ramdump_cleanup(); + cnss_put_hw_resources(info->dev); cnss_sdio_release_resource(); - + cnss_pdata = NULL; return 0; } From fcf844fbdd18d5fd88ad4c4e7d40f1c15844f18b Mon Sep 17 00:00:00 2001 From: Komal Seelam Date: Tue, 26 Jul 2016 16:58:01 +0530 Subject: [PATCH 3/3] cnss: Reset QCA Card during WLAN SubSystem Recovery During SSR, we observe cases where card enter bad state and does not responding to any I/O commands. Fix it by powering off and on the card. Change-Id: Ia3ab2781c93d1cfe7427e2b1c2644f6628f6465f CRs-Fixed: 1058794 Signed-off-by: Komal Seelam --- drivers/net/wireless/cnss/cnss_sdio.c | 36 +++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/cnss/cnss_sdio.c b/drivers/net/wireless/cnss/cnss_sdio.c index cd8b8538bc96..01b969ec627f 100644 --- a/drivers/net/wireless/cnss/cnss_sdio.c +++ b/drivers/net/wireless/cnss/cnss_sdio.c @@ -296,15 +296,25 @@ static int cnss_sdio_shutdown(const struct subsys_desc *subsys, bool force_stop) { struct cnss_sdio_info *cnss_info; struct cnss_sdio_wlan_driver *wdrv; + int ret = 0; if (!cnss_pdata) return -ENODEV; cnss_info = &cnss_pdata->cnss_sdio_info; wdrv = cnss_info->wdrv; - if (wdrv && wdrv->shutdown) - wdrv->shutdown(cnss_info->func); - return 0; + if (!wdrv) + return 0; + if (!wdrv->shutdown) + return 0; + + wdrv->shutdown(cnss_info->func); + ret = cnss_put_hw_resources(cnss_info->dev); + + if (ret) + pr_err("%s: Failed to put hw resources\n", __func__); + + return ret; } static int cnss_sdio_powerup(const struct subsys_desc *subsys) @@ -318,11 +328,23 @@ static int cnss_sdio_powerup(const struct subsys_desc *subsys) cnss_info = &cnss_pdata->cnss_sdio_info; wdrv = cnss_info->wdrv; - if (wdrv && wdrv->reinit) { - ret = wdrv->reinit(cnss_info->func, cnss_info->id); - if (ret) - pr_err("%s: wlan reinit error=%d\n", __func__, ret); + + if (!wdrv) + return 0; + + if (!wdrv->reinit) + return 0; + + ret = cnss_get_hw_resources(cnss_info->dev); + if (ret) { + pr_err("%s: Failed to power up HW\n", __func__); + return ret; } + + ret = wdrv->reinit(cnss_info->func, cnss_info->id); + if (ret) + pr_err("%s: wlan reinit error=%d\n", __func__, ret); + return ret; }