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