Skip to content

Commit

Permalink
wil6210: add support for PCIe D3hot in system suspend
Browse files Browse the repository at this point in the history
In order to preserve the connection in suspend/resume flow,
wil6210 host allows going to PCIe D3hot state in suspend,
instead of performing a full wil6210 device reset. This
requires the platform ability to initiate wakeup in case of
RX data. To check that, a new platform API is added.
In addition, add cfg80211 suspend/resume callbacks
implementation.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
  • Loading branch information
Maya Erez authored and Kalle Valo committed Jun 21, 2017
1 parent 5b49ee9 commit fe9ee51
Show file tree
Hide file tree
Showing 11 changed files with 603 additions and 49 deletions.
38 changes: 38 additions & 0 deletions drivers/net/wireless/ath/wil6210/cfg80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -1694,6 +1694,42 @@ static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
return wil_ps_update(wil, ps_profile);
}

static int wil_cfg80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;

/* Setting the wakeup trigger based on wow is TBD */

if (test_bit(wil_status_suspended, wil->status)) {
wil_dbg_pm(wil, "trying to suspend while suspended\n");
return 0;
}

rc = wil_can_suspend(wil, false);
if (rc)
goto out;

wil_dbg_pm(wil, "suspending\n");

wil_p2p_stop_discovery(wil);

wil_abort_scan(wil, true);

out:
return rc;
}

static int wil_cfg80211_resume(struct wiphy *wiphy)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);

wil_dbg_pm(wil, "resuming\n");

return 0;
}

static const struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
Expand Down Expand Up @@ -1725,6 +1761,8 @@ static const struct cfg80211_ops wil_cfg80211_ops = {
.start_p2p_device = wil_cfg80211_start_p2p_device,
.stop_p2p_device = wil_cfg80211_stop_p2p_device,
.set_power_mgmt = wil_cfg80211_set_power_mgmt,
.suspend = wil_cfg80211_suspend,
.resume = wil_cfg80211_resume,
};

static void wil_wiphy_init(struct wiphy *wiphy)
Expand Down
49 changes: 49 additions & 0 deletions drivers/net/wireless/ath/wil6210/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
void *buf;
size_t ret;

if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
test_bit(wil_status_suspended, wil_blob->wil->status))
return 0;

if (pos < 0)
return -EINVAL;

Expand Down Expand Up @@ -1600,6 +1604,49 @@ static const struct file_operations fops_fw_version = {
.llseek = seq_lseek,
};

/*---------suspend_stats---------*/
static ssize_t wil_write_suspend_stats(struct file *file,
const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;

memset(&wil->suspend_stats, 0, sizeof(wil->suspend_stats));

return len;
}

static ssize_t wil_read_suspend_stats(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
static char text[400];
int n;

n = snprintf(text, sizeof(text),
"Suspend statistics:\n"
"successful suspends:%ld failed suspends:%ld\n"
"successful resumes:%ld failed resumes:%ld\n"
"rejected by host:%ld rejected by device:%ld\n",
wil->suspend_stats.successful_suspends,
wil->suspend_stats.failed_suspends,
wil->suspend_stats.successful_resumes,
wil->suspend_stats.failed_resumes,
wil->suspend_stats.rejected_by_host,
wil->suspend_stats.rejected_by_device);

n = min_t(int, n, sizeof(text));

return simple_read_from_buffer(user_buf, count, ppos, text, n);
}

static const struct file_operations fops_suspend_stats = {
.read = wil_read_suspend_stats,
.write = wil_write_suspend_stats,
.open = simple_open,
};

/*----------------*/
static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
struct dentry *dbg)
Expand Down Expand Up @@ -1652,6 +1699,7 @@ static const struct {
{"led_blink_time", 0644, &fops_led_blink_time},
{"fw_capabilities", 0444, &fops_fw_capabilities},
{"fw_version", 0444, &fops_fw_version},
{"suspend_stats", 0644, &fops_suspend_stats},
};

static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
Expand Down Expand Up @@ -1698,6 +1746,7 @@ static const struct dbg_off dbg_wil_off[] = {
WIL_FIELD(discovery_mode, 0644, doff_u8),
WIL_FIELD(chip_revision, 0444, doff_u8),
WIL_FIELD(abft_len, 0644, doff_u8),
WIL_FIELD(wakeup_trigger, 0644, doff_u8),
{},
};

Expand Down
6 changes: 6 additions & 0 deletions drivers/net/wireless/ath/wil6210/interrupt.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,12 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie)

wil6210_unmask_irq_pseudo(wil);

if (wil->suspend_resp_rcvd) {
wil_dbg_irq(wil, "set suspend_resp_comp to true\n");
wil->suspend_resp_comp = true;
wake_up_interruptible(&wil->wq);
}

return IRQ_HANDLED;
}

Expand Down
7 changes: 6 additions & 1 deletion drivers/net/wireless/ath/wil6210/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,9 @@ int wil_priv_init(struct wil6210_priv *wil)

wil->ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;

wil->wakeup_trigger = WMI_WAKEUP_TRIGGER_UCAST |
WMI_WAKEUP_TRIGGER_BCAST;

return 0;

out_wmi_wq:
Expand All @@ -586,8 +589,10 @@ int wil_priv_init(struct wil6210_priv *wil)

void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps)
{
if (wil->platform_ops.bus_request)
if (wil->platform_ops.bus_request) {
wil->bus_request_kbps = kbps;
wil->platform_ops.bus_request(wil->platform_handle, kbps);
}
}

/**
Expand Down
50 changes: 34 additions & 16 deletions drivers/net/wireless/ath/wil6210/pcie_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)

wil_dbg_misc(wil, "if_pcie_enable, wmi_only %d\n", wmi_only);

pdev->msi_enabled = 0;

pci_set_master(pdev);

wil_dbg_misc(wil, "Setup %s interrupt\n", use_msi ? "MSI" : "INTx");
Expand Down Expand Up @@ -259,7 +257,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}

rc = pci_enable_device(pdev);
if (rc) {
if (rc && pdev->msi_enabled == 0) {
wil_err(wil,
"pci_enable_device failed, retry with MSI only\n");
/* Work around for platforms that can't allocate IRQ:
Expand All @@ -274,6 +272,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_plat;
}
/* rollback to err_disable_pdev */
pci_set_power_state(pdev, PCI_D0);

rc = pci_request_region(pdev, 0, WIL_NAME);
if (rc) {
Expand All @@ -294,6 +293,15 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
wil_set_capabilities(wil);
wil6210_clear_irq(wil);

wil->keep_radio_on_during_sleep =
wil->platform_ops.keep_radio_on_during_sleep &&
wil->platform_ops.keep_radio_on_during_sleep(
wil->platform_handle) &&
test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);

wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
wil->keep_radio_on_during_sleep);

/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
Expand Down Expand Up @@ -390,15 +398,16 @@ static int wil6210_suspend(struct device *dev, bool is_runtime)
goto out;

rc = wil_suspend(wil, is_runtime);
if (rc)
goto out;

/* TODO: how do I bring card in low power state? */

/* disable bus mastering */
pci_clear_master(pdev);
/* PCI will call pci_save_state(pdev) and pci_prepare_to_sleep(pdev) */
if (!rc) {
wil->suspend_stats.successful_suspends++;

/* If platform device supports keep_radio_on_during_sleep
* it will control PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
/* disable bus mastering */
pci_clear_master(pdev);
}
out:
return rc;
}
Expand All @@ -411,12 +420,21 @@ static int wil6210_resume(struct device *dev, bool is_runtime)

wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system");

/* allow master */
pci_set_master(pdev);

/* If platform device supports keep_radio_on_during_sleep it will
* control PCIe master
*/
if (!wil->keep_radio_on_during_sleep)
/* allow master */
pci_set_master(pdev);
rc = wil_resume(wil, is_runtime);
if (rc)
pci_clear_master(pdev);
if (rc) {
wil_err(wil, "device failed to resume (%d)\n", rc);
wil->suspend_stats.failed_resumes++;
if (!wil->keep_radio_on_during_sleep)
pci_clear_master(pdev);
} else {
wil->suspend_stats.successful_resumes++;
}

return rc;
}
Expand Down
Loading

0 comments on commit fe9ee51

Please sign in to comment.