Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa2b8af8 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 7f09ba00 38fbb271
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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";
+185 −25
Original line number Diff line number Diff line
@@ -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>
@@ -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;
}