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