From 9b372ef962d4b1d37c5767b060e30d6c93d49ebc Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Sun, 14 May 2017 16:40:19 +0300 Subject: [PATCH] msm_11ad: add support to PCIe D3hot in system suspend Transition to D3 hot in system suspend allows the wil6210 device to preserve the active connections in system suspend. Change-Id: I4c24551f91ee7e59d4bfee02b0911c31ae0a05b1 Signed-off-by: Maya Erez --- .../devicetree/bindings/arm/msm/wil6210.txt | 3 + drivers/platform/msm/msm_11ad/msm_11ad.c | 244 ++++++++++++------ 2 files changed, 175 insertions(+), 72 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt index c4673279953d..54bbf2535340 100644 --- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt +++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt @@ -32,6 +32,8 @@ Optional properties: - clocks : List of phandle and clock specifier pairs - clock-names : List of clock input name strings sorted in the same order as the clocks property. +- qcom,keep_radio_on_during_sleep: Boolean flag to indicate if to suspend to d3hot + instead of turning off the device Example: wil6210: qcom,wil6210 { @@ -56,5 +58,6 @@ Example: clocks = <&clock_gcc clk_rf_clk3>, <&clock_gcc clk_rf_clk3_pin>; clock-names = "rf_clk3_clk", "rf_clk3_pin_clk"; + qcom,keep_radio_on_during_sleep; }; diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c index f00cc69aa0d5..b1989f8741f5 100644 --- a/drivers/platform/msm/msm_11ad/msm_11ad.c +++ b/drivers/platform/msm/msm_11ad/msm_11ad.c @@ -40,9 +40,6 @@ #define SMMU_SIZE ((SZ_1G * 4ULL) - SMMU_BASE) #define WIGIG_ENABLE_DELAY 50 -#define PM_OPT_SUSPEND (MSM_PCIE_CONFIG_NO_CFG_RESTORE | \ - MSM_PCIE_CONFIG_LINKDOWN) -#define PM_OPT_RESUME MSM_PCIE_CONFIG_NO_CFG_RESTORE #define WIGIG_SUBSYS_NAME "WIGIG" #define WIGIG_RAMDUMP_SIZE 0x200000 /* maximum ramdump size */ @@ -127,6 +124,8 @@ struct msm11ad_ctx { bool use_cpu_boost; bool is_cpu_boosted; struct cpumask boost_cpu; + + bool keep_radio_on_during_sleep; }; static LIST_HEAD(dev_list); @@ -523,30 +522,8 @@ int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable) return rc; } -static int ops_suspend(void *handle, bool keep_device_power) +static int msm_11ad_turn_device_power_off(struct msm11ad_ctx *ctx) { - int rc; - struct msm11ad_ctx *ctx = handle; - struct pci_dev *pcidev; - - pr_info("%s(%p)\n", __func__, handle); - if (!ctx) { - pr_err("No context\n"); - return -ENODEV; - } - pcidev = ctx->pcidev; - rc = pci_save_state(pcidev); - if (rc) { - dev_err(ctx->dev, "pci_save_state failed :%d\n", rc); - return rc; - } - rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number, - pcidev, NULL, PM_OPT_SUSPEND); - if (rc) { - dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n", - rc); - return rc; - } if (ctx->gpio_en >= 0) gpio_direction_output(ctx->gpio_en, 0); @@ -557,20 +534,12 @@ static int ops_suspend(void *handle, bool keep_device_power) msm_11ad_disable_vregs(ctx); - return rc; + return 0; } -static int ops_resume(void *handle, bool device_powered_on) +static int msm_11ad_turn_device_power_on(struct msm11ad_ctx *ctx) { int rc; - struct msm11ad_ctx *ctx = handle; - struct pci_dev *pcidev; - - pr_info("%s(%p)\n", __func__, handle); - if (!ctx) { - pr_err("No context\n"); - return -ENODEV; - } rc = msm_11ad_enable_vregs(ctx); if (rc) { @@ -588,25 +557,124 @@ static int ops_resume(void *handle, bool device_powered_on) if (ctx->sleep_clk_en >= 0) gpio_direction_output(ctx->sleep_clk_en, 1); - pcidev = ctx->pcidev; if (ctx->gpio_en >= 0) { gpio_direction_output(ctx->gpio_en, 1); msleep(WIGIG_ENABLE_DELAY); } + return 0; + +err_disable_vregs: + msm_11ad_disable_vregs(ctx); + return rc; +} + +static int msm_11ad_suspend_power_off(void *handle) +{ + int rc; + struct msm11ad_ctx *ctx = handle; + struct pci_dev *pcidev; + + pr_debug("%s\n", __func__); + + if (!ctx) { + pr_err("%s: No context\n", __func__); + return -ENODEV; + } + + pcidev = ctx->pcidev; + + msm_pcie_shadow_control(ctx->pcidev, 0); + + rc = pci_save_state(pcidev); + if (rc) { + dev_err(ctx->dev, "pci_save_state failed :%d\n", rc); + goto out; + } + ctx->pristine_state = pci_store_saved_state(pcidev); + + rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number, + pcidev, NULL, 0); + if (rc) { + dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n", + rc); + goto out; + } + + rc = msm_11ad_turn_device_power_off(ctx); + +out: + return rc; +} + +static int ops_suspend(void *handle, bool keep_device_power) +{ + struct msm11ad_ctx *ctx = handle; + struct pci_dev *pcidev; + int rc; + + pr_debug("11ad suspend: %s\n", __func__); + if (!ctx) { + pr_err("11ad suspend: No context\n"); + return -ENODEV; + } + + if (!keep_device_power) + return msm_11ad_suspend_power_off(handle); + + pcidev = ctx->pcidev; + + msm_pcie_shadow_control(pcidev, 0); + + dev_dbg(ctx->dev, "disable device and save config\n"); + pci_disable_device(pcidev); + pci_save_state(pcidev); + ctx->pristine_state = pci_store_saved_state(pcidev); + dev_dbg(ctx->dev, "moving to D3\n"); + pci_set_power_state(pcidev, PCI_D3hot); + + rc = msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number, + pcidev, NULL, 0); + if (rc) + dev_err(ctx->dev, "msm_pcie_pm_control(SUSPEND) failed :%d\n", + rc); + + return rc; +} + +static int msm_11ad_resume_power_on(void *handle) +{ + int rc; + struct msm11ad_ctx *ctx = handle; + struct pci_dev *pcidev; + + pr_debug("%s\n", __func__); + + if (!ctx) { + pr_err("%s: No context\n", __func__); + return -ENODEV; + } + pcidev = ctx->pcidev; + + rc = msm_11ad_turn_device_power_on(ctx); + if (rc) + return rc; + rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number, - pcidev, NULL, PM_OPT_RESUME); + pcidev, NULL, 0); if (rc) { dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n", rc); goto err_disable_power; } - rc = msm_pcie_recover_config(pcidev); - if (rc) { - dev_err(ctx->dev, "msm_pcie_recover_config failed :%d\n", - rc); - goto err_suspend_rc; - } + + pci_set_power_state(pcidev, PCI_D0); + + if (ctx->pristine_state) + pci_load_saved_state(ctx->pcidev, ctx->pristine_state); + pci_restore_state(ctx->pcidev); + + msm_pcie_shadow_control(ctx->pcidev, 1); /* Disable L1, in case it is enabled */ if (ctx->l1_enabled_in_enum) { @@ -622,18 +690,54 @@ static int ops_resume(void *handle, bool device_powered_on) err_suspend_rc: msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number, - pcidev, NULL, PM_OPT_SUSPEND); + pcidev, NULL, 0); err_disable_power: - if (ctx->gpio_en >= 0) - gpio_direction_output(ctx->gpio_en, 0); + msm_11ad_turn_device_power_off(ctx); + return rc; +} - if (ctx->sleep_clk_en >= 0) - gpio_direction_output(ctx->sleep_clk_en, 0); +static int ops_resume(void *handle, bool device_powered_on) +{ + struct msm11ad_ctx *ctx = handle; + struct pci_dev *pcidev; + int rc; - msm_11ad_disable_clocks(ctx); -err_disable_vregs: - msm_11ad_disable_vregs(ctx); + pr_debug("11ad resume: %s\n", __func__); + if (!ctx) { + pr_err("11ad resume: No context\n"); + return -ENODEV; + } + pcidev = ctx->pcidev; + + if (!device_powered_on) + return msm_11ad_resume_power_on(handle); + + rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number, + pcidev, NULL, 0); + if (rc) { + dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed :%d\n", + rc); + return rc; + } + pci_set_power_state(pcidev, PCI_D0); + + dev_dbg(ctx->dev, "restore state and enable device\n"); + pci_load_saved_state(pcidev, ctx->pristine_state); + pci_restore_state(pcidev); + + rc = pci_enable_device(pcidev); + if (rc) { + dev_err(ctx->dev, "pci_enable_device failed (%d)\n", rc); + goto out; + } + + msm_pcie_shadow_control(pcidev, 1); + + dev_dbg(ctx->dev, "pci set master\n"); + pci_set_master(pcidev); + +out: return rc; } @@ -993,6 +1097,8 @@ static int msm_11ad_probe(struct platform_device *pdev) return -EINVAL; } ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support"); + ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node, + "qcom,keep_radio_on_during_sleep"); ctx->bus_scale = msm_bus_cl_get_pdata(pdev); ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en"); @@ -1105,13 +1211,6 @@ static int msm_11ad_probe(struct platform_device *pdev) } } - rc = pci_save_state(pcidev); - if (rc) { - dev_err(ctx->dev, "pci_save_state failed :%d\n", rc); - goto out_rc; - } - ctx->pristine_state = pci_store_saved_state(pcidev); - if (ctx->sleep_clk_en >= 0) { rc = gpio_request(ctx->sleep_clk_en, "msm_11ad"); if (rc < 0) { @@ -1147,7 +1246,7 @@ static int msm_11ad_probe(struct platform_device *pdev) device_disable_async_suspend(&pcidev->dev); list_add_tail(&ctx->list, &dev_list); - ops_suspend(ctx, false); + msm_11ad_suspend_power_off(ctx); return 0; out_rc: @@ -1315,7 +1414,7 @@ static void ops_uninit(void *handle) memset(&ctx->rops, 0, sizeof(ctx->rops)); ctx->wil_handle = NULL; - ops_suspend(ctx, false); + msm_11ad_suspend_power_off(ctx); } static int msm_11ad_notify_crash(struct msm11ad_ctx *ctx) @@ -1373,6 +1472,16 @@ static int ops_notify(void *handle, enum wil_platform_event evt) return rc; } +bool ops_keep_radio_on_during_sleep(void *handle) +{ + struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle; + + pr_debug("%s: keep radio on during sleep is %s\n", __func__, + ctx->keep_radio_on_during_sleep ? "allowed" : "not allowed"); + + return ctx->keep_radio_on_during_sleep; +} + void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops, const struct wil_platform_rops *rops, void *wil_handle) { @@ -1412,6 +1521,7 @@ void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops, ops->resume = ops_resume; ops->uninit = ops_uninit; ops->notify = ops_notify; + ops->keep_radio_on_during_sleep = ops_keep_radio_on_during_sleep; return ctx; } @@ -1428,19 +1538,9 @@ int msm_11ad_modinit(void) return -EINVAL; } - if (ctx->pristine_state) { - /* in old kernels, pci_load_saved_state() is not exported; - * so use pci_load_and_free_saved_state() - * and re-allocate ctx->saved_state again - */ - pci_load_and_free_saved_state(ctx->pcidev, - &ctx->pristine_state); - ctx->pristine_state = pci_store_saved_state(ctx->pcidev); - } - ctx->subsys_handle = subsystem_get(ctx->subsysdesc.name); - return ops_resume(ctx, false); + return msm_11ad_resume_power_on(ctx); } EXPORT_SYMBOL(msm_11ad_modinit);