Path: blob/main/sys/contrib/dev/athk/ath11k/htc.c
105767 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.3* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.4*/5#include <linux/skbuff.h>6#include <linux/ctype.h>78#include "debug.h"9#include "hif.h"1011struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size)12{13struct sk_buff *skb;1415skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr));16if (!skb)17return NULL;1819skb_reserve(skb, sizeof(struct ath11k_htc_hdr));2021/* FW/HTC requires 4-byte aligned streams */22if (!IS_ALIGNED((unsigned long)skb->data, 4))23ath11k_warn(ab, "Unaligned HTC tx skb\n");2425return skb;26}2728static void ath11k_htc_control_tx_complete(struct ath11k_base *ab,29struct sk_buff *skb)30{31kfree_skb(skb);32}3334static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab)35{36struct sk_buff *skb;37struct ath11k_skb_cb *skb_cb;3839skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE);40if (!skb)41return NULL;4243skb_reserve(skb, sizeof(struct ath11k_htc_hdr));44WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));4546skb_cb = ATH11K_SKB_CB(skb);47memset(skb_cb, 0, sizeof(*skb_cb));4849return skb;50}5152static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep,53struct sk_buff *skb)54{55struct ath11k_htc_hdr *hdr;5657hdr = (struct ath11k_htc_hdr *)skb->data;5859memset(hdr, 0, sizeof(*hdr));60hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) |61FIELD_PREP(HTC_HDR_PAYLOADLEN,62(skb->len - sizeof(*hdr)));6364if (ep->tx_credit_flow_enabled)65hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS,66ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE);6768spin_lock_bh(&ep->htc->tx_lock);69hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++);70spin_unlock_bh(&ep->htc->tx_lock);71}7273int ath11k_htc_send(struct ath11k_htc *htc,74enum ath11k_htc_ep_id eid,75struct sk_buff *skb)76{77struct ath11k_htc_ep *ep = &htc->endpoint[eid];78struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb);79struct device *dev = htc->ab->dev;80struct ath11k_base *ab = htc->ab;81int credits = 0;82int ret;83bool credit_flow_enabled = (ab->hw_params.credit_flow &&84ep->tx_credit_flow_enabled);8586if (eid >= ATH11K_HTC_EP_COUNT) {87ath11k_warn(ab, "Invalid endpoint id: %d\n", eid);88return -ENOENT;89}9091skb_push(skb, sizeof(struct ath11k_htc_hdr));9293if (credit_flow_enabled) {94credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);95spin_lock_bh(&htc->tx_lock);96if (ep->tx_credits < credits) {97ath11k_dbg(ab, ATH11K_DBG_HTC,98"ep %d insufficient credits required %d total %d\n",99eid, credits, ep->tx_credits);100spin_unlock_bh(&htc->tx_lock);101ret = -EAGAIN;102goto err_pull;103}104ep->tx_credits -= credits;105ath11k_dbg(ab, ATH11K_DBG_HTC,106"ep %d credits consumed %d total %d\n",107eid, credits, ep->tx_credits);108spin_unlock_bh(&htc->tx_lock);109}110111ath11k_htc_prepare_tx_skb(ep, skb);112113skb_cb->eid = eid;114skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);115ret = dma_mapping_error(dev, skb_cb->paddr);116if (ret) {117ret = -EIO;118goto err_credits;119}120121ath11k_dbg(ab, ATH11K_DBG_HTC, "tx skb %p eid %d paddr %pad\n",122skb, skb_cb->eid, &skb_cb->paddr);123124ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid);125if (ret)126goto err_unmap;127128return 0;129130err_unmap:131dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);132err_credits:133if (credit_flow_enabled) {134spin_lock_bh(&htc->tx_lock);135ep->tx_credits += credits;136ath11k_dbg(ab, ATH11K_DBG_HTC,137"ep %d credits reverted %d total %d\n",138eid, credits, ep->tx_credits);139spin_unlock_bh(&htc->tx_lock);140141if (ep->ep_ops.ep_tx_credits)142ep->ep_ops.ep_tx_credits(htc->ab);143}144err_pull:145skb_pull(skb, sizeof(struct ath11k_htc_hdr));146return ret;147}148149static void150ath11k_htc_process_credit_report(struct ath11k_htc *htc,151const struct ath11k_htc_credit_report *report,152int len,153enum ath11k_htc_ep_id eid)154{155struct ath11k_base *ab = htc->ab;156struct ath11k_htc_ep *ep;157int i, n_reports;158159if (len % sizeof(*report))160ath11k_warn(ab, "Uneven credit report len %d", len);161162n_reports = len / sizeof(*report);163164spin_lock_bh(&htc->tx_lock);165for (i = 0; i < n_reports; i++, report++) {166if (report->eid >= ATH11K_HTC_EP_COUNT)167break;168169ep = &htc->endpoint[report->eid];170ep->tx_credits += report->credits;171172ath11k_dbg(ab, ATH11K_DBG_HTC, "ep %d credits got %d total %d\n",173report->eid, report->credits, ep->tx_credits);174175if (ep->ep_ops.ep_tx_credits) {176spin_unlock_bh(&htc->tx_lock);177ep->ep_ops.ep_tx_credits(htc->ab);178spin_lock_bh(&htc->tx_lock);179}180}181spin_unlock_bh(&htc->tx_lock);182}183184static int ath11k_htc_process_trailer(struct ath11k_htc *htc,185u8 *buffer,186int length,187enum ath11k_htc_ep_id src_eid)188{189struct ath11k_base *ab = htc->ab;190int status = 0;191struct ath11k_htc_record *record;192size_t len;193194while (length > 0) {195record = (struct ath11k_htc_record *)buffer;196197if (length < sizeof(record->hdr)) {198status = -EINVAL;199break;200}201202if (record->hdr.len > length) {203/* no room left in buffer for record */204ath11k_warn(ab, "Invalid record length: %d\n",205record->hdr.len);206status = -EINVAL;207break;208}209210if (ab->hw_params.credit_flow) {211switch (record->hdr.id) {212case ATH11K_HTC_RECORD_CREDITS:213len = sizeof(struct ath11k_htc_credit_report);214if (record->hdr.len < len) {215ath11k_warn(ab, "Credit report too long\n");216status = -EINVAL;217break;218}219ath11k_htc_process_credit_report(htc,220record->credit_report,221record->hdr.len,222src_eid);223break;224default:225ath11k_warn(ab, "Unhandled record: id:%d length:%d\n",226record->hdr.id, record->hdr.len);227break;228}229}230231if (status)232break;233234/* multiple records may be present in a trailer */235buffer += sizeof(record->hdr) + record->hdr.len;236length -= sizeof(record->hdr) + record->hdr.len;237}238239return status;240}241242static void ath11k_htc_suspend_complete(struct ath11k_base *ab, bool ack)243{244ath11k_dbg(ab, ATH11K_DBG_BOOT, "suspend complete %d\n", ack);245246if (ack)247set_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);248else249clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);250251complete(&ab->htc_suspend);252}253254void ath11k_htc_tx_completion_handler(struct ath11k_base *ab,255struct sk_buff *skb)256{257struct ath11k_htc *htc = &ab->htc;258struct ath11k_htc_ep *ep;259void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *);260u8 eid;261262eid = ATH11K_SKB_CB(skb)->eid;263if (eid >= ATH11K_HTC_EP_COUNT) {264dev_kfree_skb_any(skb);265return;266}267268ep = &htc->endpoint[eid];269spin_lock_bh(&htc->tx_lock);270ep_tx_complete = ep->ep_ops.ep_tx_complete;271spin_unlock_bh(&htc->tx_lock);272if (!ep_tx_complete) {273dev_kfree_skb_any(skb);274return;275}276ep_tx_complete(htc->ab, skb);277}278279static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab)280{281ath11k_dbg(ab, ATH11K_DBG_BOOT, "wakeup from suspend is received\n");282}283284void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,285struct sk_buff *skb)286{287int status = 0;288struct ath11k_htc *htc = &ab->htc;289struct ath11k_htc_hdr *hdr;290struct ath11k_htc_ep *ep;291u16 payload_len;292u32 message_id, trailer_len = 0;293size_t min_len;294u8 eid;295bool trailer_present;296297hdr = (struct ath11k_htc_hdr *)skb->data;298skb_pull(skb, sizeof(*hdr));299300eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info);301302if (eid >= ATH11K_HTC_EP_COUNT) {303ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid);304goto out;305}306307ep = &htc->endpoint[eid];308309payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info);310311if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) {312ath11k_warn(ab, "HTC rx frame too long, len: %zu\n",313payload_len + sizeof(*hdr));314goto out;315}316317if (skb->len < payload_len) {318ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n",319skb->len, payload_len);320goto out;321}322323/* get flags to check for trailer */324trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) &325ATH11K_HTC_FLAG_TRAILER_PRESENT;326327ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p trailer_present %d\n",328eid, skb, trailer_present);329330if (trailer_present) {331u8 *trailer;332333trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info);334min_len = sizeof(struct ath11k_htc_record_hdr);335336if ((trailer_len < min_len) ||337(trailer_len > payload_len)) {338ath11k_warn(ab, "Invalid trailer length: %d\n",339trailer_len);340goto out;341}342343trailer = (u8 *)hdr;344trailer += sizeof(*hdr);345trailer += payload_len;346trailer -= trailer_len;347status = ath11k_htc_process_trailer(htc, trailer,348trailer_len, eid);349if (status)350goto out;351352skb_trim(skb, skb->len - trailer_len);353}354355if (trailer_len >= payload_len)356/* zero length packet with trailer data, just drop these */357goto out;358359if (eid == ATH11K_HTC_EP_0) {360struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data;361362message_id = FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id);363364ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p message_id %d\n",365eid, skb, message_id);366367switch (message_id) {368case ATH11K_HTC_MSG_READY_ID:369case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID:370/* handle HTC control message */371if (completion_done(&htc->ctl_resp)) {372/* this is a fatal error, target should not be373* sending unsolicited messages on the ep 0374*/375ath11k_warn(ab, "HTC rx ctrl still processing\n");376complete(&htc->ctl_resp);377goto out;378}379380htc->control_resp_len =381min_t(int, skb->len,382ATH11K_HTC_MAX_CTRL_MSG_LEN);383384memcpy(htc->control_resp_buffer, skb->data,385htc->control_resp_len);386387complete(&htc->ctl_resp);388break;389case ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE:390ath11k_htc_suspend_complete(ab, true);391break;392case ATH11K_HTC_MSG_NACK_SUSPEND:393ath11k_htc_suspend_complete(ab, false);394break;395case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:396ath11k_htc_wakeup_from_suspend(ab);397break;398default:399ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n",400FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id));401break;402}403goto out;404}405406ep->ep_ops.ep_rx_complete(ab, skb);407408/* poll tx completion for interrupt disabled CE's */409ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id);410411/* skb is now owned by the rx completion handler */412skb = NULL;413out:414kfree_skb(skb);415}416417static void ath11k_htc_control_rx_complete(struct ath11k_base *ab,418struct sk_buff *skb)419{420/* This is unexpected. FW is not supposed to send regular rx on this421* endpoint.422*/423ath11k_warn(ab, "unexpected htc rx\n");424kfree_skb(skb);425}426427static const char *htc_service_name(enum ath11k_htc_svc_id id)428{429switch (id) {430case ATH11K_HTC_SVC_ID_RESERVED:431return "Reserved";432case ATH11K_HTC_SVC_ID_RSVD_CTRL:433return "Control";434case ATH11K_HTC_SVC_ID_WMI_CONTROL:435return "WMI";436case ATH11K_HTC_SVC_ID_WMI_DATA_BE:437return "DATA BE";438case ATH11K_HTC_SVC_ID_WMI_DATA_BK:439return "DATA BK";440case ATH11K_HTC_SVC_ID_WMI_DATA_VI:441return "DATA VI";442case ATH11K_HTC_SVC_ID_WMI_DATA_VO:443return "DATA VO";444case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1:445return "WMI MAC1";446case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2:447return "WMI MAC2";448case ATH11K_HTC_SVC_ID_NMI_CONTROL:449return "NMI Control";450case ATH11K_HTC_SVC_ID_NMI_DATA:451return "NMI Data";452case ATH11K_HTC_SVC_ID_HTT_DATA_MSG:453return "HTT Data";454case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS:455return "RAW";456case ATH11K_HTC_SVC_ID_IPA_TX:457return "IPA TX";458case ATH11K_HTC_SVC_ID_PKT_LOG:459return "PKT LOG";460}461462return "Unknown";463}464465static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc)466{467struct ath11k_htc_ep *ep;468int i;469470for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) {471ep = &htc->endpoint[i];472ep->service_id = ATH11K_HTC_SVC_ID_UNUSED;473ep->max_ep_message_len = 0;474ep->max_tx_queue_depth = 0;475ep->eid = i;476ep->htc = htc;477ep->tx_credit_flow_enabled = true;478}479}480481static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc,482u16 service_id)483{484u8 i, allocation = 0;485486for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {487if (htc->service_alloc_table[i].service_id == service_id) {488allocation =489htc->service_alloc_table[i].credit_allocation;490}491}492493return allocation;494}495496static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc)497{498struct ath11k_htc_svc_tx_credits *serv_entry;499static const u32 svc_id[] = {500ATH11K_HTC_SVC_ID_WMI_CONTROL,501ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1,502ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2,503};504int i, credits;505506credits = htc->total_transmit_credits;507serv_entry = htc->service_alloc_table;508509if ((htc->wmi_ep_count == 0) ||510(htc->wmi_ep_count > ARRAY_SIZE(svc_id)))511return -EINVAL;512513/* Divide credits among number of endpoints for WMI */514credits = credits / htc->wmi_ep_count;515for (i = 0; i < htc->wmi_ep_count; i++) {516serv_entry[i].service_id = svc_id[i];517serv_entry[i].credit_allocation = credits;518}519520return 0;521}522523int ath11k_htc_wait_target(struct ath11k_htc *htc)524{525int i, status = 0;526struct ath11k_base *ab = htc->ab;527unsigned long time_left;528struct ath11k_htc_ready *ready;529u16 message_id;530u16 credit_count;531u16 credit_size;532533time_left = wait_for_completion_timeout(&htc->ctl_resp,534ATH11K_HTC_WAIT_TIMEOUT_HZ);535if (!time_left) {536ath11k_warn(ab, "failed to receive control response completion, polling..\n");537538for (i = 0; i < ab->hw_params.ce_count; i++)539ath11k_ce_per_engine_service(htc->ab, i);540541time_left =542wait_for_completion_timeout(&htc->ctl_resp,543ATH11K_HTC_WAIT_TIMEOUT_HZ);544545if (!time_left)546status = -ETIMEDOUT;547}548549if (status < 0) {550ath11k_warn(ab, "ctl_resp never came in (%d)\n", status);551return status;552}553554if (htc->control_resp_len < sizeof(*ready)) {555ath11k_warn(ab, "Invalid HTC ready msg len:%d\n",556htc->control_resp_len);557return -ECOMM;558}559560ready = (struct ath11k_htc_ready *)htc->control_resp_buffer;561message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count);562credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT,563ready->id_credit_count);564credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep);565566if (message_id != ATH11K_HTC_MSG_READY_ID) {567ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id);568return -ECOMM;569}570571htc->total_transmit_credits = credit_count;572htc->target_credit_size = credit_size;573574ath11k_dbg(ab, ATH11K_DBG_HTC,575"target ready total_transmit_credits %d target_credit_size %d\n",576htc->total_transmit_credits, htc->target_credit_size);577578if ((htc->total_transmit_credits == 0) ||579(htc->target_credit_size == 0)) {580ath11k_warn(ab, "Invalid credit size received\n");581return -ECOMM;582}583584/* For QCA6390, wmi endpoint uses 1 credit to avoid585* back-to-back write.586*/587if (ab->hw_params.supports_shadow_regs)588htc->total_transmit_credits = 1;589590ath11k_htc_setup_target_buffer_assignments(htc);591592return 0;593}594595int ath11k_htc_connect_service(struct ath11k_htc *htc,596struct ath11k_htc_svc_conn_req *conn_req,597struct ath11k_htc_svc_conn_resp *conn_resp)598{599struct ath11k_base *ab = htc->ab;600struct ath11k_htc_conn_svc *req_msg;601struct ath11k_htc_conn_svc_resp resp_msg_dummy;602struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;603enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT;604struct ath11k_htc_ep *ep;605struct sk_buff *skb;606unsigned int max_msg_size = 0;607int length, status;608unsigned long time_left;609bool disable_credit_flow_ctrl = false;610u16 message_id, service_id, flags = 0;611u8 tx_alloc = 0;612613/* special case for HTC pseudo control service */614if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) {615disable_credit_flow_ctrl = true;616assigned_eid = ATH11K_HTC_EP_0;617max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN;618memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));619goto setup;620}621622tx_alloc = ath11k_htc_get_credit_allocation(htc,623conn_req->service_id);624if (!tx_alloc)625ath11k_dbg(ab, ATH11K_DBG_BOOT,626"htc service %s does not allocate target credits\n",627htc_service_name(conn_req->service_id));628629skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);630if (!skb) {631ath11k_warn(ab, "Failed to allocate HTC packet\n");632return -ENOMEM;633}634635length = sizeof(*req_msg);636skb_put(skb, length);637memset(skb->data, 0, length);638639req_msg = (struct ath11k_htc_conn_svc *)skb->data;640req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID,641ATH11K_HTC_MSG_CONNECT_SERVICE_ID);642643flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc);644645/* Only enable credit flow control for WMI ctrl service */646if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL ||647conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||648conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {649flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;650disable_credit_flow_ctrl = true;651}652653if (!ab->hw_params.credit_flow) {654flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;655disable_credit_flow_ctrl = true;656}657658req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags);659req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID,660conn_req->service_id);661662reinit_completion(&htc->ctl_resp);663664status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);665if (status) {666kfree_skb(skb);667return status;668}669670/* wait for response */671time_left = wait_for_completion_timeout(&htc->ctl_resp,672ATH11K_HTC_CONN_SVC_TIMEOUT_HZ);673if (!time_left) {674ath11k_err(ab, "Service connect timeout\n");675return -ETIMEDOUT;676}677678/* we controlled the buffer creation, it's aligned */679resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer;680message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id);681service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID,682resp_msg->msg_svc_id);683684if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||685(htc->control_resp_len < sizeof(*resp_msg))) {686ath11k_err(ab, "Invalid resp message ID 0x%x", message_id);687return -EPROTO;688}689690ath11k_dbg(ab, ATH11K_DBG_HTC,691"service %s connect response status 0x%lx assigned ep 0x%lx\n",692htc_service_name(service_id),693FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len),694FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len));695696conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS,697resp_msg->flags_len);698699/* check response status */700if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) {701ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n",702htc_service_name(service_id),703conn_resp->connect_resp_code);704return -EPROTO;705}706707assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET(708HTC_SVC_RESP_MSG_ENDPOINTID,709resp_msg->flags_len);710711max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,712resp_msg->flags_len);713714setup:715716if (assigned_eid >= ATH11K_HTC_EP_COUNT)717return -EPROTO;718719if (max_msg_size == 0)720return -EPROTO;721722ep = &htc->endpoint[assigned_eid];723ep->eid = assigned_eid;724725if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED)726return -EPROTO;727728/* return assigned endpoint to caller */729conn_resp->eid = assigned_eid;730conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,731resp_msg->flags_len);732733/* setup the endpoint */734ep->service_id = conn_req->service_id;735ep->max_tx_queue_depth = conn_req->max_send_queue_depth;736ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,737resp_msg->flags_len);738ep->tx_credits = tx_alloc;739740/* copy all the callbacks */741ep->ep_ops = conn_req->ep_ops;742743status = ath11k_hif_map_service_to_pipe(htc->ab,744ep->service_id,745&ep->ul_pipe_id,746&ep->dl_pipe_id);747if (status)748return status;749750ath11k_dbg(ab, ATH11K_DBG_BOOT,751"htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",752htc_service_name(ep->service_id), ep->ul_pipe_id,753ep->dl_pipe_id, ep->eid);754755if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {756ep->tx_credit_flow_enabled = false;757ath11k_dbg(ab, ATH11K_DBG_BOOT,758"htc service '%s' eid %d tx flow control disabled\n",759htc_service_name(ep->service_id), assigned_eid);760}761762return status;763}764765int ath11k_htc_start(struct ath11k_htc *htc)766{767struct sk_buff *skb;768int status = 0;769struct ath11k_base *ab = htc->ab;770struct ath11k_htc_setup_complete_extended *msg;771772skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);773if (!skb)774return -ENOMEM;775776skb_put(skb, sizeof(*msg));777memset(skb->data, 0, skb->len);778779msg = (struct ath11k_htc_setup_complete_extended *)skb->data;780msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID,781ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID);782783if (ab->hw_params.credit_flow)784ath11k_dbg(ab, ATH11K_DBG_HTC, "using tx credit flow control\n");785else786msg->flags |= ATH11K_GLOBAL_DISABLE_CREDIT_FLOW;787788status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);789if (status) {790kfree_skb(skb);791return status;792}793794return 0;795}796797int ath11k_htc_init(struct ath11k_base *ab)798{799struct ath11k_htc *htc = &ab->htc;800struct ath11k_htc_svc_conn_req conn_req;801struct ath11k_htc_svc_conn_resp conn_resp;802int ret;803804spin_lock_init(&htc->tx_lock);805806ath11k_htc_reset_endpoint_states(htc);807808htc->ab = ab;809810switch (ab->wmi_ab.preferred_hw_mode) {811case WMI_HOST_HW_MODE_SINGLE:812htc->wmi_ep_count = 1;813break;814case WMI_HOST_HW_MODE_DBS:815case WMI_HOST_HW_MODE_DBS_OR_SBS:816htc->wmi_ep_count = 2;817break;818case WMI_HOST_HW_MODE_DBS_SBS:819htc->wmi_ep_count = 3;820break;821default:822htc->wmi_ep_count = ab->hw_params.max_radios;823break;824}825826/* setup our pseudo HTC control endpoint connection */827memset(&conn_req, 0, sizeof(conn_req));828memset(&conn_resp, 0, sizeof(conn_resp));829conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete;830conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete;831conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS;832conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL;833834/* connect fake service */835ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp);836if (ret) {837ath11k_err(ab, "could not connect to htc service (%d)\n", ret);838return ret;839}840841init_completion(&htc->ctl_resp);842843return 0;844}845846847