summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-09-02 13:52:29 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-09-02 13:52:29 -0700
commit40b4ea195d2f72c1b60e7d8923d32489ee9eb2ac (patch)
treec9f55f68afd70c34f8aa2b14fd9353658c0b3634
parente6fe53412e5c0b1dde9ce92d1d247cd4e2391a66 (diff)
parentfcf844fbdd18d5fd88ad4c4e7d40f1c15844f18b (diff)
Merge "cnss: Reset QCA Card during WLAN SubSystem Recovery"
-rw-r--r--Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt3
-rw-r--r--drivers/net/wireless/cnss/cnss_pci.c45
-rw-r--r--drivers/net/wireless/cnss/cnss_sdio.c212
3 files changed, 214 insertions, 46 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_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..01b969ec627f 100644
--- a/drivers/net/wireless/cnss/cnss_sdio.c
+++ b/drivers/net/wireless/cnss/cnss_sdio.c
@@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
#include <linux/io.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
@@ -46,7 +48,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"
@@ -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,19 +218,103 @@ 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;
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)
@@ -238,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;
}
@@ -551,25 +653,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 +695,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 +708,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 +814,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 +888,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 +1200,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 +1216,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 +1255,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 +1307,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;
}