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