From d86d47164b227f01c3ec34c3f5a1613977d563eb Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 19 May 2017 11:54:10 +0300 Subject: wil6210: support devices with different PCIe bar size wil6210 devices can have different PCIe bar size, hence get the bar size from PCIe device instead of using a constant bar size. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath/wil6210/wmi.c') diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 814c35645b73..93902cb2e8cf 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -157,7 +157,7 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) return NULL; off = HOSTADDR(ptr); - if (off > WIL6210_MEM_SIZE - 4) + if (off > wil->bar_size - 4) return NULL; return wil->csr + off; @@ -177,7 +177,7 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) return NULL; off = HOSTADDR(ptr); - if (off > WIL6210_MEM_SIZE - 4) + if (off > wil->bar_size - 4) return NULL; return wil->csr + off; -- cgit v1.2.3 From 59ae1d127ac0ae404baf414c434ba2651b793f46 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 16 Jun 2017 14:29:20 +0200 Subject: networking: introduce and use skb_put_data() A common pattern with skb_put() is to just want to memcpy() some data into the new space, introduce skb_put_data() for this. An spatch similar to the one for skb_put_zero() converts many of the places using it: @@ identifier p, p2; expression len, skb, data; type t, t2; @@ ( -p = skb_put(skb, len); +p = skb_put_data(skb, data, len); | -p = (t)skb_put(skb, len); +p = skb_put_data(skb, data, len); ) ( p2 = (t2)p; -memcpy(p2, data, len); | -memcpy(p, data, len); ) @@ type t, t2; identifier p, p2; expression skb, data; @@ t *p; ... ( -p = skb_put(skb, sizeof(t)); +p = skb_put_data(skb, data, sizeof(t)); | -p = (t *)skb_put(skb, sizeof(t)); +p = skb_put_data(skb, data, sizeof(t)); ) ( p2 = (t2)p; -memcpy(p2, data, sizeof(*p)); | -memcpy(p, data, sizeof(*p)); ) @@ expression skb, len, data; @@ -memcpy(skb_put(skb, len), data, len); +skb_put_data(skb, data, len); (again, manually post-processed to retain some comments) Reviewed-by: Stephen Hemminger Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath/wil6210/wmi.c') diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 814c35645b73..cff9c585972f 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -681,7 +681,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, ether_addr_copy(eth->h_dest, ndev->dev_addr); ether_addr_copy(eth->h_source, evt->src_mac); eth->h_proto = cpu_to_be16(ETH_P_PAE); - memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); + skb_put_data(skb, evt->eapol, eapol_len); skb->protocol = eth_type_trans(skb, ndev); if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { ndev->stats.rx_packets++; -- cgit v1.2.3 From 4df864c1d9afb46e2461a9f808d9f11a42d31bad Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 16 Jun 2017 14:29:21 +0200 Subject: networking: make skb_put & friends return void pointers It seems like a historic accident that these return unsigned char *, and in many places that means casts are required, more often than not. Make these functions (skb_put, __skb_put and pskb_put) return void * and remove all the casts across the tree, adding a (u8 *) cast only where the unsigned char pointer was used directly, all done with the following spatch: @@ expression SKB, LEN; typedef u8; identifier fn = { skb_put, __skb_put }; @@ - *(fn(SKB, LEN)) + *(u8 *)fn(SKB, LEN) @@ expression E, SKB, LEN; identifier fn = { skb_put, __skb_put }; type T; @@ - E = ((T *)(fn(SKB, LEN))) + E = fn(SKB, LEN) which actually doesn't cover pskb_put since there are only three users overall. A handful of stragglers were converted manually, notably a macro in drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many instances in net/bluetooth/hci_sock.c. In the former file, I also had to fix one whitespace problem spatch introduced. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath/wil6210/wmi.c') diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index cff9c585972f..0a5020f31de1 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -677,7 +677,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, return; } - eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); + eth = skb_put(skb, ETH_HLEN); ether_addr_copy(eth->h_dest, ndev->dev_addr); ether_addr_copy(eth->h_source, evt->src_mac); eth->h_proto = cpu_to_be16(ETH_P_PAE); -- cgit v1.2.3 From fe9ee51e6a43a79d9c6bf92124b4db542157aed3 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 16 Jun 2017 10:38:04 +0300 Subject: 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 Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 143 +++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'drivers/net/wireless/ath/wil6210/wmi.c') diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 93902cb2e8cf..26cf722e1495 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -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; +} -- cgit v1.2.3