wil6210: add support for PCIe D3hot in system suspend
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>
This commit is contained in:
@@ -37,6 +37,8 @@ module_param(led_id, byte, 0444);
|
||||
MODULE_PARM_DESC(led_id,
|
||||
" 60G device led enablement. Set the led ID (0-2) to enable");
|
||||
|
||||
#define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200
|
||||
|
||||
/**
|
||||
* WMI event receiving - theory of operations
|
||||
*
|
||||
@@ -233,6 +235,16 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Allow sending only suspend / resume commands during susepnd flow */
|
||||
if ((test_bit(wil_status_suspending, wil->status) ||
|
||||
test_bit(wil_status_suspended, wil->status) ||
|
||||
test_bit(wil_status_resuming, wil->status)) &&
|
||||
((cmdid != WMI_TRAFFIC_SUSPEND_CMDID) &&
|
||||
(cmdid != WMI_TRAFFIC_RESUME_CMDID))) {
|
||||
wil_err(wil, "WMI: reject send_command during suspend\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!head) {
|
||||
wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head);
|
||||
return -EINVAL;
|
||||
@@ -862,6 +874,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(wil_status_suspended, wil->status)) {
|
||||
wil_err(wil, "suspended. cannot handle WMI event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (n = 0;; n++) {
|
||||
u16 len;
|
||||
bool q;
|
||||
@@ -914,6 +931,15 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
||||
struct wmi_cmd_hdr *wmi = &evt->event.wmi;
|
||||
u16 id = le16_to_cpu(wmi->command_id);
|
||||
u32 tstamp = le32_to_cpu(wmi->fw_timestamp);
|
||||
if (test_bit(wil_status_resuming, wil->status)) {
|
||||
if (id == WMI_TRAFFIC_RESUME_EVENTID)
|
||||
clear_bit(wil_status_resuming,
|
||||
wil->status);
|
||||
else
|
||||
wil_err(wil,
|
||||
"WMI evt %d while resuming\n",
|
||||
id);
|
||||
}
|
||||
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
|
||||
if (wil->reply_id && wil->reply_id == id) {
|
||||
if (wil->reply_buf) {
|
||||
@@ -921,6 +947,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
||||
min(len, wil->reply_size));
|
||||
immed_reply = true;
|
||||
}
|
||||
if (id == WMI_TRAFFIC_SUSPEND_EVENTID) {
|
||||
wil_dbg_wmi(wil,
|
||||
"set suspend_resp_rcvd\n");
|
||||
wil->suspend_resp_rcvd = true;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
||||
|
||||
@@ -1762,6 +1793,85 @@ void wmi_event_flush(struct wil6210_priv *wil)
|
||||
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
||||
}
|
||||
|
||||
int wmi_suspend(struct wil6210_priv *wil)
|
||||
{
|
||||
int rc;
|
||||
struct wmi_traffic_suspend_cmd cmd = {
|
||||
.wakeup_trigger = wil->wakeup_trigger,
|
||||
};
|
||||
struct {
|
||||
struct wmi_cmd_hdr wmi;
|
||||
struct wmi_traffic_suspend_event evt;
|
||||
} __packed reply;
|
||||
u32 suspend_to = WIL_WAIT_FOR_SUSPEND_RESUME_COMP;
|
||||
|
||||
wil->suspend_resp_rcvd = false;
|
||||
wil->suspend_resp_comp = false;
|
||||
|
||||
reply.evt.status = WMI_TRAFFIC_SUSPEND_REJECTED;
|
||||
|
||||
rc = wmi_call(wil, WMI_TRAFFIC_SUSPEND_CMDID, &cmd, sizeof(cmd),
|
||||
WMI_TRAFFIC_SUSPEND_EVENTID, &reply, sizeof(reply),
|
||||
suspend_to);
|
||||
if (rc) {
|
||||
wil_err(wil, "wmi_call for suspend req failed, rc=%d\n", rc);
|
||||
if (rc == -ETIME)
|
||||
/* wmi_call TO */
|
||||
wil->suspend_stats.rejected_by_device++;
|
||||
else
|
||||
wil->suspend_stats.rejected_by_host++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wil_dbg_wmi(wil, "waiting for suspend_response_completed\n");
|
||||
|
||||
rc = wait_event_interruptible_timeout(wil->wq,
|
||||
wil->suspend_resp_comp,
|
||||
msecs_to_jiffies(suspend_to));
|
||||
if (rc == 0) {
|
||||
wil_err(wil, "TO waiting for suspend_response_completed\n");
|
||||
if (wil->suspend_resp_rcvd)
|
||||
/* Device responded but we TO due to another reason */
|
||||
wil->suspend_stats.rejected_by_host++;
|
||||
else
|
||||
wil->suspend_stats.rejected_by_device++;
|
||||
rc = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wil_dbg_wmi(wil, "suspend_response_completed rcvd\n");
|
||||
if (reply.evt.status == WMI_TRAFFIC_SUSPEND_REJECTED) {
|
||||
wil_dbg_pm(wil, "device rejected the suspend\n");
|
||||
wil->suspend_stats.rejected_by_device++;
|
||||
}
|
||||
rc = reply.evt.status;
|
||||
|
||||
out:
|
||||
wil->suspend_resp_rcvd = false;
|
||||
wil->suspend_resp_comp = false;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int wmi_resume(struct wil6210_priv *wil)
|
||||
{
|
||||
int rc;
|
||||
struct {
|
||||
struct wmi_cmd_hdr wmi;
|
||||
struct wmi_traffic_resume_event evt;
|
||||
} __packed reply;
|
||||
|
||||
reply.evt.status = WMI_TRAFFIC_RESUME_FAILED;
|
||||
|
||||
rc = wmi_call(wil, WMI_TRAFFIC_RESUME_CMDID, NULL, 0,
|
||||
WMI_TRAFFIC_RESUME_EVENTID, &reply, sizeof(reply),
|
||||
WIL_WAIT_FOR_SUSPEND_RESUME_COMP);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return reply.evt.status;
|
||||
}
|
||||
|
||||
static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id,
|
||||
void *d, int len)
|
||||
{
|
||||
@@ -1851,3 +1961,36 @@ void wmi_event_worker(struct work_struct *work)
|
||||
}
|
||||
wil_dbg_wmi(wil, "event_worker: Finished\n");
|
||||
}
|
||||
|
||||
bool wil_is_wmi_idle(struct wil6210_priv *wil)
|
||||
{
|
||||
ulong flags;
|
||||
struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx;
|
||||
bool rc = false;
|
||||
|
||||
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
|
||||
|
||||
/* Check if there are pending WMI events in the events queue */
|
||||
if (!list_empty(&wil->pending_wmi_ev)) {
|
||||
wil_dbg_pm(wil, "Pending WMI events in queue\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if there is a pending WMI call */
|
||||
if (wil->reply_id) {
|
||||
wil_dbg_pm(wil, "Pending WMI call\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if there are pending RX events in mbox */
|
||||
r->head = wil_r(wil, RGF_MBOX +
|
||||
offsetof(struct wil6210_mbox_ctl, rx.head));
|
||||
if (r->tail != r->head)
|
||||
wil_dbg_pm(wil, "Pending WMI mbox events\n");
|
||||
else
|
||||
rc = true;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
Reference in New Issue
Block a user