diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/htc.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath11k/htc.c | 168 |
1 files changed, 120 insertions, 48 deletions
diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c index ad13c648b679..4571d01cc33d 100644 --- a/drivers/net/wireless/ath/ath11k/htc.c +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -46,19 +47,9 @@ static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab) skb_cb = ATH11K_SKB_CB(skb); memset(skb_cb, 0, sizeof(*skb_cb)); - ath11k_dbg(ab, ATH11K_DBG_HTC, "%s: skb %pK\n", __func__, skb); return skb; } -static inline void ath11k_htc_restore_tx_skb(struct ath11k_htc *htc, - struct sk_buff *skb) -{ - struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb); - - dma_unmap_single(htc->ab->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); - skb_pull(skb, sizeof(struct ath11k_htc_hdr)); -} - static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep, struct sk_buff *skb) { @@ -69,9 +60,11 @@ static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep, memset(hdr, 0, sizeof(*hdr)); hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) | FIELD_PREP(HTC_HDR_PAYLOADLEN, - (skb->len - sizeof(*hdr))) | - FIELD_PREP(HTC_HDR_FLAGS, - ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE); + (skb->len - sizeof(*hdr))); + + if (ep->tx_credit_flow_enabled) + hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS, + ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE); spin_lock_bh(&ep->htc->tx_lock); hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++); @@ -88,6 +81,8 @@ int ath11k_htc_send(struct ath11k_htc *htc, struct ath11k_base *ab = htc->ab; int credits = 0; int ret; + bool credit_flow_enabled = (ab->hw_params.credit_flow && + ep->tx_credit_flow_enabled); if (eid >= ATH11K_HTC_EP_COUNT) { ath11k_warn(ab, "Invalid endpoint id: %d\n", eid); @@ -96,12 +91,12 @@ int ath11k_htc_send(struct ath11k_htc *htc, skb_push(skb, sizeof(struct ath11k_htc_hdr)); - if (ep->tx_credit_flow_enabled) { + if (credit_flow_enabled) { credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); spin_lock_bh(&htc->tx_lock); if (ep->tx_credits < credits) { ath11k_dbg(ab, ATH11K_DBG_HTC, - "htc insufficient credits ep %d required %d available %d\n", + "ep %d insufficient credits required %d total %d\n", eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); ret = -EAGAIN; @@ -109,7 +104,7 @@ int ath11k_htc_send(struct ath11k_htc *htc, } ep->tx_credits -= credits; ath11k_dbg(ab, ATH11K_DBG_HTC, - "htc ep %d consumed %d credits (total %d)\n", + "ep %d credits consumed %d total %d\n", eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); } @@ -124,6 +119,9 @@ int ath11k_htc_send(struct ath11k_htc *htc, goto err_credits; } + ath11k_dbg(ab, ATH11K_DBG_HTC, "tx skb %p eid %d paddr %pad\n", + skb, skb_cb->eid, &skb_cb->paddr); + ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid); if (ret) goto err_unmap; @@ -133,11 +131,11 @@ int ath11k_htc_send(struct ath11k_htc *htc, err_unmap: dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); err_credits: - if (ep->tx_credit_flow_enabled) { + if (credit_flow_enabled) { spin_lock_bh(&htc->tx_lock); ep->tx_credits += credits; ath11k_dbg(ab, ATH11K_DBG_HTC, - "htc ep %d reverted %d credits back (total %d)\n", + "ep %d credits reverted %d total %d\n", eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); @@ -172,7 +170,7 @@ ath11k_htc_process_credit_report(struct ath11k_htc *htc, ep = &htc->endpoint[report->eid]; ep->tx_credits += report->credits; - ath11k_dbg(ab, ATH11K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", + ath11k_dbg(ab, ATH11K_DBG_HTC, "ep %d credits got %d total %d\n", report->eid, report->credits, ep->tx_credits); if (ep->ep_ops.ep_tx_credits) { @@ -210,23 +208,25 @@ static int ath11k_htc_process_trailer(struct ath11k_htc *htc, break; } - switch (record->hdr.id) { - case ATH11K_HTC_RECORD_CREDITS: - len = sizeof(struct ath11k_htc_credit_report); - if (record->hdr.len < len) { - ath11k_warn(ab, "Credit report too long\n"); - status = -EINVAL; + if (ab->hw_params.credit_flow) { + switch (record->hdr.id) { + case ATH11K_HTC_RECORD_CREDITS: + len = sizeof(struct ath11k_htc_credit_report); + if (record->hdr.len < len) { + ath11k_warn(ab, "Credit report too long\n"); + status = -EINVAL; + break; + } + ath11k_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, + src_eid); + break; + default: + ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", + record->hdr.id, record->hdr.len); break; } - ath11k_htc_process_credit_report(htc, - record->credit_report, - record->hdr.len, - src_eid); - break; - default: - ath11k_warn(ab, "Unhandled record: id:%d length:%d\n", - record->hdr.id, record->hdr.len); - break; } if (status) @@ -240,6 +240,48 @@ static int ath11k_htc_process_trailer(struct ath11k_htc *htc, return status; } +static void ath11k_htc_suspend_complete(struct ath11k_base *ab, bool ack) +{ + ath11k_dbg(ab, ATH11K_DBG_BOOT, "suspend complete %d\n", ack); + + if (ack) + set_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); + else + clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); + + complete(&ab->htc_suspend); +} + +void ath11k_htc_tx_completion_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htc *htc = &ab->htc; + struct ath11k_htc_ep *ep; + void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *); + u8 eid; + + eid = ATH11K_SKB_CB(skb)->eid; + if (eid >= ATH11K_HTC_EP_COUNT) { + dev_kfree_skb_any(skb); + return; + } + + ep = &htc->endpoint[eid]; + spin_lock_bh(&htc->tx_lock); + ep_tx_complete = ep->ep_ops.ep_tx_complete; + spin_unlock_bh(&htc->tx_lock); + if (!ep_tx_complete) { + dev_kfree_skb_any(skb); + return; + } + ep_tx_complete(htc->ab, skb); +} + +static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab) +{ + ath11k_dbg(ab, ATH11K_DBG_BOOT, "wakeup from suspend is received\n"); +} + void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, struct sk_buff *skb) { @@ -248,7 +290,7 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, struct ath11k_htc_hdr *hdr; struct ath11k_htc_ep *ep; u16 payload_len; - u32 trailer_len = 0; + u32 message_id, trailer_len = 0; size_t min_len; u8 eid; bool trailer_present; @@ -283,6 +325,9 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) & ATH11K_HTC_FLAG_TRAILER_PRESENT; + ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p trailer_present %d\n", + eid, skb, trailer_present); + if (trailer_present) { u8 *trailer; @@ -315,7 +360,12 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, if (eid == ATH11K_HTC_EP_0) { struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data; - switch (FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)) { + message_id = FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id); + + ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p message_id %d\n", + eid, skb, message_id); + + switch (message_id) { case ATH11K_HTC_MSG_READY_ID: case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID: /* handle HTC control message */ @@ -337,15 +387,23 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, complete(&htc->ctl_resp); break; + case ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE: + ath11k_htc_suspend_complete(ab, true); + break; + case ATH11K_HTC_MSG_NACK_SUSPEND: + ath11k_htc_suspend_complete(ab, false); + break; + case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: + ath11k_htc_wakeup_from_suspend(ab); + break; default: - ath11k_warn(ab, "ignoring unsolicited htc ep0 event\n"); + ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n", + FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id)); break; } goto out; } - ath11k_dbg(ab, ATH11K_DBG_HTC, "htc rx completion ep %d skb %pK\n", - eid, skb); ep->ep_ops.ep_rx_complete(ab, skb); /* poll tx completion for interrupt disabled CE's */ @@ -439,7 +497,7 @@ static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc, static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc) { struct ath11k_htc_svc_tx_credits *serv_entry; - u32 svc_id[] = { + static const u32 svc_id[] = { ATH11K_HTC_SVC_ID_WMI_CONTROL, ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1, ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2, @@ -478,7 +536,7 @@ int ath11k_htc_wait_target(struct ath11k_htc *htc) if (!time_left) { ath11k_warn(ab, "failed to receive control response completion, polling..\n"); - for (i = 0; i < CE_COUNT; i++) + for (i = 0; i < ab->hw_params.ce_count; i++) ath11k_ce_per_engine_service(htc->ab, i); time_left = @@ -515,7 +573,7 @@ int ath11k_htc_wait_target(struct ath11k_htc *htc) htc->target_credit_size = credit_size; ath11k_dbg(ab, ATH11K_DBG_HTC, - "Target ready! transmit resources: %d size:%d\n", + "target ready total_transmit_credits %d target_credit_size %d\n", htc->total_transmit_credits, htc->target_credit_size); if ((htc->total_transmit_credits == 0) || @@ -524,6 +582,12 @@ int ath11k_htc_wait_target(struct ath11k_htc *htc) return -ECOMM; } + /* For QCA6390, wmi endpoint uses 1 credit to avoid + * back-to-back write. + */ + if (ab->hw_params.supports_shadow_regs) + htc->total_transmit_credits = 1; + ath11k_htc_setup_target_buffer_assignments(htc); return 0; @@ -560,7 +624,7 @@ int ath11k_htc_connect_service(struct ath11k_htc *htc, conn_req->service_id); if (!tx_alloc) ath11k_dbg(ab, ATH11K_DBG_BOOT, - "boot htc service %s does not allocate target credits\n", + "htc service %s does not allocate target credits\n", htc_service_name(conn_req->service_id)); skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); @@ -587,6 +651,11 @@ int ath11k_htc_connect_service(struct ath11k_htc *htc, disable_credit_flow_ctrl = true; } + if (!ab->hw_params.credit_flow) { + flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = true; + } + req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags); req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID, conn_req->service_id); @@ -620,7 +689,7 @@ int ath11k_htc_connect_service(struct ath11k_htc *htc, } ath11k_dbg(ab, ATH11K_DBG_HTC, - "HTC Service %s connect response: status: 0x%lx, assigned ep: 0x%lx\n", + "service %s connect response status 0x%lx assigned ep 0x%lx\n", htc_service_name(service_id), FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len), FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len)); @@ -680,14 +749,14 @@ setup: return status; ath11k_dbg(ab, ATH11K_DBG_BOOT, - "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", + "htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; ath11k_dbg(ab, ATH11K_DBG_BOOT, - "boot htc service '%s' eid %d TX flow control disabled\n", + "htc service '%s' eid %d tx flow control disabled\n", htc_service_name(ep->service_id), assigned_eid); } @@ -712,7 +781,10 @@ int ath11k_htc_start(struct ath11k_htc *htc) msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID, ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID); - ath11k_dbg(ab, ATH11K_DBG_HTC, "HTC is using TX credit flow control\n"); + if (ab->hw_params.credit_flow) + ath11k_dbg(ab, ATH11K_DBG_HTC, "using tx credit flow control\n"); + else + msg->flags |= ATH11K_GLOBAL_DISABLE_CREDIT_FLOW; status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb); if (status) { @@ -748,7 +820,7 @@ int ath11k_htc_init(struct ath11k_base *ab) htc->wmi_ep_count = 3; break; default: - htc->wmi_ep_count = 3; + htc->wmi_ep_count = ab->hw_params.max_radios; break; } |
