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 <merez@codeaurora.org>
This commit is contained in:
parent
36fc0ea2ed
commit
9b372ef962
2 changed files with 175 additions and 72 deletions
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue