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