Merge "cnss: Reset QCA Card during WLAN SubSystem Recovery"
This commit is contained in:
commit
40b4ea195d
3 changed files with 214 additions and 46 deletions
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue