xref: /freebsd/sys/contrib/dev/athk/ath12k/htc.c (revision 5c1def83)
15c1def83SBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
25c1def83SBjoern A. Zeeb /*
35c1def83SBjoern A. Zeeb  * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
45c1def83SBjoern A. Zeeb  * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
55c1def83SBjoern A. Zeeb  */
65c1def83SBjoern A. Zeeb #include <linux/skbuff.h>
75c1def83SBjoern A. Zeeb #include <linux/ctype.h>
85c1def83SBjoern A. Zeeb 
95c1def83SBjoern A. Zeeb #include "debug.h"
105c1def83SBjoern A. Zeeb #include "hif.h"
115c1def83SBjoern A. Zeeb 
ath12k_htc_alloc_skb(struct ath12k_base * ab,int size)125c1def83SBjoern A. Zeeb struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size)
135c1def83SBjoern A. Zeeb {
145c1def83SBjoern A. Zeeb 	struct sk_buff *skb;
155c1def83SBjoern A. Zeeb 
165c1def83SBjoern A. Zeeb 	skb = dev_alloc_skb(size + sizeof(struct ath12k_htc_hdr));
175c1def83SBjoern A. Zeeb 	if (!skb)
185c1def83SBjoern A. Zeeb 		return NULL;
195c1def83SBjoern A. Zeeb 
205c1def83SBjoern A. Zeeb 	skb_reserve(skb, sizeof(struct ath12k_htc_hdr));
215c1def83SBjoern A. Zeeb 
225c1def83SBjoern A. Zeeb 	/* FW/HTC requires 4-byte aligned streams */
235c1def83SBjoern A. Zeeb 	if (!IS_ALIGNED((unsigned long)skb->data, 4))
245c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Unaligned HTC tx skb\n");
255c1def83SBjoern A. Zeeb 
265c1def83SBjoern A. Zeeb 	return skb;
275c1def83SBjoern A. Zeeb }
285c1def83SBjoern A. Zeeb 
ath12k_htc_control_tx_complete(struct ath12k_base * ab,struct sk_buff * skb)295c1def83SBjoern A. Zeeb static void ath12k_htc_control_tx_complete(struct ath12k_base *ab,
305c1def83SBjoern A. Zeeb 					   struct sk_buff *skb)
315c1def83SBjoern A. Zeeb {
325c1def83SBjoern A. Zeeb 	kfree_skb(skb);
335c1def83SBjoern A. Zeeb }
345c1def83SBjoern A. Zeeb 
ath12k_htc_build_tx_ctrl_skb(void)355c1def83SBjoern A. Zeeb static struct sk_buff *ath12k_htc_build_tx_ctrl_skb(void)
365c1def83SBjoern A. Zeeb {
375c1def83SBjoern A. Zeeb 	struct sk_buff *skb;
385c1def83SBjoern A. Zeeb 	struct ath12k_skb_cb *skb_cb;
395c1def83SBjoern A. Zeeb 
405c1def83SBjoern A. Zeeb 	skb = dev_alloc_skb(ATH12K_HTC_CONTROL_BUFFER_SIZE);
415c1def83SBjoern A. Zeeb 	if (!skb)
425c1def83SBjoern A. Zeeb 		return NULL;
435c1def83SBjoern A. Zeeb 
445c1def83SBjoern A. Zeeb 	skb_reserve(skb, sizeof(struct ath12k_htc_hdr));
455c1def83SBjoern A. Zeeb 	WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
465c1def83SBjoern A. Zeeb 
475c1def83SBjoern A. Zeeb 	skb_cb = ATH12K_SKB_CB(skb);
485c1def83SBjoern A. Zeeb 	memset(skb_cb, 0, sizeof(*skb_cb));
495c1def83SBjoern A. Zeeb 
505c1def83SBjoern A. Zeeb 	return skb;
515c1def83SBjoern A. Zeeb }
525c1def83SBjoern A. Zeeb 
ath12k_htc_prepare_tx_skb(struct ath12k_htc_ep * ep,struct sk_buff * skb)535c1def83SBjoern A. Zeeb static void ath12k_htc_prepare_tx_skb(struct ath12k_htc_ep *ep,
545c1def83SBjoern A. Zeeb 				      struct sk_buff *skb)
555c1def83SBjoern A. Zeeb {
565c1def83SBjoern A. Zeeb 	struct ath12k_htc_hdr *hdr;
575c1def83SBjoern A. Zeeb 
585c1def83SBjoern A. Zeeb 	hdr = (struct ath12k_htc_hdr *)skb->data;
595c1def83SBjoern A. Zeeb 
605c1def83SBjoern A. Zeeb 	memset(hdr, 0, sizeof(*hdr));
615c1def83SBjoern A. Zeeb 	hdr->htc_info = le32_encode_bits(ep->eid, HTC_HDR_ENDPOINTID) |
625c1def83SBjoern A. Zeeb 			le32_encode_bits((skb->len - sizeof(*hdr)),
635c1def83SBjoern A. Zeeb 					 HTC_HDR_PAYLOADLEN);
645c1def83SBjoern A. Zeeb 
655c1def83SBjoern A. Zeeb 	if (ep->tx_credit_flow_enabled)
665c1def83SBjoern A. Zeeb 		hdr->htc_info |= le32_encode_bits(ATH12K_HTC_FLAG_NEED_CREDIT_UPDATE,
675c1def83SBjoern A. Zeeb 						  HTC_HDR_FLAGS);
685c1def83SBjoern A. Zeeb 
695c1def83SBjoern A. Zeeb 	spin_lock_bh(&ep->htc->tx_lock);
705c1def83SBjoern A. Zeeb 	hdr->ctrl_info = le32_encode_bits(ep->seq_no++, HTC_HDR_CONTROLBYTES1);
715c1def83SBjoern A. Zeeb 	spin_unlock_bh(&ep->htc->tx_lock);
725c1def83SBjoern A. Zeeb }
735c1def83SBjoern A. Zeeb 
ath12k_htc_send(struct ath12k_htc * htc,enum ath12k_htc_ep_id eid,struct sk_buff * skb)745c1def83SBjoern A. Zeeb int ath12k_htc_send(struct ath12k_htc *htc,
755c1def83SBjoern A. Zeeb 		    enum ath12k_htc_ep_id eid,
765c1def83SBjoern A. Zeeb 		    struct sk_buff *skb)
775c1def83SBjoern A. Zeeb {
785c1def83SBjoern A. Zeeb 	struct ath12k_htc_ep *ep = &htc->endpoint[eid];
795c1def83SBjoern A. Zeeb 	struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb);
805c1def83SBjoern A. Zeeb 	struct device *dev = htc->ab->dev;
815c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
825c1def83SBjoern A. Zeeb 	int credits = 0;
835c1def83SBjoern A. Zeeb 	int ret;
845c1def83SBjoern A. Zeeb 
855c1def83SBjoern A. Zeeb 	if (eid >= ATH12K_HTC_EP_COUNT) {
865c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Invalid endpoint id: %d\n", eid);
875c1def83SBjoern A. Zeeb 		return -ENOENT;
885c1def83SBjoern A. Zeeb 	}
895c1def83SBjoern A. Zeeb 
905c1def83SBjoern A. Zeeb 	skb_push(skb, sizeof(struct ath12k_htc_hdr));
915c1def83SBjoern A. Zeeb 
925c1def83SBjoern A. Zeeb 	if (ep->tx_credit_flow_enabled) {
935c1def83SBjoern A. Zeeb 		credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
945c1def83SBjoern A. Zeeb 		spin_lock_bh(&htc->tx_lock);
955c1def83SBjoern A. Zeeb 		if (ep->tx_credits < credits) {
965c1def83SBjoern A. Zeeb 			ath12k_dbg(ab, ATH12K_DBG_HTC,
975c1def83SBjoern A. Zeeb 				   "htc insufficient credits ep %d required %d available %d\n",
985c1def83SBjoern A. Zeeb 				   eid, credits, ep->tx_credits);
995c1def83SBjoern A. Zeeb 			spin_unlock_bh(&htc->tx_lock);
1005c1def83SBjoern A. Zeeb 			ret = -EAGAIN;
1015c1def83SBjoern A. Zeeb 			goto err_pull;
1025c1def83SBjoern A. Zeeb 		}
1035c1def83SBjoern A. Zeeb 		ep->tx_credits -= credits;
1045c1def83SBjoern A. Zeeb 		ath12k_dbg(ab, ATH12K_DBG_HTC,
1055c1def83SBjoern A. Zeeb 			   "htc ep %d consumed %d credits (total %d)\n",
1065c1def83SBjoern A. Zeeb 			   eid, credits, ep->tx_credits);
1075c1def83SBjoern A. Zeeb 		spin_unlock_bh(&htc->tx_lock);
1085c1def83SBjoern A. Zeeb 	}
1095c1def83SBjoern A. Zeeb 
1105c1def83SBjoern A. Zeeb 	ath12k_htc_prepare_tx_skb(ep, skb);
1115c1def83SBjoern A. Zeeb 
1125c1def83SBjoern A. Zeeb 	skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
1135c1def83SBjoern A. Zeeb 	ret = dma_mapping_error(dev, skb_cb->paddr);
1145c1def83SBjoern A. Zeeb 	if (ret) {
1155c1def83SBjoern A. Zeeb 		ret = -EIO;
1165c1def83SBjoern A. Zeeb 		goto err_credits;
1175c1def83SBjoern A. Zeeb 	}
1185c1def83SBjoern A. Zeeb 
1195c1def83SBjoern A. Zeeb 	ret = ath12k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid);
1205c1def83SBjoern A. Zeeb 	if (ret)
1215c1def83SBjoern A. Zeeb 		goto err_unmap;
1225c1def83SBjoern A. Zeeb 
1235c1def83SBjoern A. Zeeb 	return 0;
1245c1def83SBjoern A. Zeeb 
1255c1def83SBjoern A. Zeeb err_unmap:
1265c1def83SBjoern A. Zeeb 	dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
1275c1def83SBjoern A. Zeeb err_credits:
1285c1def83SBjoern A. Zeeb 	if (ep->tx_credit_flow_enabled) {
1295c1def83SBjoern A. Zeeb 		spin_lock_bh(&htc->tx_lock);
1305c1def83SBjoern A. Zeeb 		ep->tx_credits += credits;
1315c1def83SBjoern A. Zeeb 		ath12k_dbg(ab, ATH12K_DBG_HTC,
1325c1def83SBjoern A. Zeeb 			   "htc ep %d reverted %d credits back (total %d)\n",
1335c1def83SBjoern A. Zeeb 			   eid, credits, ep->tx_credits);
1345c1def83SBjoern A. Zeeb 		spin_unlock_bh(&htc->tx_lock);
1355c1def83SBjoern A. Zeeb 
1365c1def83SBjoern A. Zeeb 		if (ep->ep_ops.ep_tx_credits)
1375c1def83SBjoern A. Zeeb 			ep->ep_ops.ep_tx_credits(htc->ab);
1385c1def83SBjoern A. Zeeb 	}
1395c1def83SBjoern A. Zeeb err_pull:
1405c1def83SBjoern A. Zeeb 	skb_pull(skb, sizeof(struct ath12k_htc_hdr));
1415c1def83SBjoern A. Zeeb 	return ret;
1425c1def83SBjoern A. Zeeb }
1435c1def83SBjoern A. Zeeb 
1445c1def83SBjoern A. Zeeb static void
ath12k_htc_process_credit_report(struct ath12k_htc * htc,const struct ath12k_htc_credit_report * report,int len,enum ath12k_htc_ep_id eid)1455c1def83SBjoern A. Zeeb ath12k_htc_process_credit_report(struct ath12k_htc *htc,
1465c1def83SBjoern A. Zeeb 				 const struct ath12k_htc_credit_report *report,
1475c1def83SBjoern A. Zeeb 				 int len,
1485c1def83SBjoern A. Zeeb 				 enum ath12k_htc_ep_id eid)
1495c1def83SBjoern A. Zeeb {
1505c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
1515c1def83SBjoern A. Zeeb 	struct ath12k_htc_ep *ep;
1525c1def83SBjoern A. Zeeb 	int i, n_reports;
1535c1def83SBjoern A. Zeeb 
1545c1def83SBjoern A. Zeeb 	if (len % sizeof(*report))
1555c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Uneven credit report len %d", len);
1565c1def83SBjoern A. Zeeb 
1575c1def83SBjoern A. Zeeb 	n_reports = len / sizeof(*report);
1585c1def83SBjoern A. Zeeb 
1595c1def83SBjoern A. Zeeb 	spin_lock_bh(&htc->tx_lock);
1605c1def83SBjoern A. Zeeb 	for (i = 0; i < n_reports; i++, report++) {
1615c1def83SBjoern A. Zeeb 		if (report->eid >= ATH12K_HTC_EP_COUNT)
1625c1def83SBjoern A. Zeeb 			break;
1635c1def83SBjoern A. Zeeb 
1645c1def83SBjoern A. Zeeb 		ep = &htc->endpoint[report->eid];
1655c1def83SBjoern A. Zeeb 		ep->tx_credits += report->credits;
1665c1def83SBjoern A. Zeeb 
1675c1def83SBjoern A. Zeeb 		ath12k_dbg(ab, ATH12K_DBG_HTC, "htc ep %d got %d credits (total %d)\n",
1685c1def83SBjoern A. Zeeb 			   report->eid, report->credits, ep->tx_credits);
1695c1def83SBjoern A. Zeeb 
1705c1def83SBjoern A. Zeeb 		if (ep->ep_ops.ep_tx_credits) {
1715c1def83SBjoern A. Zeeb 			spin_unlock_bh(&htc->tx_lock);
1725c1def83SBjoern A. Zeeb 			ep->ep_ops.ep_tx_credits(htc->ab);
1735c1def83SBjoern A. Zeeb 			spin_lock_bh(&htc->tx_lock);
1745c1def83SBjoern A. Zeeb 		}
1755c1def83SBjoern A. Zeeb 	}
1765c1def83SBjoern A. Zeeb 	spin_unlock_bh(&htc->tx_lock);
1775c1def83SBjoern A. Zeeb }
1785c1def83SBjoern A. Zeeb 
ath12k_htc_process_trailer(struct ath12k_htc * htc,u8 * buffer,int length,enum ath12k_htc_ep_id src_eid)1795c1def83SBjoern A. Zeeb static int ath12k_htc_process_trailer(struct ath12k_htc *htc,
1805c1def83SBjoern A. Zeeb 				      u8 *buffer,
1815c1def83SBjoern A. Zeeb 				      int length,
1825c1def83SBjoern A. Zeeb 				      enum ath12k_htc_ep_id src_eid)
1835c1def83SBjoern A. Zeeb {
1845c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
1855c1def83SBjoern A. Zeeb 	int status = 0;
1865c1def83SBjoern A. Zeeb 	struct ath12k_htc_record *record;
1875c1def83SBjoern A. Zeeb 	size_t len;
1885c1def83SBjoern A. Zeeb 
1895c1def83SBjoern A. Zeeb 	while (length > 0) {
1905c1def83SBjoern A. Zeeb 		record = (struct ath12k_htc_record *)buffer;
1915c1def83SBjoern A. Zeeb 
1925c1def83SBjoern A. Zeeb 		if (length < sizeof(record->hdr)) {
1935c1def83SBjoern A. Zeeb 			status = -EINVAL;
1945c1def83SBjoern A. Zeeb 			break;
1955c1def83SBjoern A. Zeeb 		}
1965c1def83SBjoern A. Zeeb 
1975c1def83SBjoern A. Zeeb 		if (record->hdr.len > length) {
1985c1def83SBjoern A. Zeeb 			/* no room left in buffer for record */
1995c1def83SBjoern A. Zeeb 			ath12k_warn(ab, "Invalid record length: %d\n",
2005c1def83SBjoern A. Zeeb 				    record->hdr.len);
2015c1def83SBjoern A. Zeeb 			status = -EINVAL;
2025c1def83SBjoern A. Zeeb 			break;
2035c1def83SBjoern A. Zeeb 		}
2045c1def83SBjoern A. Zeeb 
2055c1def83SBjoern A. Zeeb 		switch (record->hdr.id) {
2065c1def83SBjoern A. Zeeb 		case ATH12K_HTC_RECORD_CREDITS:
2075c1def83SBjoern A. Zeeb 			len = sizeof(struct ath12k_htc_credit_report);
2085c1def83SBjoern A. Zeeb 			if (record->hdr.len < len) {
2095c1def83SBjoern A. Zeeb 				ath12k_warn(ab, "Credit report too long\n");
2105c1def83SBjoern A. Zeeb 				status = -EINVAL;
2115c1def83SBjoern A. Zeeb 				break;
2125c1def83SBjoern A. Zeeb 			}
2135c1def83SBjoern A. Zeeb 			ath12k_htc_process_credit_report(htc,
2145c1def83SBjoern A. Zeeb 							 record->credit_report,
2155c1def83SBjoern A. Zeeb 							 record->hdr.len,
2165c1def83SBjoern A. Zeeb 							 src_eid);
2175c1def83SBjoern A. Zeeb 			break;
2185c1def83SBjoern A. Zeeb 		default:
2195c1def83SBjoern A. Zeeb 			ath12k_warn(ab, "Unhandled record: id:%d length:%d\n",
2205c1def83SBjoern A. Zeeb 				    record->hdr.id, record->hdr.len);
2215c1def83SBjoern A. Zeeb 			break;
2225c1def83SBjoern A. Zeeb 		}
2235c1def83SBjoern A. Zeeb 
2245c1def83SBjoern A. Zeeb 		if (status)
2255c1def83SBjoern A. Zeeb 			break;
2265c1def83SBjoern A. Zeeb 
2275c1def83SBjoern A. Zeeb 		/* multiple records may be present in a trailer */
2285c1def83SBjoern A. Zeeb 		buffer += sizeof(record->hdr) + record->hdr.len;
2295c1def83SBjoern A. Zeeb 		length -= sizeof(record->hdr) + record->hdr.len;
2305c1def83SBjoern A. Zeeb 	}
2315c1def83SBjoern A. Zeeb 
2325c1def83SBjoern A. Zeeb 	return status;
2335c1def83SBjoern A. Zeeb }
2345c1def83SBjoern A. Zeeb 
ath12k_htc_suspend_complete(struct ath12k_base * ab,bool ack)2355c1def83SBjoern A. Zeeb static void ath12k_htc_suspend_complete(struct ath12k_base *ab, bool ack)
2365c1def83SBjoern A. Zeeb {
2375c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot suspend complete %d\n", ack);
2385c1def83SBjoern A. Zeeb 
2395c1def83SBjoern A. Zeeb 	if (ack)
2405c1def83SBjoern A. Zeeb 		set_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
2415c1def83SBjoern A. Zeeb 	else
2425c1def83SBjoern A. Zeeb 		clear_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
2435c1def83SBjoern A. Zeeb 
2445c1def83SBjoern A. Zeeb 	complete(&ab->htc_suspend);
2455c1def83SBjoern A. Zeeb }
2465c1def83SBjoern A. Zeeb 
ath12k_htc_rx_completion_handler(struct ath12k_base * ab,struct sk_buff * skb)2475c1def83SBjoern A. Zeeb void ath12k_htc_rx_completion_handler(struct ath12k_base *ab,
2485c1def83SBjoern A. Zeeb 				      struct sk_buff *skb)
2495c1def83SBjoern A. Zeeb {
2505c1def83SBjoern A. Zeeb 	int status = 0;
2515c1def83SBjoern A. Zeeb 	struct ath12k_htc *htc = &ab->htc;
2525c1def83SBjoern A. Zeeb 	struct ath12k_htc_hdr *hdr;
2535c1def83SBjoern A. Zeeb 	struct ath12k_htc_ep *ep;
2545c1def83SBjoern A. Zeeb 	u16 payload_len;
2555c1def83SBjoern A. Zeeb 	u32 trailer_len = 0;
2565c1def83SBjoern A. Zeeb 	size_t min_len;
2575c1def83SBjoern A. Zeeb 	u8 eid;
2585c1def83SBjoern A. Zeeb 	bool trailer_present;
2595c1def83SBjoern A. Zeeb 
2605c1def83SBjoern A. Zeeb 	hdr = (struct ath12k_htc_hdr *)skb->data;
2615c1def83SBjoern A. Zeeb 	skb_pull(skb, sizeof(*hdr));
2625c1def83SBjoern A. Zeeb 
2635c1def83SBjoern A. Zeeb 	eid = le32_get_bits(hdr->htc_info, HTC_HDR_ENDPOINTID);
2645c1def83SBjoern A. Zeeb 
2655c1def83SBjoern A. Zeeb 	if (eid >= ATH12K_HTC_EP_COUNT) {
2665c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "HTC Rx: invalid eid %d\n", eid);
2675c1def83SBjoern A. Zeeb 		goto out;
2685c1def83SBjoern A. Zeeb 	}
2695c1def83SBjoern A. Zeeb 
2705c1def83SBjoern A. Zeeb 	ep = &htc->endpoint[eid];
2715c1def83SBjoern A. Zeeb 
2725c1def83SBjoern A. Zeeb 	payload_len = le32_get_bits(hdr->htc_info, HTC_HDR_PAYLOADLEN);
2735c1def83SBjoern A. Zeeb 
2745c1def83SBjoern A. Zeeb 	if (payload_len + sizeof(*hdr) > ATH12K_HTC_MAX_LEN) {
2755c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "HTC rx frame too long, len: %zu\n",
2765c1def83SBjoern A. Zeeb 			    payload_len + sizeof(*hdr));
2775c1def83SBjoern A. Zeeb 		goto out;
2785c1def83SBjoern A. Zeeb 	}
2795c1def83SBjoern A. Zeeb 
2805c1def83SBjoern A. Zeeb 	if (skb->len < payload_len) {
2815c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n",
2825c1def83SBjoern A. Zeeb 			    skb->len, payload_len);
2835c1def83SBjoern A. Zeeb 		goto out;
2845c1def83SBjoern A. Zeeb 	}
2855c1def83SBjoern A. Zeeb 
2865c1def83SBjoern A. Zeeb 	/* get flags to check for trailer */
2875c1def83SBjoern A. Zeeb 	trailer_present = le32_get_bits(hdr->htc_info, HTC_HDR_FLAGS) &
2885c1def83SBjoern A. Zeeb 			  ATH12K_HTC_FLAG_TRAILER_PRESENT;
2895c1def83SBjoern A. Zeeb 
2905c1def83SBjoern A. Zeeb 	if (trailer_present) {
2915c1def83SBjoern A. Zeeb 		u8 *trailer;
2925c1def83SBjoern A. Zeeb 
2935c1def83SBjoern A. Zeeb 		trailer_len = le32_get_bits(hdr->ctrl_info,
2945c1def83SBjoern A. Zeeb 					    HTC_HDR_CONTROLBYTES0);
2955c1def83SBjoern A. Zeeb 		min_len = sizeof(struct ath12k_htc_record_hdr);
2965c1def83SBjoern A. Zeeb 
2975c1def83SBjoern A. Zeeb 		if ((trailer_len < min_len) ||
2985c1def83SBjoern A. Zeeb 		    (trailer_len > payload_len)) {
2995c1def83SBjoern A. Zeeb 			ath12k_warn(ab, "Invalid trailer length: %d\n",
3005c1def83SBjoern A. Zeeb 				    trailer_len);
3015c1def83SBjoern A. Zeeb 			goto out;
3025c1def83SBjoern A. Zeeb 		}
3035c1def83SBjoern A. Zeeb 
3045c1def83SBjoern A. Zeeb 		trailer = (u8 *)hdr;
3055c1def83SBjoern A. Zeeb 		trailer += sizeof(*hdr);
3065c1def83SBjoern A. Zeeb 		trailer += payload_len;
3075c1def83SBjoern A. Zeeb 		trailer -= trailer_len;
3085c1def83SBjoern A. Zeeb 		status = ath12k_htc_process_trailer(htc, trailer,
3095c1def83SBjoern A. Zeeb 						    trailer_len, eid);
3105c1def83SBjoern A. Zeeb 		if (status)
3115c1def83SBjoern A. Zeeb 			goto out;
3125c1def83SBjoern A. Zeeb 
3135c1def83SBjoern A. Zeeb 		skb_trim(skb, skb->len - trailer_len);
3145c1def83SBjoern A. Zeeb 	}
3155c1def83SBjoern A. Zeeb 
3165c1def83SBjoern A. Zeeb 	if (trailer_len >= payload_len)
3175c1def83SBjoern A. Zeeb 		/* zero length packet with trailer data, just drop these */
3185c1def83SBjoern A. Zeeb 		goto out;
3195c1def83SBjoern A. Zeeb 
3205c1def83SBjoern A. Zeeb 	if (eid == ATH12K_HTC_EP_0) {
3215c1def83SBjoern A. Zeeb 		struct ath12k_htc_msg *msg = (struct ath12k_htc_msg *)skb->data;
3225c1def83SBjoern A. Zeeb 
3235c1def83SBjoern A. Zeeb 		switch (le32_get_bits(msg->msg_svc_id, HTC_MSG_MESSAGEID)) {
3245c1def83SBjoern A. Zeeb 		case ATH12K_HTC_MSG_READY_ID:
3255c1def83SBjoern A. Zeeb 		case ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID:
3265c1def83SBjoern A. Zeeb 			/* handle HTC control message */
3275c1def83SBjoern A. Zeeb 			if (completion_done(&htc->ctl_resp)) {
3285c1def83SBjoern A. Zeeb 				/* this is a fatal error, target should not be
3295c1def83SBjoern A. Zeeb 				 * sending unsolicited messages on the ep 0
3305c1def83SBjoern A. Zeeb 				 */
3315c1def83SBjoern A. Zeeb 				ath12k_warn(ab, "HTC rx ctrl still processing\n");
3325c1def83SBjoern A. Zeeb 				complete(&htc->ctl_resp);
3335c1def83SBjoern A. Zeeb 				goto out;
3345c1def83SBjoern A. Zeeb 			}
3355c1def83SBjoern A. Zeeb 
3365c1def83SBjoern A. Zeeb 			htc->control_resp_len =
3375c1def83SBjoern A. Zeeb 				min_t(int, skb->len,
3385c1def83SBjoern A. Zeeb 				      ATH12K_HTC_MAX_CTRL_MSG_LEN);
3395c1def83SBjoern A. Zeeb 
3405c1def83SBjoern A. Zeeb 			memcpy(htc->control_resp_buffer, skb->data,
3415c1def83SBjoern A. Zeeb 			       htc->control_resp_len);
3425c1def83SBjoern A. Zeeb 
3435c1def83SBjoern A. Zeeb 			complete(&htc->ctl_resp);
3445c1def83SBjoern A. Zeeb 			break;
3455c1def83SBjoern A. Zeeb 		case ATH12K_HTC_MSG_SEND_SUSPEND_COMPLETE:
3465c1def83SBjoern A. Zeeb 			ath12k_htc_suspend_complete(ab, true);
3475c1def83SBjoern A. Zeeb 			break;
3485c1def83SBjoern A. Zeeb 		case ATH12K_HTC_MSG_NACK_SUSPEND:
3495c1def83SBjoern A. Zeeb 			ath12k_htc_suspend_complete(ab, false);
3505c1def83SBjoern A. Zeeb 			break;
3515c1def83SBjoern A. Zeeb 		case ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
3525c1def83SBjoern A. Zeeb 			break;
3535c1def83SBjoern A. Zeeb 		default:
3545c1def83SBjoern A. Zeeb 			ath12k_warn(ab, "ignoring unsolicited htc ep0 event %u\n",
3555c1def83SBjoern A. Zeeb 				    le32_get_bits(msg->msg_svc_id, HTC_MSG_MESSAGEID));
3565c1def83SBjoern A. Zeeb 			break;
3575c1def83SBjoern A. Zeeb 		}
3585c1def83SBjoern A. Zeeb 		goto out;
3595c1def83SBjoern A. Zeeb 	}
3605c1def83SBjoern A. Zeeb 
3615c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_HTC, "htc rx completion ep %d skb %pK\n",
3625c1def83SBjoern A. Zeeb 		   eid, skb);
3635c1def83SBjoern A. Zeeb 	ep->ep_ops.ep_rx_complete(ab, skb);
3645c1def83SBjoern A. Zeeb 
3655c1def83SBjoern A. Zeeb 	/* poll tx completion for interrupt disabled CE's */
3665c1def83SBjoern A. Zeeb 	ath12k_ce_poll_send_completed(ab, ep->ul_pipe_id);
3675c1def83SBjoern A. Zeeb 
3685c1def83SBjoern A. Zeeb 	/* skb is now owned by the rx completion handler */
3695c1def83SBjoern A. Zeeb 	skb = NULL;
3705c1def83SBjoern A. Zeeb out:
3715c1def83SBjoern A. Zeeb 	kfree_skb(skb);
3725c1def83SBjoern A. Zeeb }
3735c1def83SBjoern A. Zeeb 
ath12k_htc_control_rx_complete(struct ath12k_base * ab,struct sk_buff * skb)3745c1def83SBjoern A. Zeeb static void ath12k_htc_control_rx_complete(struct ath12k_base *ab,
3755c1def83SBjoern A. Zeeb 					   struct sk_buff *skb)
3765c1def83SBjoern A. Zeeb {
3775c1def83SBjoern A. Zeeb 	/* This is unexpected. FW is not supposed to send regular rx on this
3785c1def83SBjoern A. Zeeb 	 * endpoint.
3795c1def83SBjoern A. Zeeb 	 */
3805c1def83SBjoern A. Zeeb 	ath12k_warn(ab, "unexpected htc rx\n");
3815c1def83SBjoern A. Zeeb 	kfree_skb(skb);
3825c1def83SBjoern A. Zeeb }
3835c1def83SBjoern A. Zeeb 
htc_service_name(enum ath12k_htc_svc_id id)3845c1def83SBjoern A. Zeeb static const char *htc_service_name(enum ath12k_htc_svc_id id)
3855c1def83SBjoern A. Zeeb {
3865c1def83SBjoern A. Zeeb 	switch (id) {
3875c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_RESERVED:
3885c1def83SBjoern A. Zeeb 		return "Reserved";
3895c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_RSVD_CTRL:
3905c1def83SBjoern A. Zeeb 		return "Control";
3915c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_CONTROL:
3925c1def83SBjoern A. Zeeb 		return "WMI";
3935c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_DATA_BE:
3945c1def83SBjoern A. Zeeb 		return "DATA BE";
3955c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_DATA_BK:
3965c1def83SBjoern A. Zeeb 		return "DATA BK";
3975c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_DATA_VI:
3985c1def83SBjoern A. Zeeb 		return "DATA VI";
3995c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_DATA_VO:
4005c1def83SBjoern A. Zeeb 		return "DATA VO";
4015c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1:
4025c1def83SBjoern A. Zeeb 		return "WMI MAC1";
4035c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2:
4045c1def83SBjoern A. Zeeb 		return "WMI MAC2";
4055c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_NMI_CONTROL:
4065c1def83SBjoern A. Zeeb 		return "NMI Control";
4075c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_NMI_DATA:
4085c1def83SBjoern A. Zeeb 		return "NMI Data";
4095c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_HTT_DATA_MSG:
4105c1def83SBjoern A. Zeeb 		return "HTT Data";
4115c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS:
4125c1def83SBjoern A. Zeeb 		return "RAW";
4135c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_IPA_TX:
4145c1def83SBjoern A. Zeeb 		return "IPA TX";
4155c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_PKT_LOG:
4165c1def83SBjoern A. Zeeb 		return "PKT LOG";
4175c1def83SBjoern A. Zeeb 	case ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG:
4185c1def83SBjoern A. Zeeb 		return "WMI DIAG";
4195c1def83SBjoern A. Zeeb 	}
4205c1def83SBjoern A. Zeeb 
4215c1def83SBjoern A. Zeeb 	return "Unknown";
4225c1def83SBjoern A. Zeeb }
4235c1def83SBjoern A. Zeeb 
ath12k_htc_reset_endpoint_states(struct ath12k_htc * htc)4245c1def83SBjoern A. Zeeb static void ath12k_htc_reset_endpoint_states(struct ath12k_htc *htc)
4255c1def83SBjoern A. Zeeb {
4265c1def83SBjoern A. Zeeb 	struct ath12k_htc_ep *ep;
4275c1def83SBjoern A. Zeeb 	int i;
4285c1def83SBjoern A. Zeeb 
4295c1def83SBjoern A. Zeeb 	for (i = ATH12K_HTC_EP_0; i < ATH12K_HTC_EP_COUNT; i++) {
4305c1def83SBjoern A. Zeeb 		ep = &htc->endpoint[i];
4315c1def83SBjoern A. Zeeb 		ep->service_id = ATH12K_HTC_SVC_ID_UNUSED;
4325c1def83SBjoern A. Zeeb 		ep->max_ep_message_len = 0;
4335c1def83SBjoern A. Zeeb 		ep->max_tx_queue_depth = 0;
4345c1def83SBjoern A. Zeeb 		ep->eid = i;
4355c1def83SBjoern A. Zeeb 		ep->htc = htc;
4365c1def83SBjoern A. Zeeb 		ep->tx_credit_flow_enabled = true;
4375c1def83SBjoern A. Zeeb 	}
4385c1def83SBjoern A. Zeeb }
4395c1def83SBjoern A. Zeeb 
ath12k_htc_get_credit_allocation(struct ath12k_htc * htc,u16 service_id)4405c1def83SBjoern A. Zeeb static u8 ath12k_htc_get_credit_allocation(struct ath12k_htc *htc,
4415c1def83SBjoern A. Zeeb 					   u16 service_id)
4425c1def83SBjoern A. Zeeb {
4435c1def83SBjoern A. Zeeb 	struct ath12k_htc_svc_tx_credits *serv_entry;
4445c1def83SBjoern A. Zeeb 	u8 i, allocation = 0;
4455c1def83SBjoern A. Zeeb 
4465c1def83SBjoern A. Zeeb 	serv_entry = htc->service_alloc_table;
4475c1def83SBjoern A. Zeeb 
4485c1def83SBjoern A. Zeeb 	for (i = 0; i < ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
4495c1def83SBjoern A. Zeeb 		if (serv_entry[i].service_id == service_id) {
4505c1def83SBjoern A. Zeeb 			allocation = serv_entry[i].credit_allocation;
4515c1def83SBjoern A. Zeeb 			break;
4525c1def83SBjoern A. Zeeb 		}
4535c1def83SBjoern A. Zeeb 	}
4545c1def83SBjoern A. Zeeb 
4555c1def83SBjoern A. Zeeb 	return allocation;
4565c1def83SBjoern A. Zeeb }
4575c1def83SBjoern A. Zeeb 
ath12k_htc_setup_target_buffer_assignments(struct ath12k_htc * htc)4585c1def83SBjoern A. Zeeb static int ath12k_htc_setup_target_buffer_assignments(struct ath12k_htc *htc)
4595c1def83SBjoern A. Zeeb {
4605c1def83SBjoern A. Zeeb 	struct ath12k_htc_svc_tx_credits *serv_entry;
4615c1def83SBjoern A. Zeeb 	static const u32 svc_id[] = {
4625c1def83SBjoern A. Zeeb 					ATH12K_HTC_SVC_ID_WMI_CONTROL,
4635c1def83SBjoern A. Zeeb 					ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1,
4645c1def83SBjoern A. Zeeb 					ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2,
4655c1def83SBjoern A. Zeeb 				};
4665c1def83SBjoern A. Zeeb 	int i, credits;
4675c1def83SBjoern A. Zeeb 
4685c1def83SBjoern A. Zeeb 	credits = htc->total_transmit_credits;
4695c1def83SBjoern A. Zeeb 	serv_entry = htc->service_alloc_table;
4705c1def83SBjoern A. Zeeb 
4715c1def83SBjoern A. Zeeb 	if ((htc->wmi_ep_count == 0) ||
4725c1def83SBjoern A. Zeeb 	    (htc->wmi_ep_count > ARRAY_SIZE(svc_id)))
4735c1def83SBjoern A. Zeeb 		return -EINVAL;
4745c1def83SBjoern A. Zeeb 
4755c1def83SBjoern A. Zeeb 	/* Divide credits among number of endpoints for WMI */
4765c1def83SBjoern A. Zeeb 	credits = credits / htc->wmi_ep_count;
4775c1def83SBjoern A. Zeeb 	for (i = 0; i < htc->wmi_ep_count; i++) {
4785c1def83SBjoern A. Zeeb 		serv_entry[i].service_id = svc_id[i];
4795c1def83SBjoern A. Zeeb 		serv_entry[i].credit_allocation = credits;
4805c1def83SBjoern A. Zeeb 	}
4815c1def83SBjoern A. Zeeb 
4825c1def83SBjoern A. Zeeb 	return 0;
4835c1def83SBjoern A. Zeeb }
4845c1def83SBjoern A. Zeeb 
ath12k_htc_wait_target(struct ath12k_htc * htc)4855c1def83SBjoern A. Zeeb int ath12k_htc_wait_target(struct ath12k_htc *htc)
4865c1def83SBjoern A. Zeeb {
4875c1def83SBjoern A. Zeeb 	int i, status = 0;
4885c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
4895c1def83SBjoern A. Zeeb 	unsigned long time_left;
4905c1def83SBjoern A. Zeeb 	struct ath12k_htc_ready *ready;
4915c1def83SBjoern A. Zeeb 	u16 message_id;
4925c1def83SBjoern A. Zeeb 	u16 credit_count;
4935c1def83SBjoern A. Zeeb 	u16 credit_size;
4945c1def83SBjoern A. Zeeb 
4955c1def83SBjoern A. Zeeb 	time_left = wait_for_completion_timeout(&htc->ctl_resp,
4965c1def83SBjoern A. Zeeb 						ATH12K_HTC_WAIT_TIMEOUT_HZ);
4975c1def83SBjoern A. Zeeb 	if (!time_left) {
4985c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "failed to receive control response completion, polling..\n");
4995c1def83SBjoern A. Zeeb 
5005c1def83SBjoern A. Zeeb 		for (i = 0; i < ab->hw_params->ce_count; i++)
5015c1def83SBjoern A. Zeeb 			ath12k_ce_per_engine_service(htc->ab, i);
5025c1def83SBjoern A. Zeeb 
5035c1def83SBjoern A. Zeeb 		time_left =
5045c1def83SBjoern A. Zeeb 			wait_for_completion_timeout(&htc->ctl_resp,
5055c1def83SBjoern A. Zeeb 						    ATH12K_HTC_WAIT_TIMEOUT_HZ);
5065c1def83SBjoern A. Zeeb 
5075c1def83SBjoern A. Zeeb 		if (!time_left)
5085c1def83SBjoern A. Zeeb 			status = -ETIMEDOUT;
5095c1def83SBjoern A. Zeeb 	}
5105c1def83SBjoern A. Zeeb 
5115c1def83SBjoern A. Zeeb 	if (status < 0) {
5125c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "ctl_resp never came in (%d)\n", status);
5135c1def83SBjoern A. Zeeb 		return status;
5145c1def83SBjoern A. Zeeb 	}
5155c1def83SBjoern A. Zeeb 
5165c1def83SBjoern A. Zeeb 	if (htc->control_resp_len < sizeof(*ready)) {
5175c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Invalid HTC ready msg len:%d\n",
5185c1def83SBjoern A. Zeeb 			    htc->control_resp_len);
5195c1def83SBjoern A. Zeeb 		return -ECOMM;
5205c1def83SBjoern A. Zeeb 	}
5215c1def83SBjoern A. Zeeb 
5225c1def83SBjoern A. Zeeb 	ready = (struct ath12k_htc_ready *)htc->control_resp_buffer;
5235c1def83SBjoern A. Zeeb 	message_id = le32_get_bits(ready->id_credit_count, HTC_MSG_MESSAGEID);
5245c1def83SBjoern A. Zeeb 	credit_count = le32_get_bits(ready->id_credit_count,
5255c1def83SBjoern A. Zeeb 				     HTC_READY_MSG_CREDITCOUNT);
5265c1def83SBjoern A. Zeeb 	credit_size = le32_get_bits(ready->size_ep, HTC_READY_MSG_CREDITSIZE);
5275c1def83SBjoern A. Zeeb 
5285c1def83SBjoern A. Zeeb 	if (message_id != ATH12K_HTC_MSG_READY_ID) {
5295c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id);
5305c1def83SBjoern A. Zeeb 		return -ECOMM;
5315c1def83SBjoern A. Zeeb 	}
5325c1def83SBjoern A. Zeeb 
5335c1def83SBjoern A. Zeeb 	htc->total_transmit_credits = credit_count;
5345c1def83SBjoern A. Zeeb 	htc->target_credit_size = credit_size;
5355c1def83SBjoern A. Zeeb 
5365c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_HTC,
5375c1def83SBjoern A. Zeeb 		   "Target ready! transmit resources: %d size:%d\n",
5385c1def83SBjoern A. Zeeb 		   htc->total_transmit_credits, htc->target_credit_size);
5395c1def83SBjoern A. Zeeb 
5405c1def83SBjoern A. Zeeb 	if ((htc->total_transmit_credits == 0) ||
5415c1def83SBjoern A. Zeeb 	    (htc->target_credit_size == 0)) {
5425c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Invalid credit size received\n");
5435c1def83SBjoern A. Zeeb 		return -ECOMM;
5445c1def83SBjoern A. Zeeb 	}
5455c1def83SBjoern A. Zeeb 
5465c1def83SBjoern A. Zeeb 	ath12k_htc_setup_target_buffer_assignments(htc);
5475c1def83SBjoern A. Zeeb 
5485c1def83SBjoern A. Zeeb 	return 0;
5495c1def83SBjoern A. Zeeb }
5505c1def83SBjoern A. Zeeb 
ath12k_htc_connect_service(struct ath12k_htc * htc,struct ath12k_htc_svc_conn_req * conn_req,struct ath12k_htc_svc_conn_resp * conn_resp)5515c1def83SBjoern A. Zeeb int ath12k_htc_connect_service(struct ath12k_htc *htc,
5525c1def83SBjoern A. Zeeb 			       struct ath12k_htc_svc_conn_req *conn_req,
5535c1def83SBjoern A. Zeeb 			       struct ath12k_htc_svc_conn_resp *conn_resp)
5545c1def83SBjoern A. Zeeb {
5555c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
5565c1def83SBjoern A. Zeeb 	struct ath12k_htc_conn_svc *req_msg;
5575c1def83SBjoern A. Zeeb 	struct ath12k_htc_conn_svc_resp resp_msg_dummy;
5585c1def83SBjoern A. Zeeb 	struct ath12k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;
5595c1def83SBjoern A. Zeeb 	enum ath12k_htc_ep_id assigned_eid = ATH12K_HTC_EP_COUNT;
5605c1def83SBjoern A. Zeeb 	struct ath12k_htc_ep *ep;
5615c1def83SBjoern A. Zeeb 	struct sk_buff *skb;
5625c1def83SBjoern A. Zeeb 	unsigned int max_msg_size = 0;
5635c1def83SBjoern A. Zeeb 	int length, status;
5645c1def83SBjoern A. Zeeb 	unsigned long time_left;
5655c1def83SBjoern A. Zeeb 	bool disable_credit_flow_ctrl = false;
5665c1def83SBjoern A. Zeeb 	u16 message_id, service_id, flags = 0;
5675c1def83SBjoern A. Zeeb 	u8 tx_alloc = 0;
5685c1def83SBjoern A. Zeeb 
5695c1def83SBjoern A. Zeeb 	/* special case for HTC pseudo control service */
5705c1def83SBjoern A. Zeeb 	if (conn_req->service_id == ATH12K_HTC_SVC_ID_RSVD_CTRL) {
5715c1def83SBjoern A. Zeeb 		disable_credit_flow_ctrl = true;
5725c1def83SBjoern A. Zeeb 		assigned_eid = ATH12K_HTC_EP_0;
5735c1def83SBjoern A. Zeeb 		max_msg_size = ATH12K_HTC_MAX_CTRL_MSG_LEN;
5745c1def83SBjoern A. Zeeb 		memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
5755c1def83SBjoern A. Zeeb 		goto setup;
5765c1def83SBjoern A. Zeeb 	}
5775c1def83SBjoern A. Zeeb 
5785c1def83SBjoern A. Zeeb 	tx_alloc = ath12k_htc_get_credit_allocation(htc,
5795c1def83SBjoern A. Zeeb 						    conn_req->service_id);
5805c1def83SBjoern A. Zeeb 	if (!tx_alloc)
5815c1def83SBjoern A. Zeeb 		ath12k_dbg(ab, ATH12K_DBG_BOOT,
5825c1def83SBjoern A. Zeeb 			   "boot htc service %s does not allocate target credits\n",
5835c1def83SBjoern A. Zeeb 			   htc_service_name(conn_req->service_id));
5845c1def83SBjoern A. Zeeb 
5855c1def83SBjoern A. Zeeb 	skb = ath12k_htc_build_tx_ctrl_skb();
5865c1def83SBjoern A. Zeeb 	if (!skb) {
5875c1def83SBjoern A. Zeeb 		ath12k_warn(ab, "Failed to allocate HTC packet\n");
5885c1def83SBjoern A. Zeeb 		return -ENOMEM;
5895c1def83SBjoern A. Zeeb 	}
5905c1def83SBjoern A. Zeeb 
5915c1def83SBjoern A. Zeeb 	length = sizeof(*req_msg);
5925c1def83SBjoern A. Zeeb 	skb_put(skb, length);
5935c1def83SBjoern A. Zeeb 	memset(skb->data, 0, length);
5945c1def83SBjoern A. Zeeb 
5955c1def83SBjoern A. Zeeb 	req_msg = (struct ath12k_htc_conn_svc *)skb->data;
5965c1def83SBjoern A. Zeeb 	req_msg->msg_svc_id = le32_encode_bits(ATH12K_HTC_MSG_CONNECT_SERVICE_ID,
5975c1def83SBjoern A. Zeeb 					       HTC_MSG_MESSAGEID);
5985c1def83SBjoern A. Zeeb 
5995c1def83SBjoern A. Zeeb 	flags |= u32_encode_bits(tx_alloc, ATH12K_HTC_CONN_FLAGS_RECV_ALLOC);
6005c1def83SBjoern A. Zeeb 
6015c1def83SBjoern A. Zeeb 	/* Only enable credit flow control for WMI ctrl service */
6025c1def83SBjoern A. Zeeb 	if (!(conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL ||
6035c1def83SBjoern A. Zeeb 	      conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||
6045c1def83SBjoern A. Zeeb 	      conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {
6055c1def83SBjoern A. Zeeb 		flags |= ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
6065c1def83SBjoern A. Zeeb 		disable_credit_flow_ctrl = true;
6075c1def83SBjoern A. Zeeb 	}
6085c1def83SBjoern A. Zeeb 
6095c1def83SBjoern A. Zeeb 	req_msg->flags_len = le32_encode_bits(flags, HTC_SVC_MSG_CONNECTIONFLAGS);
6105c1def83SBjoern A. Zeeb 	req_msg->msg_svc_id |= le32_encode_bits(conn_req->service_id,
6115c1def83SBjoern A. Zeeb 						HTC_SVC_MSG_SERVICE_ID);
6125c1def83SBjoern A. Zeeb 
6135c1def83SBjoern A. Zeeb 	reinit_completion(&htc->ctl_resp);
6145c1def83SBjoern A. Zeeb 
6155c1def83SBjoern A. Zeeb 	status = ath12k_htc_send(htc, ATH12K_HTC_EP_0, skb);
6165c1def83SBjoern A. Zeeb 	if (status) {
6175c1def83SBjoern A. Zeeb 		kfree_skb(skb);
6185c1def83SBjoern A. Zeeb 		return status;
6195c1def83SBjoern A. Zeeb 	}
6205c1def83SBjoern A. Zeeb 
6215c1def83SBjoern A. Zeeb 	/* wait for response */
6225c1def83SBjoern A. Zeeb 	time_left = wait_for_completion_timeout(&htc->ctl_resp,
6235c1def83SBjoern A. Zeeb 						ATH12K_HTC_CONN_SVC_TIMEOUT_HZ);
6245c1def83SBjoern A. Zeeb 	if (!time_left) {
6255c1def83SBjoern A. Zeeb 		ath12k_err(ab, "Service connect timeout\n");
6265c1def83SBjoern A. Zeeb 		return -ETIMEDOUT;
6275c1def83SBjoern A. Zeeb 	}
6285c1def83SBjoern A. Zeeb 
6295c1def83SBjoern A. Zeeb 	/* we controlled the buffer creation, it's aligned */
6305c1def83SBjoern A. Zeeb 	resp_msg = (struct ath12k_htc_conn_svc_resp *)htc->control_resp_buffer;
6315c1def83SBjoern A. Zeeb 	message_id = le32_get_bits(resp_msg->msg_svc_id, HTC_MSG_MESSAGEID);
6325c1def83SBjoern A. Zeeb 	service_id = le32_get_bits(resp_msg->msg_svc_id,
6335c1def83SBjoern A. Zeeb 				   HTC_SVC_RESP_MSG_SERVICEID);
6345c1def83SBjoern A. Zeeb 
6355c1def83SBjoern A. Zeeb 	if ((message_id != ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
6365c1def83SBjoern A. Zeeb 	    (htc->control_resp_len < sizeof(*resp_msg))) {
6375c1def83SBjoern A. Zeeb 		ath12k_err(ab, "Invalid resp message ID 0x%x", message_id);
6385c1def83SBjoern A. Zeeb 		return -EPROTO;
6395c1def83SBjoern A. Zeeb 	}
6405c1def83SBjoern A. Zeeb 
6415c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_HTC,
6425c1def83SBjoern A. Zeeb 		   "HTC Service %s connect response: status: %u, assigned ep: %u\n",
6435c1def83SBjoern A. Zeeb 		   htc_service_name(service_id),
6445c1def83SBjoern A. Zeeb 		   le32_get_bits(resp_msg->flags_len, HTC_SVC_RESP_MSG_STATUS),
6455c1def83SBjoern A. Zeeb 		   le32_get_bits(resp_msg->flags_len, HTC_SVC_RESP_MSG_ENDPOINTID));
6465c1def83SBjoern A. Zeeb 
6475c1def83SBjoern A. Zeeb 	conn_resp->connect_resp_code = le32_get_bits(resp_msg->flags_len,
6485c1def83SBjoern A. Zeeb 						     HTC_SVC_RESP_MSG_STATUS);
6495c1def83SBjoern A. Zeeb 
6505c1def83SBjoern A. Zeeb 	/* check response status */
6515c1def83SBjoern A. Zeeb 	if (conn_resp->connect_resp_code != ATH12K_HTC_CONN_SVC_STATUS_SUCCESS) {
6525c1def83SBjoern A. Zeeb 		ath12k_err(ab, "HTC Service %s connect request failed: 0x%x)\n",
6535c1def83SBjoern A. Zeeb 			   htc_service_name(service_id),
6545c1def83SBjoern A. Zeeb 		       conn_resp->connect_resp_code);
6555c1def83SBjoern A. Zeeb 		return -EPROTO;
6565c1def83SBjoern A. Zeeb 	}
6575c1def83SBjoern A. Zeeb 
6585c1def83SBjoern A. Zeeb 	assigned_eid = le32_get_bits(resp_msg->flags_len,
6595c1def83SBjoern A. Zeeb 				     HTC_SVC_RESP_MSG_ENDPOINTID);
6605c1def83SBjoern A. Zeeb 
6615c1def83SBjoern A. Zeeb 	max_msg_size = le32_get_bits(resp_msg->flags_len,
6625c1def83SBjoern A. Zeeb 				     HTC_SVC_RESP_MSG_MAXMSGSIZE);
6635c1def83SBjoern A. Zeeb 
6645c1def83SBjoern A. Zeeb setup:
6655c1def83SBjoern A. Zeeb 
6665c1def83SBjoern A. Zeeb 	if (assigned_eid >= ATH12K_HTC_EP_COUNT)
6675c1def83SBjoern A. Zeeb 		return -EPROTO;
6685c1def83SBjoern A. Zeeb 
6695c1def83SBjoern A. Zeeb 	if (max_msg_size == 0)
6705c1def83SBjoern A. Zeeb 		return -EPROTO;
6715c1def83SBjoern A. Zeeb 
6725c1def83SBjoern A. Zeeb 	ep = &htc->endpoint[assigned_eid];
6735c1def83SBjoern A. Zeeb 	ep->eid = assigned_eid;
6745c1def83SBjoern A. Zeeb 
6755c1def83SBjoern A. Zeeb 	if (ep->service_id != ATH12K_HTC_SVC_ID_UNUSED)
6765c1def83SBjoern A. Zeeb 		return -EPROTO;
6775c1def83SBjoern A. Zeeb 
6785c1def83SBjoern A. Zeeb 	/* return assigned endpoint to caller */
6795c1def83SBjoern A. Zeeb 	conn_resp->eid = assigned_eid;
6805c1def83SBjoern A. Zeeb 	conn_resp->max_msg_len = le32_get_bits(resp_msg->flags_len,
6815c1def83SBjoern A. Zeeb 					       HTC_SVC_RESP_MSG_MAXMSGSIZE);
6825c1def83SBjoern A. Zeeb 
6835c1def83SBjoern A. Zeeb 	/* setup the endpoint */
6845c1def83SBjoern A. Zeeb 	ep->service_id = conn_req->service_id;
6855c1def83SBjoern A. Zeeb 	ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
6865c1def83SBjoern A. Zeeb 	ep->max_ep_message_len = le32_get_bits(resp_msg->flags_len,
6875c1def83SBjoern A. Zeeb 					       HTC_SVC_RESP_MSG_MAXMSGSIZE);
6885c1def83SBjoern A. Zeeb 	ep->tx_credits = tx_alloc;
6895c1def83SBjoern A. Zeeb 
6905c1def83SBjoern A. Zeeb 	/* copy all the callbacks */
6915c1def83SBjoern A. Zeeb 	ep->ep_ops = conn_req->ep_ops;
6925c1def83SBjoern A. Zeeb 
6935c1def83SBjoern A. Zeeb 	status = ath12k_hif_map_service_to_pipe(htc->ab,
6945c1def83SBjoern A. Zeeb 						ep->service_id,
6955c1def83SBjoern A. Zeeb 						&ep->ul_pipe_id,
6965c1def83SBjoern A. Zeeb 						&ep->dl_pipe_id);
6975c1def83SBjoern A. Zeeb 	if (status)
6985c1def83SBjoern A. Zeeb 		return status;
6995c1def83SBjoern A. Zeeb 
7005c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_BOOT,
7015c1def83SBjoern A. Zeeb 		   "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
7025c1def83SBjoern A. Zeeb 		   htc_service_name(ep->service_id), ep->ul_pipe_id,
7035c1def83SBjoern A. Zeeb 		   ep->dl_pipe_id, ep->eid);
7045c1def83SBjoern A. Zeeb 
7055c1def83SBjoern A. Zeeb 	if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
7065c1def83SBjoern A. Zeeb 		ep->tx_credit_flow_enabled = false;
7075c1def83SBjoern A. Zeeb 		ath12k_dbg(ab, ATH12K_DBG_BOOT,
7085c1def83SBjoern A. Zeeb 			   "boot htc service '%s' eid %d TX flow control disabled\n",
7095c1def83SBjoern A. Zeeb 			   htc_service_name(ep->service_id), assigned_eid);
7105c1def83SBjoern A. Zeeb 	}
7115c1def83SBjoern A. Zeeb 
7125c1def83SBjoern A. Zeeb 	return status;
7135c1def83SBjoern A. Zeeb }
7145c1def83SBjoern A. Zeeb 
ath12k_htc_start(struct ath12k_htc * htc)7155c1def83SBjoern A. Zeeb int ath12k_htc_start(struct ath12k_htc *htc)
7165c1def83SBjoern A. Zeeb {
7175c1def83SBjoern A. Zeeb 	struct sk_buff *skb;
7185c1def83SBjoern A. Zeeb 	int status;
7195c1def83SBjoern A. Zeeb 	struct ath12k_base *ab = htc->ab;
7205c1def83SBjoern A. Zeeb 	struct ath12k_htc_setup_complete_extended *msg;
7215c1def83SBjoern A. Zeeb 
7225c1def83SBjoern A. Zeeb 	skb = ath12k_htc_build_tx_ctrl_skb();
7235c1def83SBjoern A. Zeeb 	if (!skb)
7245c1def83SBjoern A. Zeeb 		return -ENOMEM;
7255c1def83SBjoern A. Zeeb 
7265c1def83SBjoern A. Zeeb 	skb_put(skb, sizeof(*msg));
7275c1def83SBjoern A. Zeeb 	memset(skb->data, 0, skb->len);
7285c1def83SBjoern A. Zeeb 
7295c1def83SBjoern A. Zeeb 	msg = (struct ath12k_htc_setup_complete_extended *)skb->data;
7305c1def83SBjoern A. Zeeb 	msg->msg_id = le32_encode_bits(ATH12K_HTC_MSG_SETUP_COMPLETE_EX_ID,
7315c1def83SBjoern A. Zeeb 				       HTC_MSG_MESSAGEID);
7325c1def83SBjoern A. Zeeb 
7335c1def83SBjoern A. Zeeb 	ath12k_dbg(ab, ATH12K_DBG_HTC, "HTC is using TX credit flow control\n");
7345c1def83SBjoern A. Zeeb 
7355c1def83SBjoern A. Zeeb 	status = ath12k_htc_send(htc, ATH12K_HTC_EP_0, skb);
7365c1def83SBjoern A. Zeeb 	if (status) {
7375c1def83SBjoern A. Zeeb 		kfree_skb(skb);
7385c1def83SBjoern A. Zeeb 		return status;
7395c1def83SBjoern A. Zeeb 	}
7405c1def83SBjoern A. Zeeb 
7415c1def83SBjoern A. Zeeb 	return 0;
7425c1def83SBjoern A. Zeeb }
7435c1def83SBjoern A. Zeeb 
ath12k_htc_init(struct ath12k_base * ab)7445c1def83SBjoern A. Zeeb int ath12k_htc_init(struct ath12k_base *ab)
7455c1def83SBjoern A. Zeeb {
7465c1def83SBjoern A. Zeeb 	struct ath12k_htc *htc = &ab->htc;
7475c1def83SBjoern A. Zeeb 	struct ath12k_htc_svc_conn_req conn_req = { };
7485c1def83SBjoern A. Zeeb 	struct ath12k_htc_svc_conn_resp conn_resp = { };
7495c1def83SBjoern A. Zeeb 	int ret;
7505c1def83SBjoern A. Zeeb 
7515c1def83SBjoern A. Zeeb 	spin_lock_init(&htc->tx_lock);
7525c1def83SBjoern A. Zeeb 
7535c1def83SBjoern A. Zeeb 	ath12k_htc_reset_endpoint_states(htc);
7545c1def83SBjoern A. Zeeb 
7555c1def83SBjoern A. Zeeb 	htc->ab = ab;
7565c1def83SBjoern A. Zeeb 
7575c1def83SBjoern A. Zeeb 	switch (ab->wmi_ab.preferred_hw_mode) {
7585c1def83SBjoern A. Zeeb 	case WMI_HOST_HW_MODE_SINGLE:
7595c1def83SBjoern A. Zeeb 		htc->wmi_ep_count = 1;
7605c1def83SBjoern A. Zeeb 		break;
7615c1def83SBjoern A. Zeeb 	case WMI_HOST_HW_MODE_DBS:
7625c1def83SBjoern A. Zeeb 	case WMI_HOST_HW_MODE_DBS_OR_SBS:
7635c1def83SBjoern A. Zeeb 		htc->wmi_ep_count = 2;
7645c1def83SBjoern A. Zeeb 		break;
7655c1def83SBjoern A. Zeeb 	case WMI_HOST_HW_MODE_DBS_SBS:
7665c1def83SBjoern A. Zeeb 		htc->wmi_ep_count = 3;
7675c1def83SBjoern A. Zeeb 		break;
7685c1def83SBjoern A. Zeeb 	default:
7695c1def83SBjoern A. Zeeb 		htc->wmi_ep_count = ab->hw_params->max_radios;
7705c1def83SBjoern A. Zeeb 		break;
7715c1def83SBjoern A. Zeeb 	}
7725c1def83SBjoern A. Zeeb 
7735c1def83SBjoern A. Zeeb 	/* setup our pseudo HTC control endpoint connection */
7745c1def83SBjoern A. Zeeb 	conn_req.ep_ops.ep_tx_complete = ath12k_htc_control_tx_complete;
7755c1def83SBjoern A. Zeeb 	conn_req.ep_ops.ep_rx_complete = ath12k_htc_control_rx_complete;
7765c1def83SBjoern A. Zeeb 	conn_req.max_send_queue_depth = ATH12K_NUM_CONTROL_TX_BUFFERS;
7775c1def83SBjoern A. Zeeb 	conn_req.service_id = ATH12K_HTC_SVC_ID_RSVD_CTRL;
7785c1def83SBjoern A. Zeeb 
7795c1def83SBjoern A. Zeeb 	/* connect fake service */
7805c1def83SBjoern A. Zeeb 	ret = ath12k_htc_connect_service(htc, &conn_req, &conn_resp);
7815c1def83SBjoern A. Zeeb 	if (ret) {
7825c1def83SBjoern A. Zeeb 		ath12k_err(ab, "could not connect to htc service (%d)\n", ret);
7835c1def83SBjoern A. Zeeb 		return ret;
7845c1def83SBjoern A. Zeeb 	}
7855c1def83SBjoern A. Zeeb 
7865c1def83SBjoern A. Zeeb 	init_completion(&htc->ctl_resp);
7875c1def83SBjoern A. Zeeb 
7885c1def83SBjoern A. Zeeb 	return 0;
7895c1def83SBjoern A. Zeeb }
790