xref: /freebsd/sys/contrib/dev/athk/ath11k/htc.c (revision 28348cae)
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