1dd4f32aeSBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
2dd4f32aeSBjoern A. Zeeb /*
3dd4f32aeSBjoern A. Zeeb * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4dd4f32aeSBjoern A. Zeeb */
5dd4f32aeSBjoern A. Zeeb #include <linux/skbuff.h>
6dd4f32aeSBjoern A. Zeeb #include <linux/ctype.h>
7dd4f32aeSBjoern A. Zeeb
8dd4f32aeSBjoern A. Zeeb #include "debug.h"
9dd4f32aeSBjoern A. Zeeb #include "hif.h"
10dd4f32aeSBjoern A. Zeeb
ath11k_htc_alloc_skb(struct ath11k_base * ab,int size)11dd4f32aeSBjoern A. Zeeb struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size)
12dd4f32aeSBjoern A. Zeeb {
13dd4f32aeSBjoern A. Zeeb struct sk_buff *skb;
14dd4f32aeSBjoern A. Zeeb
15dd4f32aeSBjoern A. Zeeb skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr));
16dd4f32aeSBjoern A. Zeeb if (!skb)
17dd4f32aeSBjoern A. Zeeb return NULL;
18dd4f32aeSBjoern A. Zeeb
19dd4f32aeSBjoern A. Zeeb skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
20dd4f32aeSBjoern A. Zeeb
21dd4f32aeSBjoern A. Zeeb /* FW/HTC requires 4-byte aligned streams */
22dd4f32aeSBjoern A. Zeeb if (!IS_ALIGNED((unsigned long)skb->data, 4))
23dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Unaligned HTC tx skb\n");
24dd4f32aeSBjoern A. Zeeb
25dd4f32aeSBjoern A. Zeeb return skb;
26dd4f32aeSBjoern A. Zeeb }
27dd4f32aeSBjoern A. Zeeb
ath11k_htc_control_tx_complete(struct ath11k_base * ab,struct sk_buff * skb)28dd4f32aeSBjoern A. Zeeb static void ath11k_htc_control_tx_complete(struct ath11k_base *ab,
29dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
30dd4f32aeSBjoern A. Zeeb {
31dd4f32aeSBjoern A. Zeeb kfree_skb(skb);
32dd4f32aeSBjoern A. Zeeb }
33dd4f32aeSBjoern A. Zeeb
ath11k_htc_build_tx_ctrl_skb(void * ab)34dd4f32aeSBjoern A. Zeeb static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab)
35dd4f32aeSBjoern A. Zeeb {
36dd4f32aeSBjoern A. Zeeb struct sk_buff *skb;
37dd4f32aeSBjoern A. Zeeb struct ath11k_skb_cb *skb_cb;
38dd4f32aeSBjoern A. Zeeb
39dd4f32aeSBjoern A. Zeeb skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE);
40dd4f32aeSBjoern A. Zeeb if (!skb)
41dd4f32aeSBjoern A. Zeeb return NULL;
42dd4f32aeSBjoern A. Zeeb
43dd4f32aeSBjoern A. Zeeb skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
44dd4f32aeSBjoern A. Zeeb WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
45dd4f32aeSBjoern A. Zeeb
46dd4f32aeSBjoern A. Zeeb skb_cb = ATH11K_SKB_CB(skb);
47dd4f32aeSBjoern A. Zeeb memset(skb_cb, 0, sizeof(*skb_cb));
48dd4f32aeSBjoern A. Zeeb
49dd4f32aeSBjoern A. Zeeb return skb;
50dd4f32aeSBjoern A. Zeeb }
51dd4f32aeSBjoern A. Zeeb
ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep * ep,struct sk_buff * skb)52dd4f32aeSBjoern A. Zeeb static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep,
53dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
54dd4f32aeSBjoern A. Zeeb {
55dd4f32aeSBjoern A. Zeeb struct ath11k_htc_hdr *hdr;
56dd4f32aeSBjoern A. Zeeb
57dd4f32aeSBjoern A. Zeeb hdr = (struct ath11k_htc_hdr *)skb->data;
58dd4f32aeSBjoern A. Zeeb
59dd4f32aeSBjoern A. Zeeb memset(hdr, 0, sizeof(*hdr));
60dd4f32aeSBjoern A. Zeeb hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) |
61dd4f32aeSBjoern A. Zeeb FIELD_PREP(HTC_HDR_PAYLOADLEN,
62dd4f32aeSBjoern A. Zeeb (skb->len - sizeof(*hdr)));
63dd4f32aeSBjoern A. Zeeb
64dd4f32aeSBjoern A. Zeeb if (ep->tx_credit_flow_enabled)
65dd4f32aeSBjoern A. Zeeb hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS,
66dd4f32aeSBjoern A. Zeeb ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE);
67dd4f32aeSBjoern A. Zeeb
68dd4f32aeSBjoern A. Zeeb spin_lock_bh(&ep->htc->tx_lock);
69dd4f32aeSBjoern A. Zeeb hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++);
70dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&ep->htc->tx_lock);
71dd4f32aeSBjoern A. Zeeb }
72dd4f32aeSBjoern A. Zeeb
ath11k_htc_send(struct ath11k_htc * htc,enum ath11k_htc_ep_id eid,struct sk_buff * skb)73dd4f32aeSBjoern A. Zeeb int ath11k_htc_send(struct ath11k_htc *htc,
74dd4f32aeSBjoern A. Zeeb enum ath11k_htc_ep_id eid,
75dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
76dd4f32aeSBjoern A. Zeeb {
77dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep = &htc->endpoint[eid];
78dd4f32aeSBjoern A. Zeeb struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb);
79dd4f32aeSBjoern A. Zeeb struct device *dev = htc->ab->dev;
80dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
81dd4f32aeSBjoern A. Zeeb int credits = 0;
82dd4f32aeSBjoern A. Zeeb int ret;
83dd4f32aeSBjoern A. Zeeb bool credit_flow_enabled = (ab->hw_params.credit_flow &&
84dd4f32aeSBjoern A. Zeeb ep->tx_credit_flow_enabled);
85dd4f32aeSBjoern A. Zeeb
86dd4f32aeSBjoern A. Zeeb if (eid >= ATH11K_HTC_EP_COUNT) {
87dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid endpoint id: %d\n", eid);
88dd4f32aeSBjoern A. Zeeb return -ENOENT;
89dd4f32aeSBjoern A. Zeeb }
90dd4f32aeSBjoern A. Zeeb
91dd4f32aeSBjoern A. Zeeb skb_push(skb, sizeof(struct ath11k_htc_hdr));
92dd4f32aeSBjoern A. Zeeb
93dd4f32aeSBjoern A. Zeeb if (credit_flow_enabled) {
94dd4f32aeSBjoern A. Zeeb credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
95dd4f32aeSBjoern A. Zeeb spin_lock_bh(&htc->tx_lock);
96dd4f32aeSBjoern A. Zeeb if (ep->tx_credits < credits) {
97dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC,
9828348caeSBjoern A. Zeeb "ep %d insufficient credits required %d total %d\n",
99dd4f32aeSBjoern A. Zeeb eid, credits, ep->tx_credits);
100dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
101dd4f32aeSBjoern A. Zeeb ret = -EAGAIN;
102dd4f32aeSBjoern A. Zeeb goto err_pull;
103dd4f32aeSBjoern A. Zeeb }
104dd4f32aeSBjoern A. Zeeb ep->tx_credits -= credits;
105dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC,
10628348caeSBjoern A. Zeeb "ep %d credits consumed %d total %d\n",
107dd4f32aeSBjoern A. Zeeb eid, credits, ep->tx_credits);
108dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
109dd4f32aeSBjoern A. Zeeb }
110dd4f32aeSBjoern A. Zeeb
111dd4f32aeSBjoern A. Zeeb ath11k_htc_prepare_tx_skb(ep, skb);
112dd4f32aeSBjoern A. Zeeb
113dd4f32aeSBjoern A. Zeeb skb_cb->eid = eid;
114dd4f32aeSBjoern A. Zeeb skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
115dd4f32aeSBjoern A. Zeeb ret = dma_mapping_error(dev, skb_cb->paddr);
116dd4f32aeSBjoern A. Zeeb if (ret) {
117dd4f32aeSBjoern A. Zeeb ret = -EIO;
118dd4f32aeSBjoern A. Zeeb goto err_credits;
119dd4f32aeSBjoern A. Zeeb }
120dd4f32aeSBjoern A. Zeeb
12128348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC, "tx skb %p eid %d paddr %pad\n",
12228348caeSBjoern A. Zeeb skb, skb_cb->eid, &skb_cb->paddr);
12328348caeSBjoern A. Zeeb
124dd4f32aeSBjoern A. Zeeb ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid);
125dd4f32aeSBjoern A. Zeeb if (ret)
126dd4f32aeSBjoern A. Zeeb goto err_unmap;
127dd4f32aeSBjoern A. Zeeb
128dd4f32aeSBjoern A. Zeeb return 0;
129dd4f32aeSBjoern A. Zeeb
130dd4f32aeSBjoern A. Zeeb err_unmap:
131dd4f32aeSBjoern A. Zeeb dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
132dd4f32aeSBjoern A. Zeeb err_credits:
133dd4f32aeSBjoern A. Zeeb if (credit_flow_enabled) {
134dd4f32aeSBjoern A. Zeeb spin_lock_bh(&htc->tx_lock);
135dd4f32aeSBjoern A. Zeeb ep->tx_credits += credits;
136dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC,
13728348caeSBjoern A. Zeeb "ep %d credits reverted %d total %d\n",
138dd4f32aeSBjoern A. Zeeb eid, credits, ep->tx_credits);
139dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
140dd4f32aeSBjoern A. Zeeb
141dd4f32aeSBjoern A. Zeeb if (ep->ep_ops.ep_tx_credits)
142dd4f32aeSBjoern A. Zeeb ep->ep_ops.ep_tx_credits(htc->ab);
143dd4f32aeSBjoern A. Zeeb }
144dd4f32aeSBjoern A. Zeeb err_pull:
145dd4f32aeSBjoern A. Zeeb skb_pull(skb, sizeof(struct ath11k_htc_hdr));
146dd4f32aeSBjoern A. Zeeb return ret;
147dd4f32aeSBjoern A. Zeeb }
148dd4f32aeSBjoern A. Zeeb
149dd4f32aeSBjoern A. Zeeb static void
ath11k_htc_process_credit_report(struct ath11k_htc * htc,const struct ath11k_htc_credit_report * report,int len,enum ath11k_htc_ep_id eid)150dd4f32aeSBjoern A. Zeeb ath11k_htc_process_credit_report(struct ath11k_htc *htc,
151dd4f32aeSBjoern A. Zeeb const struct ath11k_htc_credit_report *report,
152dd4f32aeSBjoern A. Zeeb int len,
153dd4f32aeSBjoern A. Zeeb enum ath11k_htc_ep_id eid)
154dd4f32aeSBjoern A. Zeeb {
155dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
156dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep;
157dd4f32aeSBjoern A. Zeeb int i, n_reports;
158dd4f32aeSBjoern A. Zeeb
159dd4f32aeSBjoern A. Zeeb if (len % sizeof(*report))
160dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Uneven credit report len %d", len);
161dd4f32aeSBjoern A. Zeeb
162dd4f32aeSBjoern A. Zeeb n_reports = len / sizeof(*report);
163dd4f32aeSBjoern A. Zeeb
164dd4f32aeSBjoern A. Zeeb spin_lock_bh(&htc->tx_lock);
165dd4f32aeSBjoern A. Zeeb for (i = 0; i < n_reports; i++, report++) {
166dd4f32aeSBjoern A. Zeeb if (report->eid >= ATH11K_HTC_EP_COUNT)
167dd4f32aeSBjoern A. Zeeb break;
168dd4f32aeSBjoern A. Zeeb
169dd4f32aeSBjoern A. Zeeb ep = &htc->endpoint[report->eid];
170dd4f32aeSBjoern A. Zeeb ep->tx_credits += report->credits;
171dd4f32aeSBjoern A. Zeeb
17228348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC, "ep %d credits got %d total %d\n",
173dd4f32aeSBjoern A. Zeeb report->eid, report->credits, ep->tx_credits);
174dd4f32aeSBjoern A. Zeeb
175dd4f32aeSBjoern A. Zeeb if (ep->ep_ops.ep_tx_credits) {
176dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
177dd4f32aeSBjoern A. Zeeb ep->ep_ops.ep_tx_credits(htc->ab);
178dd4f32aeSBjoern A. Zeeb spin_lock_bh(&htc->tx_lock);
179dd4f32aeSBjoern A. Zeeb }
180dd4f32aeSBjoern A. Zeeb }
181dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
182dd4f32aeSBjoern A. Zeeb }
183dd4f32aeSBjoern A. Zeeb
ath11k_htc_process_trailer(struct ath11k_htc * htc,u8 * buffer,int length,enum ath11k_htc_ep_id src_eid)184dd4f32aeSBjoern A. Zeeb static int ath11k_htc_process_trailer(struct ath11k_htc *htc,
185dd4f32aeSBjoern A. Zeeb u8 *buffer,
186dd4f32aeSBjoern A. Zeeb int length,
187dd4f32aeSBjoern A. Zeeb enum ath11k_htc_ep_id src_eid)
188dd4f32aeSBjoern A. Zeeb {
189dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
190dd4f32aeSBjoern A. Zeeb int status = 0;
191dd4f32aeSBjoern A. Zeeb struct ath11k_htc_record *record;
192dd4f32aeSBjoern A. Zeeb size_t len;
193dd4f32aeSBjoern A. Zeeb
194dd4f32aeSBjoern A. Zeeb while (length > 0) {
195dd4f32aeSBjoern A. Zeeb record = (struct ath11k_htc_record *)buffer;
196dd4f32aeSBjoern A. Zeeb
197dd4f32aeSBjoern A. Zeeb if (length < sizeof(record->hdr)) {
198dd4f32aeSBjoern A. Zeeb status = -EINVAL;
199dd4f32aeSBjoern A. Zeeb break;
200dd4f32aeSBjoern A. Zeeb }
201dd4f32aeSBjoern A. Zeeb
202dd4f32aeSBjoern A. Zeeb if (record->hdr.len > length) {
203dd4f32aeSBjoern A. Zeeb /* no room left in buffer for record */
204dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid record length: %d\n",
205dd4f32aeSBjoern A. Zeeb record->hdr.len);
206dd4f32aeSBjoern A. Zeeb status = -EINVAL;
207dd4f32aeSBjoern A. Zeeb break;
208dd4f32aeSBjoern A. Zeeb }
209dd4f32aeSBjoern A. Zeeb
210dd4f32aeSBjoern A. Zeeb if (ab->hw_params.credit_flow) {
211dd4f32aeSBjoern A. Zeeb switch (record->hdr.id) {
212dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_RECORD_CREDITS:
213dd4f32aeSBjoern A. Zeeb len = sizeof(struct ath11k_htc_credit_report);
214dd4f32aeSBjoern A. Zeeb if (record->hdr.len < len) {
215dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Credit report too long\n");
216dd4f32aeSBjoern A. Zeeb status = -EINVAL;
217dd4f32aeSBjoern A. Zeeb break;
218dd4f32aeSBjoern A. Zeeb }
219dd4f32aeSBjoern A. Zeeb ath11k_htc_process_credit_report(htc,
220dd4f32aeSBjoern A. Zeeb record->credit_report,
221dd4f32aeSBjoern A. Zeeb record->hdr.len,
222dd4f32aeSBjoern A. Zeeb src_eid);
223dd4f32aeSBjoern A. Zeeb break;
224dd4f32aeSBjoern A. Zeeb default:
225dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Unhandled record: id:%d length:%d\n",
226dd4f32aeSBjoern A. Zeeb record->hdr.id, record->hdr.len);
227dd4f32aeSBjoern A. Zeeb break;
228dd4f32aeSBjoern A. Zeeb }
229dd4f32aeSBjoern A. Zeeb }
230dd4f32aeSBjoern A. Zeeb
231dd4f32aeSBjoern A. Zeeb if (status)
232dd4f32aeSBjoern A. Zeeb break;
233dd4f32aeSBjoern A. Zeeb
234dd4f32aeSBjoern A. Zeeb /* multiple records may be present in a trailer */
235dd4f32aeSBjoern A. Zeeb buffer += sizeof(record->hdr) + record->hdr.len;
236dd4f32aeSBjoern A. Zeeb length -= sizeof(record->hdr) + record->hdr.len;
237dd4f32aeSBjoern A. Zeeb }
238dd4f32aeSBjoern A. Zeeb
239dd4f32aeSBjoern A. Zeeb return status;
240dd4f32aeSBjoern A. Zeeb }
241dd4f32aeSBjoern A. Zeeb
ath11k_htc_suspend_complete(struct ath11k_base * ab,bool ack)242dd4f32aeSBjoern A. Zeeb static void ath11k_htc_suspend_complete(struct ath11k_base *ab, bool ack)
243dd4f32aeSBjoern A. Zeeb {
24428348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_BOOT, "suspend complete %d\n", ack);
245dd4f32aeSBjoern A. Zeeb
246dd4f32aeSBjoern A. Zeeb if (ack)
247dd4f32aeSBjoern A. Zeeb set_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
248dd4f32aeSBjoern A. Zeeb else
249dd4f32aeSBjoern A. Zeeb clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
250dd4f32aeSBjoern A. Zeeb
251dd4f32aeSBjoern A. Zeeb complete(&ab->htc_suspend);
252dd4f32aeSBjoern A. Zeeb }
253dd4f32aeSBjoern A. Zeeb
ath11k_htc_tx_completion_handler(struct ath11k_base * ab,struct sk_buff * skb)254dd4f32aeSBjoern A. Zeeb void ath11k_htc_tx_completion_handler(struct ath11k_base *ab,
255dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
256dd4f32aeSBjoern A. Zeeb {
257dd4f32aeSBjoern A. Zeeb struct ath11k_htc *htc = &ab->htc;
258dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep;
259dd4f32aeSBjoern A. Zeeb void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *);
260dd4f32aeSBjoern A. Zeeb u8 eid;
261dd4f32aeSBjoern A. Zeeb
262dd4f32aeSBjoern A. Zeeb eid = ATH11K_SKB_CB(skb)->eid;
26328348caeSBjoern A. Zeeb if (eid >= ATH11K_HTC_EP_COUNT) {
26428348caeSBjoern A. Zeeb dev_kfree_skb_any(skb);
265dd4f32aeSBjoern A. Zeeb return;
26628348caeSBjoern A. Zeeb }
267dd4f32aeSBjoern A. Zeeb
268dd4f32aeSBjoern A. Zeeb ep = &htc->endpoint[eid];
269dd4f32aeSBjoern A. Zeeb spin_lock_bh(&htc->tx_lock);
270dd4f32aeSBjoern A. Zeeb ep_tx_complete = ep->ep_ops.ep_tx_complete;
271dd4f32aeSBjoern A. Zeeb spin_unlock_bh(&htc->tx_lock);
272dd4f32aeSBjoern A. Zeeb if (!ep_tx_complete) {
273dd4f32aeSBjoern A. Zeeb dev_kfree_skb_any(skb);
274dd4f32aeSBjoern A. Zeeb return;
275dd4f32aeSBjoern A. Zeeb }
276dd4f32aeSBjoern A. Zeeb ep_tx_complete(htc->ab, skb);
277dd4f32aeSBjoern A. Zeeb }
278dd4f32aeSBjoern A. Zeeb
ath11k_htc_wakeup_from_suspend(struct ath11k_base * ab)27928348caeSBjoern A. Zeeb static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab)
28028348caeSBjoern A. Zeeb {
28128348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_BOOT, "wakeup from suspend is received\n");
28228348caeSBjoern A. Zeeb }
28328348caeSBjoern A. Zeeb
ath11k_htc_rx_completion_handler(struct ath11k_base * ab,struct sk_buff * skb)284dd4f32aeSBjoern A. Zeeb void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,
285dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
286dd4f32aeSBjoern A. Zeeb {
287dd4f32aeSBjoern A. Zeeb int status = 0;
288dd4f32aeSBjoern A. Zeeb struct ath11k_htc *htc = &ab->htc;
289dd4f32aeSBjoern A. Zeeb struct ath11k_htc_hdr *hdr;
290dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep;
291dd4f32aeSBjoern A. Zeeb u16 payload_len;
29228348caeSBjoern A. Zeeb u32 message_id, trailer_len = 0;
293dd4f32aeSBjoern A. Zeeb size_t min_len;
294dd4f32aeSBjoern A. Zeeb u8 eid;
295dd4f32aeSBjoern A. Zeeb bool trailer_present;
296dd4f32aeSBjoern A. Zeeb
297dd4f32aeSBjoern A. Zeeb hdr = (struct ath11k_htc_hdr *)skb->data;
298dd4f32aeSBjoern A. Zeeb skb_pull(skb, sizeof(*hdr));
299dd4f32aeSBjoern A. Zeeb
300dd4f32aeSBjoern A. Zeeb eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info);
301dd4f32aeSBjoern A. Zeeb
302dd4f32aeSBjoern A. Zeeb if (eid >= ATH11K_HTC_EP_COUNT) {
303dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid);
304dd4f32aeSBjoern A. Zeeb goto out;
305dd4f32aeSBjoern A. Zeeb }
306dd4f32aeSBjoern A. Zeeb
307dd4f32aeSBjoern A. Zeeb ep = &htc->endpoint[eid];
308dd4f32aeSBjoern A. Zeeb
309dd4f32aeSBjoern A. Zeeb payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info);
310dd4f32aeSBjoern A. Zeeb
311dd4f32aeSBjoern A. Zeeb if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) {
312dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "HTC rx frame too long, len: %zu\n",
313dd4f32aeSBjoern A. Zeeb payload_len + sizeof(*hdr));
314dd4f32aeSBjoern A. Zeeb goto out;
315dd4f32aeSBjoern A. Zeeb }
316dd4f32aeSBjoern A. Zeeb
317dd4f32aeSBjoern A. Zeeb if (skb->len < payload_len) {
318dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n",
319dd4f32aeSBjoern A. Zeeb skb->len, payload_len);
320dd4f32aeSBjoern A. Zeeb goto out;
321dd4f32aeSBjoern A. Zeeb }
322dd4f32aeSBjoern A. Zeeb
323dd4f32aeSBjoern A. Zeeb /* get flags to check for trailer */
324dd4f32aeSBjoern A. Zeeb trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) &
325dd4f32aeSBjoern A. Zeeb ATH11K_HTC_FLAG_TRAILER_PRESENT;
326dd4f32aeSBjoern A. Zeeb
32728348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p trailer_present %d\n",
32828348caeSBjoern A. Zeeb eid, skb, trailer_present);
32928348caeSBjoern A. Zeeb
330dd4f32aeSBjoern A. Zeeb if (trailer_present) {
331dd4f32aeSBjoern A. Zeeb u8 *trailer;
332dd4f32aeSBjoern A. Zeeb
333dd4f32aeSBjoern A. Zeeb trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info);
334dd4f32aeSBjoern A. Zeeb min_len = sizeof(struct ath11k_htc_record_hdr);
335dd4f32aeSBjoern A. Zeeb
336dd4f32aeSBjoern A. Zeeb if ((trailer_len < min_len) ||
337dd4f32aeSBjoern A. Zeeb (trailer_len > payload_len)) {
338dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid trailer length: %d\n",
339dd4f32aeSBjoern A. Zeeb trailer_len);
340dd4f32aeSBjoern A. Zeeb goto out;
341dd4f32aeSBjoern A. Zeeb }
342dd4f32aeSBjoern A. Zeeb
343dd4f32aeSBjoern A. Zeeb trailer = (u8 *)hdr;
344dd4f32aeSBjoern A. Zeeb trailer += sizeof(*hdr);
345dd4f32aeSBjoern A. Zeeb trailer += payload_len;
346dd4f32aeSBjoern A. Zeeb trailer -= trailer_len;
347dd4f32aeSBjoern A. Zeeb status = ath11k_htc_process_trailer(htc, trailer,
348dd4f32aeSBjoern A. Zeeb trailer_len, eid);
349dd4f32aeSBjoern A. Zeeb if (status)
350dd4f32aeSBjoern A. Zeeb goto out;
351dd4f32aeSBjoern A. Zeeb
352dd4f32aeSBjoern A. Zeeb skb_trim(skb, skb->len - trailer_len);
353dd4f32aeSBjoern A. Zeeb }
354dd4f32aeSBjoern A. Zeeb
355dd4f32aeSBjoern A. Zeeb if (trailer_len >= payload_len)
356dd4f32aeSBjoern A. Zeeb /* zero length packet with trailer data, just drop these */
357dd4f32aeSBjoern A. Zeeb goto out;
358dd4f32aeSBjoern A. Zeeb
359dd4f32aeSBjoern A. Zeeb if (eid == ATH11K_HTC_EP_0) {
360dd4f32aeSBjoern A. Zeeb struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data;
361dd4f32aeSBjoern A. Zeeb
36228348caeSBjoern A. Zeeb message_id = FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id);
36328348caeSBjoern A. Zeeb
36428348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p message_id %d\n",
36528348caeSBjoern A. Zeeb eid, skb, message_id);
36628348caeSBjoern A. Zeeb
36728348caeSBjoern A. Zeeb switch (message_id) {
368dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_MSG_READY_ID:
369dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID:
370dd4f32aeSBjoern A. Zeeb /* handle HTC control message */
371dd4f32aeSBjoern A. Zeeb if (completion_done(&htc->ctl_resp)) {
372dd4f32aeSBjoern A. Zeeb /* this is a fatal error, target should not be
373dd4f32aeSBjoern A. Zeeb * sending unsolicited messages on the ep 0
374dd4f32aeSBjoern A. Zeeb */
375dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "HTC rx ctrl still processing\n");
376dd4f32aeSBjoern A. Zeeb complete(&htc->ctl_resp);
377dd4f32aeSBjoern A. Zeeb goto out;
378dd4f32aeSBjoern A. Zeeb }
379dd4f32aeSBjoern A. Zeeb
380dd4f32aeSBjoern A. Zeeb htc->control_resp_len =
381dd4f32aeSBjoern A. Zeeb min_t(int, skb->len,
382dd4f32aeSBjoern A. Zeeb ATH11K_HTC_MAX_CTRL_MSG_LEN);
383dd4f32aeSBjoern A. Zeeb
384dd4f32aeSBjoern A. Zeeb memcpy(htc->control_resp_buffer, skb->data,
385dd4f32aeSBjoern A. Zeeb htc->control_resp_len);
386dd4f32aeSBjoern A. Zeeb
387dd4f32aeSBjoern A. Zeeb complete(&htc->ctl_resp);
388dd4f32aeSBjoern A. Zeeb break;
389dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE:
390dd4f32aeSBjoern A. Zeeb ath11k_htc_suspend_complete(ab, true);
391dd4f32aeSBjoern A. Zeeb break;
392dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_MSG_NACK_SUSPEND:
393dd4f32aeSBjoern A. Zeeb ath11k_htc_suspend_complete(ab, false);
394dd4f32aeSBjoern A. Zeeb break;
395dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
39628348caeSBjoern A. Zeeb ath11k_htc_wakeup_from_suspend(ab);
397dd4f32aeSBjoern A. Zeeb break;
398dd4f32aeSBjoern A. Zeeb default:
399dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n",
400dd4f32aeSBjoern A. Zeeb FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id));
401dd4f32aeSBjoern A. Zeeb break;
402dd4f32aeSBjoern A. Zeeb }
403dd4f32aeSBjoern A. Zeeb goto out;
404dd4f32aeSBjoern A. Zeeb }
405dd4f32aeSBjoern A. Zeeb
406dd4f32aeSBjoern A. Zeeb ep->ep_ops.ep_rx_complete(ab, skb);
407dd4f32aeSBjoern A. Zeeb
408dd4f32aeSBjoern A. Zeeb /* poll tx completion for interrupt disabled CE's */
409dd4f32aeSBjoern A. Zeeb ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id);
410dd4f32aeSBjoern A. Zeeb
411dd4f32aeSBjoern A. Zeeb /* skb is now owned by the rx completion handler */
412dd4f32aeSBjoern A. Zeeb skb = NULL;
413dd4f32aeSBjoern A. Zeeb out:
414dd4f32aeSBjoern A. Zeeb kfree_skb(skb);
415dd4f32aeSBjoern A. Zeeb }
416dd4f32aeSBjoern A. Zeeb
ath11k_htc_control_rx_complete(struct ath11k_base * ab,struct sk_buff * skb)417dd4f32aeSBjoern A. Zeeb static void ath11k_htc_control_rx_complete(struct ath11k_base *ab,
418dd4f32aeSBjoern A. Zeeb struct sk_buff *skb)
419dd4f32aeSBjoern A. Zeeb {
420dd4f32aeSBjoern A. Zeeb /* This is unexpected. FW is not supposed to send regular rx on this
421dd4f32aeSBjoern A. Zeeb * endpoint.
422dd4f32aeSBjoern A. Zeeb */
423dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "unexpected htc rx\n");
424dd4f32aeSBjoern A. Zeeb kfree_skb(skb);
425dd4f32aeSBjoern A. Zeeb }
426dd4f32aeSBjoern A. Zeeb
htc_service_name(enum ath11k_htc_svc_id id)427dd4f32aeSBjoern A. Zeeb static const char *htc_service_name(enum ath11k_htc_svc_id id)
428dd4f32aeSBjoern A. Zeeb {
429dd4f32aeSBjoern A. Zeeb switch (id) {
430dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_RESERVED:
431dd4f32aeSBjoern A. Zeeb return "Reserved";
432dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_RSVD_CTRL:
433dd4f32aeSBjoern A. Zeeb return "Control";
434dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_CONTROL:
435dd4f32aeSBjoern A. Zeeb return "WMI";
436dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_DATA_BE:
437dd4f32aeSBjoern A. Zeeb return "DATA BE";
438dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_DATA_BK:
439dd4f32aeSBjoern A. Zeeb return "DATA BK";
440dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_DATA_VI:
441dd4f32aeSBjoern A. Zeeb return "DATA VI";
442dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_DATA_VO:
443dd4f32aeSBjoern A. Zeeb return "DATA VO";
444dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1:
445dd4f32aeSBjoern A. Zeeb return "WMI MAC1";
446dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2:
447dd4f32aeSBjoern A. Zeeb return "WMI MAC2";
448dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_NMI_CONTROL:
449dd4f32aeSBjoern A. Zeeb return "NMI Control";
450dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_NMI_DATA:
451dd4f32aeSBjoern A. Zeeb return "NMI Data";
452dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_HTT_DATA_MSG:
453dd4f32aeSBjoern A. Zeeb return "HTT Data";
454dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS:
455dd4f32aeSBjoern A. Zeeb return "RAW";
456dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_IPA_TX:
457dd4f32aeSBjoern A. Zeeb return "IPA TX";
458dd4f32aeSBjoern A. Zeeb case ATH11K_HTC_SVC_ID_PKT_LOG:
459dd4f32aeSBjoern A. Zeeb return "PKT LOG";
460dd4f32aeSBjoern A. Zeeb }
461dd4f32aeSBjoern A. Zeeb
462dd4f32aeSBjoern A. Zeeb return "Unknown";
463dd4f32aeSBjoern A. Zeeb }
464dd4f32aeSBjoern A. Zeeb
ath11k_htc_reset_endpoint_states(struct ath11k_htc * htc)465dd4f32aeSBjoern A. Zeeb static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc)
466dd4f32aeSBjoern A. Zeeb {
467dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep;
468dd4f32aeSBjoern A. Zeeb int i;
469dd4f32aeSBjoern A. Zeeb
470dd4f32aeSBjoern A. Zeeb for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) {
471dd4f32aeSBjoern A. Zeeb ep = &htc->endpoint[i];
472dd4f32aeSBjoern A. Zeeb ep->service_id = ATH11K_HTC_SVC_ID_UNUSED;
473dd4f32aeSBjoern A. Zeeb ep->max_ep_message_len = 0;
474dd4f32aeSBjoern A. Zeeb ep->max_tx_queue_depth = 0;
475dd4f32aeSBjoern A. Zeeb ep->eid = i;
476dd4f32aeSBjoern A. Zeeb ep->htc = htc;
477dd4f32aeSBjoern A. Zeeb ep->tx_credit_flow_enabled = true;
478dd4f32aeSBjoern A. Zeeb }
479dd4f32aeSBjoern A. Zeeb }
480dd4f32aeSBjoern A. Zeeb
ath11k_htc_get_credit_allocation(struct ath11k_htc * htc,u16 service_id)481dd4f32aeSBjoern A. Zeeb static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc,
482dd4f32aeSBjoern A. Zeeb u16 service_id)
483dd4f32aeSBjoern A. Zeeb {
484dd4f32aeSBjoern A. Zeeb u8 i, allocation = 0;
485dd4f32aeSBjoern A. Zeeb
486dd4f32aeSBjoern A. Zeeb for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
487dd4f32aeSBjoern A. Zeeb if (htc->service_alloc_table[i].service_id == service_id) {
488dd4f32aeSBjoern A. Zeeb allocation =
489dd4f32aeSBjoern A. Zeeb htc->service_alloc_table[i].credit_allocation;
490dd4f32aeSBjoern A. Zeeb }
491dd4f32aeSBjoern A. Zeeb }
492dd4f32aeSBjoern A. Zeeb
493dd4f32aeSBjoern A. Zeeb return allocation;
494dd4f32aeSBjoern A. Zeeb }
495dd4f32aeSBjoern A. Zeeb
ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc * htc)496dd4f32aeSBjoern A. Zeeb static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc)
497dd4f32aeSBjoern A. Zeeb {
498dd4f32aeSBjoern A. Zeeb struct ath11k_htc_svc_tx_credits *serv_entry;
499dd4f32aeSBjoern A. Zeeb u32 svc_id[] = {
500dd4f32aeSBjoern A. Zeeb ATH11K_HTC_SVC_ID_WMI_CONTROL,
501dd4f32aeSBjoern A. Zeeb ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1,
502dd4f32aeSBjoern A. Zeeb ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2,
503dd4f32aeSBjoern A. Zeeb };
504dd4f32aeSBjoern A. Zeeb int i, credits;
505dd4f32aeSBjoern A. Zeeb
506dd4f32aeSBjoern A. Zeeb credits = htc->total_transmit_credits;
507dd4f32aeSBjoern A. Zeeb serv_entry = htc->service_alloc_table;
508dd4f32aeSBjoern A. Zeeb
509dd4f32aeSBjoern A. Zeeb if ((htc->wmi_ep_count == 0) ||
510dd4f32aeSBjoern A. Zeeb (htc->wmi_ep_count > ARRAY_SIZE(svc_id)))
511dd4f32aeSBjoern A. Zeeb return -EINVAL;
512dd4f32aeSBjoern A. Zeeb
513dd4f32aeSBjoern A. Zeeb /* Divide credits among number of endpoints for WMI */
514dd4f32aeSBjoern A. Zeeb credits = credits / htc->wmi_ep_count;
515dd4f32aeSBjoern A. Zeeb for (i = 0; i < htc->wmi_ep_count; i++) {
516dd4f32aeSBjoern A. Zeeb serv_entry[i].service_id = svc_id[i];
517dd4f32aeSBjoern A. Zeeb serv_entry[i].credit_allocation = credits;
518dd4f32aeSBjoern A. Zeeb }
519dd4f32aeSBjoern A. Zeeb
520dd4f32aeSBjoern A. Zeeb return 0;
521dd4f32aeSBjoern A. Zeeb }
522dd4f32aeSBjoern A. Zeeb
ath11k_htc_wait_target(struct ath11k_htc * htc)523dd4f32aeSBjoern A. Zeeb int ath11k_htc_wait_target(struct ath11k_htc *htc)
524dd4f32aeSBjoern A. Zeeb {
525dd4f32aeSBjoern A. Zeeb int i, status = 0;
526dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
527dd4f32aeSBjoern A. Zeeb unsigned long time_left;
528dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ready *ready;
529dd4f32aeSBjoern A. Zeeb u16 message_id;
530dd4f32aeSBjoern A. Zeeb u16 credit_count;
531dd4f32aeSBjoern A. Zeeb u16 credit_size;
532dd4f32aeSBjoern A. Zeeb
533dd4f32aeSBjoern A. Zeeb time_left = wait_for_completion_timeout(&htc->ctl_resp,
534dd4f32aeSBjoern A. Zeeb ATH11K_HTC_WAIT_TIMEOUT_HZ);
535dd4f32aeSBjoern A. Zeeb if (!time_left) {
536dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "failed to receive control response completion, polling..\n");
537dd4f32aeSBjoern A. Zeeb
538dd4f32aeSBjoern A. Zeeb for (i = 0; i < ab->hw_params.ce_count; i++)
539dd4f32aeSBjoern A. Zeeb ath11k_ce_per_engine_service(htc->ab, i);
540dd4f32aeSBjoern A. Zeeb
541dd4f32aeSBjoern A. Zeeb time_left =
542dd4f32aeSBjoern A. Zeeb wait_for_completion_timeout(&htc->ctl_resp,
543dd4f32aeSBjoern A. Zeeb ATH11K_HTC_WAIT_TIMEOUT_HZ);
544dd4f32aeSBjoern A. Zeeb
545dd4f32aeSBjoern A. Zeeb if (!time_left)
546dd4f32aeSBjoern A. Zeeb status = -ETIMEDOUT;
547dd4f32aeSBjoern A. Zeeb }
548dd4f32aeSBjoern A. Zeeb
549dd4f32aeSBjoern A. Zeeb if (status < 0) {
550dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "ctl_resp never came in (%d)\n", status);
551dd4f32aeSBjoern A. Zeeb return status;
552dd4f32aeSBjoern A. Zeeb }
553dd4f32aeSBjoern A. Zeeb
554dd4f32aeSBjoern A. Zeeb if (htc->control_resp_len < sizeof(*ready)) {
555dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid HTC ready msg len:%d\n",
556dd4f32aeSBjoern A. Zeeb htc->control_resp_len);
557dd4f32aeSBjoern A. Zeeb return -ECOMM;
558dd4f32aeSBjoern A. Zeeb }
559dd4f32aeSBjoern A. Zeeb
560dd4f32aeSBjoern A. Zeeb ready = (struct ath11k_htc_ready *)htc->control_resp_buffer;
561dd4f32aeSBjoern A. Zeeb message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count);
562dd4f32aeSBjoern A. Zeeb credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT,
563dd4f32aeSBjoern A. Zeeb ready->id_credit_count);
564dd4f32aeSBjoern A. Zeeb credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep);
565dd4f32aeSBjoern A. Zeeb
566dd4f32aeSBjoern A. Zeeb if (message_id != ATH11K_HTC_MSG_READY_ID) {
567dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id);
568dd4f32aeSBjoern A. Zeeb return -ECOMM;
569dd4f32aeSBjoern A. Zeeb }
570dd4f32aeSBjoern A. Zeeb
571dd4f32aeSBjoern A. Zeeb htc->total_transmit_credits = credit_count;
572dd4f32aeSBjoern A. Zeeb htc->target_credit_size = credit_size;
573dd4f32aeSBjoern A. Zeeb
574dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC,
57528348caeSBjoern A. Zeeb "target ready total_transmit_credits %d target_credit_size %d\n",
576dd4f32aeSBjoern A. Zeeb htc->total_transmit_credits, htc->target_credit_size);
577dd4f32aeSBjoern A. Zeeb
578dd4f32aeSBjoern A. Zeeb if ((htc->total_transmit_credits == 0) ||
579dd4f32aeSBjoern A. Zeeb (htc->target_credit_size == 0)) {
580dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Invalid credit size received\n");
581dd4f32aeSBjoern A. Zeeb return -ECOMM;
582dd4f32aeSBjoern A. Zeeb }
583dd4f32aeSBjoern A. Zeeb
584dd4f32aeSBjoern A. Zeeb /* For QCA6390, wmi endpoint uses 1 credit to avoid
585dd4f32aeSBjoern A. Zeeb * back-to-back write.
586dd4f32aeSBjoern A. Zeeb */
587dd4f32aeSBjoern A. Zeeb if (ab->hw_params.supports_shadow_regs)
588dd4f32aeSBjoern A. Zeeb htc->total_transmit_credits = 1;
589dd4f32aeSBjoern A. Zeeb
590dd4f32aeSBjoern A. Zeeb ath11k_htc_setup_target_buffer_assignments(htc);
591dd4f32aeSBjoern A. Zeeb
592dd4f32aeSBjoern A. Zeeb return 0;
593dd4f32aeSBjoern A. Zeeb }
594dd4f32aeSBjoern A. Zeeb
ath11k_htc_connect_service(struct ath11k_htc * htc,struct ath11k_htc_svc_conn_req * conn_req,struct ath11k_htc_svc_conn_resp * conn_resp)595dd4f32aeSBjoern A. Zeeb int ath11k_htc_connect_service(struct ath11k_htc *htc,
596dd4f32aeSBjoern A. Zeeb struct ath11k_htc_svc_conn_req *conn_req,
597dd4f32aeSBjoern A. Zeeb struct ath11k_htc_svc_conn_resp *conn_resp)
598dd4f32aeSBjoern A. Zeeb {
599dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
600dd4f32aeSBjoern A. Zeeb struct ath11k_htc_conn_svc *req_msg;
601dd4f32aeSBjoern A. Zeeb struct ath11k_htc_conn_svc_resp resp_msg_dummy;
602dd4f32aeSBjoern A. Zeeb struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;
603dd4f32aeSBjoern A. Zeeb enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT;
604dd4f32aeSBjoern A. Zeeb struct ath11k_htc_ep *ep;
605dd4f32aeSBjoern A. Zeeb struct sk_buff *skb;
606dd4f32aeSBjoern A. Zeeb unsigned int max_msg_size = 0;
607dd4f32aeSBjoern A. Zeeb int length, status;
608dd4f32aeSBjoern A. Zeeb unsigned long time_left;
609dd4f32aeSBjoern A. Zeeb bool disable_credit_flow_ctrl = false;
610dd4f32aeSBjoern A. Zeeb u16 message_id, service_id, flags = 0;
611dd4f32aeSBjoern A. Zeeb u8 tx_alloc = 0;
612dd4f32aeSBjoern A. Zeeb
613dd4f32aeSBjoern A. Zeeb /* special case for HTC pseudo control service */
614dd4f32aeSBjoern A. Zeeb if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) {
615dd4f32aeSBjoern A. Zeeb disable_credit_flow_ctrl = true;
616dd4f32aeSBjoern A. Zeeb assigned_eid = ATH11K_HTC_EP_0;
617dd4f32aeSBjoern A. Zeeb max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN;
618dd4f32aeSBjoern A. Zeeb memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
619dd4f32aeSBjoern A. Zeeb goto setup;
620dd4f32aeSBjoern A. Zeeb }
621dd4f32aeSBjoern A. Zeeb
622dd4f32aeSBjoern A. Zeeb tx_alloc = ath11k_htc_get_credit_allocation(htc,
623dd4f32aeSBjoern A. Zeeb conn_req->service_id);
624dd4f32aeSBjoern A. Zeeb if (!tx_alloc)
625dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_BOOT,
62628348caeSBjoern A. Zeeb "htc service %s does not allocate target credits\n",
627dd4f32aeSBjoern A. Zeeb htc_service_name(conn_req->service_id));
628dd4f32aeSBjoern A. Zeeb
629dd4f32aeSBjoern A. Zeeb skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
630dd4f32aeSBjoern A. Zeeb if (!skb) {
631dd4f32aeSBjoern A. Zeeb ath11k_warn(ab, "Failed to allocate HTC packet\n");
632dd4f32aeSBjoern A. Zeeb return -ENOMEM;
633dd4f32aeSBjoern A. Zeeb }
634dd4f32aeSBjoern A. Zeeb
635dd4f32aeSBjoern A. Zeeb length = sizeof(*req_msg);
636dd4f32aeSBjoern A. Zeeb skb_put(skb, length);
637dd4f32aeSBjoern A. Zeeb memset(skb->data, 0, length);
638dd4f32aeSBjoern A. Zeeb
639dd4f32aeSBjoern A. Zeeb req_msg = (struct ath11k_htc_conn_svc *)skb->data;
640dd4f32aeSBjoern A. Zeeb req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID,
641dd4f32aeSBjoern A. Zeeb ATH11K_HTC_MSG_CONNECT_SERVICE_ID);
642dd4f32aeSBjoern A. Zeeb
643dd4f32aeSBjoern A. Zeeb flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc);
644dd4f32aeSBjoern A. Zeeb
645dd4f32aeSBjoern A. Zeeb /* Only enable credit flow control for WMI ctrl service */
646dd4f32aeSBjoern A. Zeeb if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL ||
647dd4f32aeSBjoern A. Zeeb conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||
648dd4f32aeSBjoern A. Zeeb conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {
649dd4f32aeSBjoern A. Zeeb flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
650dd4f32aeSBjoern A. Zeeb disable_credit_flow_ctrl = true;
651dd4f32aeSBjoern A. Zeeb }
652dd4f32aeSBjoern A. Zeeb
653dd4f32aeSBjoern A. Zeeb if (!ab->hw_params.credit_flow) {
654dd4f32aeSBjoern A. Zeeb flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
655dd4f32aeSBjoern A. Zeeb disable_credit_flow_ctrl = true;
656dd4f32aeSBjoern A. Zeeb }
657dd4f32aeSBjoern A. Zeeb
658dd4f32aeSBjoern A. Zeeb req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags);
659dd4f32aeSBjoern A. Zeeb req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID,
660dd4f32aeSBjoern A. Zeeb conn_req->service_id);
661dd4f32aeSBjoern A. Zeeb
662dd4f32aeSBjoern A. Zeeb reinit_completion(&htc->ctl_resp);
663dd4f32aeSBjoern A. Zeeb
664dd4f32aeSBjoern A. Zeeb status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
665dd4f32aeSBjoern A. Zeeb if (status) {
666dd4f32aeSBjoern A. Zeeb kfree_skb(skb);
667dd4f32aeSBjoern A. Zeeb return status;
668dd4f32aeSBjoern A. Zeeb }
669dd4f32aeSBjoern A. Zeeb
670dd4f32aeSBjoern A. Zeeb /* wait for response */
671dd4f32aeSBjoern A. Zeeb time_left = wait_for_completion_timeout(&htc->ctl_resp,
672dd4f32aeSBjoern A. Zeeb ATH11K_HTC_CONN_SVC_TIMEOUT_HZ);
673dd4f32aeSBjoern A. Zeeb if (!time_left) {
674dd4f32aeSBjoern A. Zeeb ath11k_err(ab, "Service connect timeout\n");
675dd4f32aeSBjoern A. Zeeb return -ETIMEDOUT;
676dd4f32aeSBjoern A. Zeeb }
677dd4f32aeSBjoern A. Zeeb
678dd4f32aeSBjoern A. Zeeb /* we controlled the buffer creation, it's aligned */
679dd4f32aeSBjoern A. Zeeb resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer;
680dd4f32aeSBjoern A. Zeeb message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id);
681dd4f32aeSBjoern A. Zeeb service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID,
682dd4f32aeSBjoern A. Zeeb resp_msg->msg_svc_id);
683dd4f32aeSBjoern A. Zeeb
684dd4f32aeSBjoern A. Zeeb if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
685dd4f32aeSBjoern A. Zeeb (htc->control_resp_len < sizeof(*resp_msg))) {
686dd4f32aeSBjoern A. Zeeb ath11k_err(ab, "Invalid resp message ID 0x%x", message_id);
687dd4f32aeSBjoern A. Zeeb return -EPROTO;
688dd4f32aeSBjoern A. Zeeb }
689dd4f32aeSBjoern A. Zeeb
690dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC,
69128348caeSBjoern A. Zeeb "service %s connect response status 0x%lx assigned ep 0x%lx\n",
692dd4f32aeSBjoern A. Zeeb htc_service_name(service_id),
693dd4f32aeSBjoern A. Zeeb FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len),
694dd4f32aeSBjoern A. Zeeb FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len));
695dd4f32aeSBjoern A. Zeeb
696dd4f32aeSBjoern A. Zeeb conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS,
697dd4f32aeSBjoern A. Zeeb resp_msg->flags_len);
698dd4f32aeSBjoern A. Zeeb
699dd4f32aeSBjoern A. Zeeb /* check response status */
700dd4f32aeSBjoern A. Zeeb if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) {
701dd4f32aeSBjoern A. Zeeb ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n",
702dd4f32aeSBjoern A. Zeeb htc_service_name(service_id),
703dd4f32aeSBjoern A. Zeeb conn_resp->connect_resp_code);
704dd4f32aeSBjoern A. Zeeb return -EPROTO;
705dd4f32aeSBjoern A. Zeeb }
706dd4f32aeSBjoern A. Zeeb
707dd4f32aeSBjoern A. Zeeb assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET(
708dd4f32aeSBjoern A. Zeeb HTC_SVC_RESP_MSG_ENDPOINTID,
709dd4f32aeSBjoern A. Zeeb resp_msg->flags_len);
710dd4f32aeSBjoern A. Zeeb
711dd4f32aeSBjoern A. Zeeb max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
712dd4f32aeSBjoern A. Zeeb resp_msg->flags_len);
713dd4f32aeSBjoern A. Zeeb
714dd4f32aeSBjoern A. Zeeb setup:
715dd4f32aeSBjoern A. Zeeb
716dd4f32aeSBjoern A. Zeeb if (assigned_eid >= ATH11K_HTC_EP_COUNT)
717dd4f32aeSBjoern A. Zeeb return -EPROTO;
718dd4f32aeSBjoern A. Zeeb
719dd4f32aeSBjoern A. Zeeb if (max_msg_size == 0)
720dd4f32aeSBjoern A. Zeeb return -EPROTO;
721dd4f32aeSBjoern A. Zeeb
722dd4f32aeSBjoern A. Zeeb ep = &htc->endpoint[assigned_eid];
723dd4f32aeSBjoern A. Zeeb ep->eid = assigned_eid;
724dd4f32aeSBjoern A. Zeeb
725dd4f32aeSBjoern A. Zeeb if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED)
726dd4f32aeSBjoern A. Zeeb return -EPROTO;
727dd4f32aeSBjoern A. Zeeb
728dd4f32aeSBjoern A. Zeeb /* return assigned endpoint to caller */
729dd4f32aeSBjoern A. Zeeb conn_resp->eid = assigned_eid;
730dd4f32aeSBjoern A. Zeeb conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
731dd4f32aeSBjoern A. Zeeb resp_msg->flags_len);
732dd4f32aeSBjoern A. Zeeb
733dd4f32aeSBjoern A. Zeeb /* setup the endpoint */
734dd4f32aeSBjoern A. Zeeb ep->service_id = conn_req->service_id;
735dd4f32aeSBjoern A. Zeeb ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
736dd4f32aeSBjoern A. Zeeb ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
737dd4f32aeSBjoern A. Zeeb resp_msg->flags_len);
738dd4f32aeSBjoern A. Zeeb ep->tx_credits = tx_alloc;
739dd4f32aeSBjoern A. Zeeb
740dd4f32aeSBjoern A. Zeeb /* copy all the callbacks */
741dd4f32aeSBjoern A. Zeeb ep->ep_ops = conn_req->ep_ops;
742dd4f32aeSBjoern A. Zeeb
743dd4f32aeSBjoern A. Zeeb status = ath11k_hif_map_service_to_pipe(htc->ab,
744dd4f32aeSBjoern A. Zeeb ep->service_id,
745dd4f32aeSBjoern A. Zeeb &ep->ul_pipe_id,
746dd4f32aeSBjoern A. Zeeb &ep->dl_pipe_id);
747dd4f32aeSBjoern A. Zeeb if (status)
748dd4f32aeSBjoern A. Zeeb return status;
749dd4f32aeSBjoern A. Zeeb
750dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_BOOT,
75128348caeSBjoern A. Zeeb "htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
752dd4f32aeSBjoern A. Zeeb htc_service_name(ep->service_id), ep->ul_pipe_id,
753dd4f32aeSBjoern A. Zeeb ep->dl_pipe_id, ep->eid);
754dd4f32aeSBjoern A. Zeeb
755dd4f32aeSBjoern A. Zeeb if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
756dd4f32aeSBjoern A. Zeeb ep->tx_credit_flow_enabled = false;
757dd4f32aeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_BOOT,
75828348caeSBjoern A. Zeeb "htc service '%s' eid %d tx flow control disabled\n",
759dd4f32aeSBjoern A. Zeeb htc_service_name(ep->service_id), assigned_eid);
760dd4f32aeSBjoern A. Zeeb }
761dd4f32aeSBjoern A. Zeeb
762dd4f32aeSBjoern A. Zeeb return status;
763dd4f32aeSBjoern A. Zeeb }
764dd4f32aeSBjoern A. Zeeb
ath11k_htc_start(struct ath11k_htc * htc)765dd4f32aeSBjoern A. Zeeb int ath11k_htc_start(struct ath11k_htc *htc)
766dd4f32aeSBjoern A. Zeeb {
767dd4f32aeSBjoern A. Zeeb struct sk_buff *skb;
768dd4f32aeSBjoern A. Zeeb int status = 0;
769dd4f32aeSBjoern A. Zeeb struct ath11k_base *ab = htc->ab;
770dd4f32aeSBjoern A. Zeeb struct ath11k_htc_setup_complete_extended *msg;
771dd4f32aeSBjoern A. Zeeb
772dd4f32aeSBjoern A. Zeeb skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
773dd4f32aeSBjoern A. Zeeb if (!skb)
774dd4f32aeSBjoern A. Zeeb return -ENOMEM;
775dd4f32aeSBjoern A. Zeeb
776dd4f32aeSBjoern A. Zeeb skb_put(skb, sizeof(*msg));
777dd4f32aeSBjoern A. Zeeb memset(skb->data, 0, skb->len);
778dd4f32aeSBjoern A. Zeeb
779dd4f32aeSBjoern A. Zeeb msg = (struct ath11k_htc_setup_complete_extended *)skb->data;
780dd4f32aeSBjoern A. Zeeb msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID,
781dd4f32aeSBjoern A. Zeeb ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID);
782dd4f32aeSBjoern A. Zeeb
783dd4f32aeSBjoern A. Zeeb if (ab->hw_params.credit_flow)
78428348caeSBjoern A. Zeeb ath11k_dbg(ab, ATH11K_DBG_HTC, "using tx credit flow control\n");
785dd4f32aeSBjoern A. Zeeb else
786dd4f32aeSBjoern A. Zeeb msg->flags |= ATH11K_GLOBAL_DISABLE_CREDIT_FLOW;
787dd4f32aeSBjoern A. Zeeb
788dd4f32aeSBjoern A. Zeeb status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
789dd4f32aeSBjoern A. Zeeb if (status) {
790dd4f32aeSBjoern A. Zeeb kfree_skb(skb);
791dd4f32aeSBjoern A. Zeeb return status;
792dd4f32aeSBjoern A. Zeeb }
793dd4f32aeSBjoern A. Zeeb
794dd4f32aeSBjoern A. Zeeb return 0;
795dd4f32aeSBjoern A. Zeeb }
796dd4f32aeSBjoern A. Zeeb
ath11k_htc_init(struct ath11k_base * ab)797dd4f32aeSBjoern A. Zeeb int ath11k_htc_init(struct ath11k_base *ab)
798dd4f32aeSBjoern A. Zeeb {
799dd4f32aeSBjoern A. Zeeb struct ath11k_htc *htc = &ab->htc;
800dd4f32aeSBjoern A. Zeeb struct ath11k_htc_svc_conn_req conn_req;
801dd4f32aeSBjoern A. Zeeb struct ath11k_htc_svc_conn_resp conn_resp;
802dd4f32aeSBjoern A. Zeeb int ret;
803dd4f32aeSBjoern A. Zeeb
804dd4f32aeSBjoern A. Zeeb spin_lock_init(&htc->tx_lock);
805dd4f32aeSBjoern A. Zeeb
806dd4f32aeSBjoern A. Zeeb ath11k_htc_reset_endpoint_states(htc);
807dd4f32aeSBjoern A. Zeeb
808dd4f32aeSBjoern A. Zeeb htc->ab = ab;
809dd4f32aeSBjoern A. Zeeb
810dd4f32aeSBjoern A. Zeeb switch (ab->wmi_ab.preferred_hw_mode) {
811dd4f32aeSBjoern A. Zeeb case WMI_HOST_HW_MODE_SINGLE:
812dd4f32aeSBjoern A. Zeeb htc->wmi_ep_count = 1;
813dd4f32aeSBjoern A. Zeeb break;
814dd4f32aeSBjoern A. Zeeb case WMI_HOST_HW_MODE_DBS:
815dd4f32aeSBjoern A. Zeeb case WMI_HOST_HW_MODE_DBS_OR_SBS:
816dd4f32aeSBjoern A. Zeeb htc->wmi_ep_count = 2;
817dd4f32aeSBjoern A. Zeeb break;
818dd4f32aeSBjoern A. Zeeb case WMI_HOST_HW_MODE_DBS_SBS:
819dd4f32aeSBjoern A. Zeeb htc->wmi_ep_count = 3;
820dd4f32aeSBjoern A. Zeeb break;
821dd4f32aeSBjoern A. Zeeb default:
822dd4f32aeSBjoern A. Zeeb htc->wmi_ep_count = ab->hw_params.max_radios;
823dd4f32aeSBjoern A. Zeeb break;
824dd4f32aeSBjoern A. Zeeb }
825dd4f32aeSBjoern A. Zeeb
826dd4f32aeSBjoern A. Zeeb /* setup our pseudo HTC control endpoint connection */
827dd4f32aeSBjoern A. Zeeb memset(&conn_req, 0, sizeof(conn_req));
828dd4f32aeSBjoern A. Zeeb memset(&conn_resp, 0, sizeof(conn_resp));
829dd4f32aeSBjoern A. Zeeb conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete;
830dd4f32aeSBjoern A. Zeeb conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete;
831dd4f32aeSBjoern A. Zeeb conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS;
832dd4f32aeSBjoern A. Zeeb conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL;
833dd4f32aeSBjoern A. Zeeb
834dd4f32aeSBjoern A. Zeeb /* connect fake service */
835dd4f32aeSBjoern A. Zeeb ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp);
836dd4f32aeSBjoern A. Zeeb if (ret) {
837dd4f32aeSBjoern A. Zeeb ath11k_err(ab, "could not connect to htc service (%d)\n", ret);
838dd4f32aeSBjoern A. Zeeb return ret;
839dd4f32aeSBjoern A. Zeeb }
840dd4f32aeSBjoern A. Zeeb
841dd4f32aeSBjoern A. Zeeb init_completion(&htc->ctl_resp);
842dd4f32aeSBjoern A. Zeeb
843dd4f32aeSBjoern A. Zeeb return 0;
844dd4f32aeSBjoern A. Zeeb }
845