Merge "cnss: Reset QCA Card during WLAN SubSystem Recovery"

This commit is contained in:
Linux Build Service Account 2016-09-02 13:52:29 -07:00 committed by Gerrit - the friendly Code Review server
commit 40b4ea195d
3 changed files with 214 additions and 46 deletions

View file

@ -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";

View file

@ -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,19 +2967,11 @@ 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);
ret = cnss_init_dump_entry();
if (ret) {
pr_err("%s: Dump table setup failed: %d\n",
__func__, 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,
penv->subsysdesc.dev);

View file

@ -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);
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) {
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,
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;
}