Loading Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt +2 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading drivers/net/wireless/cnss/cnss_sdio.c +185 −25 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 { Loading Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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) Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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( Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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 Loading
Documentation/devicetree/bindings/cnss/cnss-sdio-wlan.txt +2 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading
drivers/net/wireless/cnss/cnss_sdio.c +185 −25 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 { Loading Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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) Loading @@ -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) Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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( Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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