diff options
132 files changed, 50743 insertions, 1377 deletions
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml new file mode 100644 index 000000000000..a1717db36dba --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml @@ -0,0 +1,273 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/qcom,ath11k.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies ath11k wireless devices Generic Binding + +maintainers: + - Kalle Valo <kvalo@codeaurora.org> + +description: | + These are dt entries for Qualcomm Technologies, Inc. IEEE 802.11ax + devices, for example like AHB based IPQ8074. + +properties: + compatible: + const: qcom,ipq8074-wifi + + reg: + maxItems: 1 + + interrupts: + items: + - description: misc-pulse1 interrupt events + - description: misc-latch interrupt events + - description: sw exception interrupt events + - description: watchdog interrupt events + - description: interrupt event for ring CE0 + - description: interrupt event for ring CE1 + - description: interrupt event for ring CE2 + - description: interrupt event for ring CE3 + - description: interrupt event for ring CE4 + - description: interrupt event for ring CE5 + - description: interrupt event for ring CE6 + - description: interrupt event for ring CE7 + - description: interrupt event for ring CE8 + - description: interrupt event for ring CE9 + - description: interrupt event for ring CE10 + - description: interrupt event for ring CE11 + - description: interrupt event for ring host2wbm-desc-feed + - description: interrupt event for ring host2reo-re-injection + - description: interrupt event for ring host2reo-command + - description: interrupt event for ring host2rxdma-monitor-ring3 + - description: interrupt event for ring host2rxdma-monitor-ring2 + - description: interrupt event for ring host2rxdma-monitor-ring1 + - description: interrupt event for ring reo2ost-exception + - description: interrupt event for ring wbm2host-rx-release + - description: interrupt event for ring reo2host-status + - description: interrupt event for ring reo2host-destination-ring4 + - description: interrupt event for ring reo2host-destination-ring3 + - description: interrupt event for ring reo2host-destination-ring2 + - description: interrupt event for ring reo2host-destination-ring1 + - description: interrupt event for ring rxdma2host-monitor-destination-mac3 + - description: interrupt event for ring rxdma2host-monitor-destination-mac2 + - description: interrupt event for ring rxdma2host-monitor-destination-mac1 + - description: interrupt event for ring ppdu-end-interrupts-mac3 + - description: interrupt event for ring ppdu-end-interrupts-mac2 + - description: interrupt event for ring ppdu-end-interrupts-mac1 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac3 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac2 + - description: interrupt event for ring rxdma2host-monitor-status-ring-mac1 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac3 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac2 + - description: interrupt event for ring host2rxdma-host-buf-ring-mac1 + - description: interrupt event for ring rxdma2host-destination-ring-mac3 + - description: interrupt event for ring rxdma2host-destination-ring-mac2 + - description: interrupt event for ring rxdma2host-destination-ring-mac1 + - description: interrupt event for ring host2tcl-input-ring4 + - description: interrupt event for ring host2tcl-input-ring3 + - description: interrupt event for ring host2tcl-input-ring2 + - description: interrupt event for ring host2tcl-input-ring1 + - description: interrupt event for ring wbm2host-tx-completions-ring3 + - description: interrupt event for ring wbm2host-tx-completions-ring2 + - description: interrupt event for ring wbm2host-tx-completions-ring1 + - description: interrupt event for ring tcl2host-status-ring + + + interrupt-names: + items: + - const: misc-pulse1 + - const: misc-latch + - const: sw-exception + - const: watchdog + - const: ce0 + - const: ce1 + - const: ce2 + - const: ce3 + - const: ce4 + - const: ce5 + - const: ce6 + - const: ce7 + - const: ce8 + - const: ce9 + - const: ce10 + - const: ce11 + - const: host2wbm-desc-feed + - const: host2reo-re-injection + - const: host2reo-command + - const: host2rxdma-monitor-ring3 + - const: host2rxdma-monitor-ring2 + - const: host2rxdma-monitor-ring1 + - const: reo2ost-exception + - const: wbm2host-rx-release + - const: reo2host-status + - const: reo2host-destination-ring4 + - const: reo2host-destination-ring3 + - const: reo2host-destination-ring2 + - const: reo2host-destination-ring1 + - const: rxdma2host-monitor-destination-mac3 + - const: rxdma2host-monitor-destination-mac2 + - const: rxdma2host-monitor-destination-mac1 + - const: ppdu-end-interrupts-mac3 + - const: ppdu-end-interrupts-mac2 + - const: ppdu-end-interrupts-mac1 + - const: rxdma2host-monitor-status-ring-mac3 + - const: rxdma2host-monitor-status-ring-mac2 + - const: rxdma2host-monitor-status-ring-mac1 + - const: host2rxdma-host-buf-ring-mac3 + - const: host2rxdma-host-buf-ring-mac2 + - const: host2rxdma-host-buf-ring-mac1 + - const: rxdma2host-destination-ring-mac3 + - const: rxdma2host-destination-ring-mac2 + - const: rxdma2host-destination-ring-mac1 + - const: host2tcl-input-ring4 + - const: host2tcl-input-ring3 + - const: host2tcl-input-ring2 + - const: host2tcl-input-ring1 + - const: wbm2host-tx-completions-ring3 + - const: wbm2host-tx-completions-ring2 + - const: wbm2host-tx-completions-ring1 + - const: tcl2host-status-ring + + qcom,rproc: + $ref: /schemas/types.yaml#definitions/phandle + description: + DT entry of q6v5-wcss remoteproc driver. + Phandle to a node that can contain the following properties + * compatible + * reg + * reg-names + +required: + - compatible + - reg + - interrupts + - interrupt-names + - qcom,rproc + +additionalProperties: false + +examples: + - | + + q6v5_wcss: q6v5_wcss@CD00000 { + compatible = "qcom,ipq8074-wcss-pil"; + reg = <0xCD00000 0x4040>, + <0x4AB000 0x20>; + reg-names = "qdsp6", + "rmb"; + }; + + wifi0: wifi@c000000 { + compatible = "qcom,ipq8074-wifi"; + reg = <0xc000000 0x2000000>; + interrupts = <0 320 1>, + <0 319 1>, + <0 318 1>, + <0 317 1>, + <0 316 1>, + <0 315 1>, + <0 314 1>, + <0 311 1>, + <0 310 1>, + <0 411 1>, + <0 410 1>, + <0 40 1>, + <0 39 1>, + <0 302 1>, + <0 301 1>, + <0 37 1>, + <0 36 1>, + <0 296 1>, + <0 295 1>, + <0 294 1>, + <0 293 1>, + <0 292 1>, + <0 291 1>, + <0 290 1>, + <0 289 1>, + <0 288 1>, + <0 239 1>, + <0 236 1>, + <0 235 1>, + <0 234 1>, + <0 233 1>, + <0 232 1>, + <0 231 1>, + <0 230 1>, + <0 229 1>, + <0 228 1>, + <0 224 1>, + <0 223 1>, + <0 203 1>, + <0 183 1>, + <0 180 1>, + <0 179 1>, + <0 178 1>, + <0 177 1>, + <0 176 1>, + <0 163 1>, + <0 162 1>, + <0 160 1>, + <0 159 1>, + <0 158 1>, + <0 157 1>, + <0 156 1>; + interrupt-names = "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring"; + qcom,rproc = <&q6v5_wcss>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a28c77ee6b0d..60131db28141 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9956,7 +9956,7 @@ F: drivers/net/ethernet/marvell/mvneta.* MARVELL MWIFIEX WIRELESS DRIVER M: Amitkumar Karwar <amitkarwar@gmail.com> M: Nishant Sarmukadam <nishants@marvell.com> -M: Ganapathi Bhat <gbhat@marvell.com> +M: Ganapathi Bhat <ganapathi.bhat@nxp.com> M: Xinming Hu <huxinming820@gmail.com> L: linux-wireless@vger.kernel.org S: Maintained @@ -13644,6 +13644,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git S: Supported F: drivers/net/wireless/ath/ath10k/ +QUALCOMM ATHEROS ATH11K WIRELESS DRIVER +M: Kalle Valo <kvalo@codeaurora.org> +L: ath11k@lists.infradead.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git +S: Supported +F: drivers/net/wireless/ath/ath11k/ + QUALCOMM ATHEROS ATH9K WIRELESS DRIVER M: QCA ath9k Development <ath9k-devel@qca.qualcomm.com> L: linux-wireless@vger.kernel.org diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 4a2d1b235fb5..1f2de714b401 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -219,7 +219,7 @@ static s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr) static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr, u32 type, u8 port) { - u32 addrl, addrh, sizel, sizeh = 0; + u32 addrl, addrh, sizeh = 0; u32 size; u32 ent = bcma_erom_get_ent(bus, eromptr); @@ -239,12 +239,9 @@ static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr, if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) { size = bcma_erom_get_ent(bus, eromptr); - sizel = size & SCAN_SIZE_SZ; if (size & SCAN_SIZE_SG32) sizeh = bcma_erom_get_ent(bus, eromptr); - } else - sizel = SCAN_ADDR_SZ_BASE << - ((ent & SCAN_ADDR_SZ) >> SCAN_ADDR_SZ_SHIFT); + } return addrl; } diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 7b90b8546162..b10972b6cba4 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -62,5 +62,6 @@ source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig" source "drivers/net/wireless/ath/wcn36xx/Kconfig" +source "drivers/net/wireless/ath/ath11k/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index ee2b2431e5a3..8e4ae9de5ae4 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_WCN36XX) += wcn36xx/ +obj-$(CONFIG_ATH11K) += ath11k/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 95dc4be82e5c..ea908107581d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -346,6 +346,52 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) return 0; } +static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length) +{ + struct bmi_cmd *cmd; + u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data); + u32 txlen; + int ret; + size_t buf_len; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", + buffer, length); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "command disallowed\n"); + return -EBUSY; + } + + buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE; + cmd = kzalloc(buf_len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + while (length) { + txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen); + + WARN_ON_ONCE(txlen & 3); + + cmd->id = __cpu_to_le32(BMI_LZ_DATA); + cmd->lz_data.len = __cpu_to_le32(txlen); + memcpy(cmd->lz_data.payload, buffer, txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn(ar, "unable to write to the device\n"); + return ret; + } + + buffer += txlen; + length -= txlen; + } + + kfree(cmd); + + return 0; +} + int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) { struct bmi_cmd cmd; @@ -430,7 +476,11 @@ int ath10k_bmi_fast_download(struct ath10k *ar, if (trailer_len > 0) memcpy(trailer, buffer + head_len, trailer_len); - ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ar->hw_params.bmi_large_size_download) + ret = ath10k_bmi_lz_data_large(ar, buffer, head_len); + else + ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index ef3bdba43bed..f6fadcbdd86e 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -45,6 +45,15 @@ sizeof(u32) + \ sizeof(u32)) +/* Maximum data size used for large BMI transfers */ +#define BMI_MAX_LARGE_DATA_SIZE 2048 + +/* len = cmd + addr + length */ +#define BMI_MAX_LARGE_CMDBUF_SIZE (BMI_MAX_LARGE_DATA_SIZE + \ + sizeof(u32) + \ + sizeof(u32) + \ + sizeof(u32)) + /* BMI Commands */ enum bmi_cmd_id { @@ -258,6 +267,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result); int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); + int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4f76ba5d78a9..5ec16ce19b69 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -189,6 +189,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .num_wds_entries = 0x20, .uart_pin_workaround = true, .tx_stats_over_pktlog = false, + .bmi_large_size_download = true, }, { .id = QCA6174_HW_2_1_VERSION, @@ -714,18 +715,6 @@ static int ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode) if (ret) return ret; - /* Explicitly set fwlog prints to zero as target may turn it on - * based on scratch registers. - */ - ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m); - if (ret) - return ret; - - param |= HI_OPTION_DISABLE_DBGLOG; - ret = ath10k_bmi_write32(ar, hi_option_flag, param); - if (ret) - return ret; - return 0; } @@ -3231,6 +3220,8 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_waitqueue_head(&ar->htt.empty_tx_wq); init_waitqueue_head(&ar->wmi.tx_credits_wq); + skb_queue_head_init(&ar->htt.rx_indication_head); + init_completion(&ar->offchan_tx_completed); INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); skb_queue_head_init(&ar->offchan_tx_queue); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index af68eb5d0776..5101bf2b5b15 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -124,6 +124,7 @@ struct ath10k_skb_cb { struct ath10k_skb_rxcb { dma_addr_t paddr; struct hlist_node hlist; + u8 eid; }; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) @@ -1180,6 +1181,7 @@ struct ath10k { struct { /* protected by data_lock */ + u32 rx_crc_err_drop; u32 fw_crash_counter; u32 fw_warm_reset_counter; u32 fw_cold_reset_counter; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 04c50a26a4f4..e000677ac516 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1094,6 +1094,7 @@ static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = { "d_rts_good", "d_tx_power", /* in .5 dbM I think */ "d_rx_crc_err", /* fcs_bad */ + "d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */ "d_no_beacon", "d_tx_mpdus_queued", "d_tx_msdu_queued", @@ -1193,6 +1194,7 @@ void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, data[i++] = pdev_stats->rts_good; data[i++] = pdev_stats->chan_tx_power; data[i++] = pdev_stats->fcs_bad; + data[i++] = ar->stats.rx_crc_err_drop; data[i++] = pdev_stats->no_beacons; data[i++] = pdev_stats->mpdu_enqued; data[i++] = pdev_stats->msdu_enqued; diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 1d4d1a1992fe..2248d6c022f4 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -270,7 +270,7 @@ ath10k_htc_process_lookahead_bundle(struct ath10k_htc *htc, struct ath10k *ar = htc->ar; int bundle_cnt = len / sizeof(*report); - if (!bundle_cnt || (bundle_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE)) { + if (!bundle_cnt || (bundle_cnt > htc->max_msgs_per_htc_bundle)) { ath10k_warn(ar, "Invalid lookahead bundle count: %d\n", bundle_cnt); return -EINVAL; @@ -800,8 +800,8 @@ setup: &ep->ul_pipe_id, &ep->dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ep->service_id); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC service id: %d\n", + ep->service_id); return status; } @@ -878,8 +878,8 @@ static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) &ul_pipe_id, &dl_pipe_id); if (status) { - ath10k_warn(ar, "unsupported HTC service id: %d\n", - ATH10K_HTC_SVC_ID_HTT_LOG_MSG); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "unsupported HTC pktlog service id: %d\n", + ATH10K_HTC_SVC_ID_HTT_LOG_MSG); return false; } diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index f55d3caec61f..065c82d9d689 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -12,6 +12,7 @@ #include <linux/bug.h> #include <linux/skbuff.h> #include <linux/timer.h> +#include <linux/bitfield.h> struct ath10k; @@ -39,7 +40,7 @@ struct ath10k; * 4-byte aligned. */ -#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 8 +#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 32 enum ath10k_htc_tx_flags { ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, @@ -49,9 +50,27 @@ enum ath10k_htc_tx_flags { enum ath10k_htc_rx_flags { ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK = 0x01, ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02, - ATH10K_HTC_FLAG_BUNDLE_MASK = 0xF0 }; +#define ATH10K_HTC_FLAG_BUNDLE_MASK GENMASK(7, 4) + +/* bits 2-3 are for extra bundle count bits 4-5 */ +#define ATH10K_HTC_BUNDLE_EXTRA_MASK GENMASK(3, 2) +#define ATH10K_HTC_BUNDLE_EXTRA_SHIFT 4 + +static inline unsigned int ath10k_htc_get_bundle_count(u8 max_msgs, u8 flags) +{ + unsigned int count, extra_count = 0; + + count = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, flags); + + if (max_msgs > 16) + extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) << + ATH10K_HTC_BUNDLE_EXTRA_SHIFT; + + return count + extra_count; +} + struct ath10k_htc_hdr { u8 eid; /* @enum ath10k_htc_ep_id */ u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */ diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 30c080094af1..4a12564fc30e 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1869,6 +1869,8 @@ struct ath10k_htt { struct ath10k *ar; enum ath10k_htc_ep_id eid; + struct sk_buff_head rx_indication_head; + u8 target_version_major; u8 target_version_minor; struct completion target_version_received; @@ -2283,6 +2285,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu); void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget); +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget); void ath10k_htt_set_tx_ops(struct ath10k_htt *htt); void ath10k_htt_set_rx_ops(struct ath10k_htt *htt); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index d95b63f133ab..7fb15180c685 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1285,6 +1285,13 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) status = IEEE80211_SKB_RXCB(skb); + if (!(ar->filter_flags & FIF_FCSFAIL) && + status->flag & RX_FLAG_FAILED_FCS_CRC) { + ar->stats.rx_crc_err_drop++; + dev_kfree_skb_any(skb); + return; + } + ath10k_dbg(ar, ATH10K_DBG_DATA, "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, @@ -2196,8 +2203,8 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, HTT_RX_IND_MPDU_STATUS_OK && mpdu_ranges->mpdu_range_status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) { - ath10k_warn(ar, "MPDU range status: %d\n", - mpdu_ranges->mpdu_range_status); + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt mpdu_range_status %d\n", + mpdu_ranges->mpdu_range_status); goto err; } @@ -2235,8 +2242,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, hdr = (struct ieee80211_hdr *)skb->data; qos = ieee80211_is_data_qos(hdr->frame_control); + rx_status = IEEE80211_SKB_RXCB(skb); - rx_status->chains |= BIT(0); + memset(rx_status, 0, sizeof(*rx_status)); + if (rx->ppdu.combined_rssi == 0) { /* SDIO firmware does not provide signal */ rx_status->signal = 0; @@ -2350,7 +2359,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt, memcpy(skb->data + offset, &qos_ctrl, IEEE80211_QOS_CTL_LEN); } - ieee80211_rx_ni(ar->hw, skb); + if (ar->napi.dev) + ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi); + else + ieee80211_rx_ni(ar->hw, skb); /* We have delivered the skb to the upper layers (mac80211) so we * must not free it. @@ -3751,14 +3763,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IND: - if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) - return ath10k_htt_rx_proc_rx_ind_hl(htt, - &resp->rx_ind_hl, - skb, - HTT_RX_PN_CHECK, - HTT_RX_NON_TKIP_MIC); - else + if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL) { ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind); + } else { + skb_queue_tail(&htt->rx_indication_head, skb); + return false; + } break; case HTT_T2H_MSG_TYPE_PEER_MAP: { struct htt_peer_map_event ev = { @@ -3948,6 +3958,37 @@ static int ath10k_htt_rx_deliver_msdu(struct ath10k *ar, int quota, int budget) return quota; } +int ath10k_htt_rx_hl_indication(struct ath10k *ar, int budget) +{ + struct htt_resp *resp; + struct ath10k_htt *htt = &ar->htt; + struct sk_buff *skb; + bool release; + int quota; + + for (quota = 0; quota < budget; quota++) { + skb = skb_dequeue(&htt->rx_indication_head); + if (!skb) + break; + + resp = (struct htt_resp *)skb->data; + + release = ath10k_htt_rx_proc_rx_ind_hl(htt, + &resp->rx_ind_hl, + skb, + HTT_RX_PN_CHECK, + HTT_RX_NON_TKIP_MIC); + + if (release) + dev_kfree_skb_any(skb); + + ath10k_dbg(ar, ATH10K_DBG_HTT, "rx indication poll pending count:%d\n", + skb_queue_len(&htt->rx_indication_head)); + } + return quota; +} +EXPORT_SYMBOL(ath10k_htt_rx_hl_indication); + int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget) { struct ath10k_htt *htt = &ar->htt; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 35a362329a4f..21b7a2a873b0 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -613,6 +613,9 @@ struct ath10k_hw_params { /* target supporting fw download via diag ce */ bool fw_diag_ce_download; + /* target supporting fw download via large size BMI */ + bool bmi_large_size_download; + /* need to set uart pin if disable uart print, workaround for a * firmware bug */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 83cc8778ca1e..0969e9bf5d5e 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1098,7 +1098,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n", + ath10k_warn(ar, "failed to request monitor vdev %i stop: %d\n", ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); @@ -6329,6 +6329,9 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (sta && sta->tdls) ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, ar->wmi.peer_param->authorize, 1); + else if (sta && cmd == SET_KEY && (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + ath10k_wmi_peer_set_param(ar, arvif->vdev_id, peer_addr, + ar->wmi.peer_param->authorize, 1); exit: mutex_unlock(&ar->conf_mutex); @@ -8908,6 +8911,7 @@ int ath10k_mac_register(struct ath10k *ar) WMI_PNO_MAX_SCHED_SCAN_PLAN_INT; ar->hw->wiphy->max_sched_scan_plan_iterations = WMI_PNO_MAX_SCHED_SCAN_PLAN_ITRNS; + ar->hw->wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; } ar->hw->vif_data_size = sizeof(struct ath10k_vif); diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index a0ba07b85362..7b524b614f22 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -279,7 +279,15 @@ static int ath10k_qmi_bdf_dnld_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* end = 1 triggers a CRC check on the BDF. If this fails, we + * get a QMI_ERR_MALFORMED_MSG_V01 error, but the FW is still + * willing to use the BDF. For some platforms, all the valid + * released BDFs fail this CRC check, so attempt to detect this + * scenario and treat it as non-fatal. + */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + !(req->end == 1 && + resp.resp.result == QMI_ERR_MALFORMED_MSG_V01)) { ath10k_err(ar, "failed to download board data file: %d\n", resp.resp.error); ret = -EINVAL; @@ -635,7 +643,9 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi) if (ret < 0) goto out; - if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + /* older FW didn't support this request, which is not fatal */ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01 && + resp.resp.error != QMI_ERR_NOT_SUPPORTED_V01) { ath10k_err(ar, "host capability request rejected: %d\n", resp.resp.error); ret = -EINVAL; goto out; diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 120200a93bcc..6fdf71b8b676 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -24,6 +24,8 @@ #include "trace.h" #include "sdio.h" +#define ATH10K_SDIO_VSG_BUF_SIZE (64 * 1024) + /* inlined helper functions */ static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio, @@ -417,6 +419,7 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, struct ath10k_htc *htc = &ar->htc; struct ath10k_sdio_rx_data *pkt; struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; enum ath10k_htc_ep_id id; int ret, i, *n_lookahead_local; u32 *lookaheads_local; @@ -462,10 +465,16 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar, if (ret) goto out; - if (!pkt->trailer_only) - ep->ep_ops.ep_rx_complete(ar_sdio->ar, pkt->skb); - else + if (!pkt->trailer_only) { + cb = ATH10K_SKB_RXCB(pkt->skb); + cb->eid = id; + + skb_queue_tail(&ar_sdio->rx_head, pkt->skb); + queue_work(ar->workqueue_aux, + &ar_sdio->async_work_rx); + } else { kfree_skb(pkt->skb); + } /* The RX complete handler now owns the skb...*/ pkt->skb = NULL; @@ -484,21 +493,22 @@ out: return ret; } -static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar, - struct ath10k_sdio_rx_data *rx_pkts, - struct ath10k_htc_hdr *htc_hdr, - size_t full_len, size_t act_len, - size_t *bndl_cnt) +static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar, + struct ath10k_sdio_rx_data *rx_pkts, + struct ath10k_htc_hdr *htc_hdr, + size_t full_len, size_t act_len, + size_t *bndl_cnt) { int ret, i; + u8 max_msgs = ar->htc.max_msgs_per_htc_bundle; - *bndl_cnt = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, htc_hdr->flags); + *bndl_cnt = ath10k_htc_get_bundle_count(max_msgs, htc_hdr->flags); - if (*bndl_cnt > HTC_HOST_MAX_MSG_PER_RX_BUNDLE) { + if (*bndl_cnt > max_msgs) { ath10k_warn(ar, "HTC bundle length %u exceeds maximum %u\n", le16_to_cpu(htc_hdr->len), - HTC_HOST_MAX_MSG_PER_RX_BUNDLE); + max_msgs); return -ENOMEM; } @@ -529,12 +539,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, size_t full_len, act_len; bool last_in_bundle; int ret, i; + int pkt_cnt = 0; if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) { - ath10k_warn(ar, - "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", - n_lookaheads, - ATH10K_SDIO_MAX_RX_MSGS); + ath10k_warn(ar, "the total number of pkgs to be fetched (%u) exceeds maximum %u\n", + n_lookaheads, ATH10K_SDIO_MAX_RX_MSGS); ret = -ENOMEM; goto err; } @@ -543,10 +552,8 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, htc_hdr = (struct ath10k_htc_hdr *)&lookaheads[i]; last_in_bundle = false; - if (le16_to_cpu(htc_hdr->len) > - ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { - ath10k_warn(ar, - "payload length %d exceeds max htc length: %zu\n", + if (le16_to_cpu(htc_hdr->len) > ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH) { + ath10k_warn(ar, "payload length %d exceeds max htc length: %zu\n", le16_to_cpu(htc_hdr->len), ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH); ret = -ENOMEM; @@ -557,36 +564,37 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, full_len = ath10k_sdio_calc_txrx_padded_len(ar_sdio, act_len); if (full_len > ATH10K_SDIO_MAX_BUFFER_SIZE) { - ath10k_warn(ar, - "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", + ath10k_warn(ar, "rx buffer requested with invalid htc_hdr length (%d, 0x%x): %d\n", htc_hdr->eid, htc_hdr->flags, le16_to_cpu(htc_hdr->len)); ret = -EINVAL; goto err; } - if (htc_hdr->flags & ATH10K_HTC_FLAG_BUNDLE_MASK) { + if (ath10k_htc_get_bundle_count( + ar->htc.max_msgs_per_htc_bundle, htc_hdr->flags)) { /* HTC header indicates that every packet to follow * has the same padded length so that it can be * optimally fetched as a full bundle. */ size_t bndl_cnt; - ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar, - &ar_sdio->rx_pkts[i], - htc_hdr, - full_len, - act_len, - &bndl_cnt); + ret = ath10k_sdio_mbox_alloc_bundle(ar, + &ar_sdio->rx_pkts[pkt_cnt], + htc_hdr, + full_len, + act_len, + &bndl_cnt); if (ret) { - ath10k_warn(ar, "alloc_bundle error %d\n", ret); + ath10k_warn(ar, "failed to allocate a bundle: %d\n", + ret); goto err; } - n_lookaheads += bndl_cnt; - i += bndl_cnt; - /*Next buffer will be the last in the bundle */ + pkt_cnt += bndl_cnt; + + /* next buffer will be the last in the bundle */ last_in_bundle = true; } @@ -597,7 +605,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK) full_len += ATH10K_HIF_MBOX_BLOCK_SIZE; - ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i], + ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt], act_len, full_len, last_in_bundle, @@ -606,9 +614,11 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret); goto err; } + + pkt_cnt++; } - ar_sdio->n_rx_pkts = i; + ar_sdio->n_rx_pkts = pkt_cnt; return 0; @@ -622,59 +632,74 @@ err: return ret; } -static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar, - struct ath10k_sdio_rx_data *pkt) +static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0]; struct sk_buff *skb = pkt->skb; struct ath10k_htc_hdr *htc_hdr; int ret; ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, skb->data, pkt->alloc_len); - if (ret) - goto out; - /* Update actual length. The original length may be incorrect, - * as the FW will bundle multiple packets as long as their sizes - * fit within the same aligned length (pkt->alloc_len). - */ - htc_hdr = (struct ath10k_htc_hdr *)skb->data; - pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); - if (pkt->act_len > pkt->alloc_len) { - ath10k_warn(ar, "rx packet too large (%zu > %zu)\n", - pkt->act_len, pkt->alloc_len); - ret = -EMSGSIZE; - goto out; + if (ret) { + ar_sdio->n_rx_pkts = 0; + ath10k_sdio_mbox_free_rx_pkt(pkt); + return ret; } + htc_hdr = (struct ath10k_htc_hdr *)skb->data; + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); skb_put(skb, pkt->act_len); -out: - pkt->status = ret; - return ret; } -static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar) +static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar) { struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + struct ath10k_sdio_rx_data *pkt; + struct ath10k_htc_hdr *htc_hdr; int ret, i; + u32 pkt_offset, virt_pkt_len; + + virt_pkt_len = 0; + for (i = 0; i < ar_sdio->n_rx_pkts; i++) + virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len; + if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) { + ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len); + ret = -E2BIG; + goto err; + } + + ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr, + ar_sdio->vsg_buffer, virt_pkt_len); + if (ret) { + ath10k_warn(ar, "failed to read bundle packets: %d", ret); + goto err; + } + + pkt_offset = 0; for (i = 0; i < ar_sdio->n_rx_pkts; i++) { - ret = ath10k_sdio_mbox_rx_packet(ar, - &ar_sdio->rx_pkts[i]); - if (ret) - goto err; + pkt = &ar_sdio->rx_pkts[i]; + htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset); + pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr); + + skb_put_data(pkt->skb, htc_hdr, pkt->act_len); + pkt_offset += pkt->alloc_len; } return 0; err: /* Free all packets that was not successfully fetched. */ - for (; i < ar_sdio->n_rx_pkts; i++) + for (i = 0; i < ar_sdio->n_rx_pkts; i++) ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]); + ar_sdio->n_rx_pkts = 0; + return ret; } @@ -717,7 +742,10 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar, */ *done = false; - ret = ath10k_sdio_mbox_rx_fetch(ar); + if (ar_sdio->n_rx_pkts > 1) + ret = ath10k_sdio_mbox_rx_fetch_bundle(ar); + else + ret = ath10k_sdio_mbox_rx_fetch(ar); /* Process fetched packets. This will potentially update * n_lookaheads depending on if the packets contain lookahead @@ -1293,6 +1321,31 @@ static void __ath10k_sdio_write_async(struct ath10k *ar, ath10k_sdio_free_bus_req(ar, req); } +/* To improve throughput use workqueue to deliver packets to HTC layer, + * this way SDIO bus is utilised much better. + */ +static void ath10k_rx_indication_async_work(struct work_struct *work) +{ + struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, + async_work_rx); + struct ath10k *ar = ar_sdio->ar; + struct ath10k_htc_ep *ep; + struct ath10k_skb_rxcb *cb; + struct sk_buff *skb; + + while (true) { + skb = skb_dequeue(&ar_sdio->rx_head); + if (!skb) + break; + cb = ATH10K_SKB_RXCB(skb); + ep = &ar->htc.endpoint[cb->eid]; + ep->ep_ops.ep_rx_complete(ar, skb); + } + + if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + napi_schedule(&ar->napi); +} + static void ath10k_sdio_write_async_work(struct work_struct *work) { struct ath10k_sdio *ar_sdio = container_of(work, struct ath10k_sdio, @@ -1681,6 +1734,8 @@ static int ath10k_sdio_hif_start(struct ath10k *ar) struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); int ret; + napi_enable(&ar->napi); + /* Sleep 20 ms before HIF interrupts are disabled. * This will give target plenty of time to process the BMI done * request before interrupts are disabled. @@ -1805,13 +1860,16 @@ static void ath10k_sdio_hif_stop(struct ath10k *ar) } spin_unlock_bh(&ar_sdio->wr_async_lock); + + napi_synchronize(&ar->napi); + napi_disable(&ar->napi); } #ifdef CONFIG_PM static int ath10k_sdio_hif_suspend(struct ath10k *ar) { - return -EOPNOTSUPP; + return 0; } static int ath10k_sdio_hif_resume(struct ath10k *ar) @@ -1961,7 +2019,26 @@ static const struct ath10k_hif_ops ath10k_sdio_hif_ops = { */ static int ath10k_sdio_pm_suspend(struct device *device) { - return 0; + struct sdio_func *func = dev_to_sdio_func(device); + struct ath10k_sdio *ar_sdio = sdio_get_drvdata(func); + struct ath10k *ar = ar_sdio->ar; + mmc_pm_flag_t pm_flag, pm_caps; + int ret; + + if (!device_may_wakeup(ar->dev)) + return 0; + + pm_flag = MMC_PM_KEEP_POWER; + + ret = sdio_set_host_pm_flags(func, pm_flag); + if (ret) { + pm_caps = sdio_get_host_pm_caps(func); + ath10k_warn(ar, "failed to set sdio host pm flags (0x%x, 0x%x): %d\n", + pm_flag, pm_caps, ret); + return ret; + } + + return ret; } static int ath10k_sdio_pm_resume(struct device *device) @@ -1980,6 +2057,20 @@ static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend, #endif /* CONFIG_PM_SLEEP */ +static int ath10k_sdio_napi_poll(struct napi_struct *ctx, int budget) +{ + struct ath10k *ar = container_of(ctx, struct ath10k, napi); + int done; + + done = ath10k_htt_rx_hl_indication(ar, budget); + ath10k_dbg(ar, ATH10K_DBG_SDIO, "napi poll: done: %d, budget:%d\n", done, budget); + + if (done < budget) + napi_complete_done(ctx, done); + + return done; +} + static int ath10k_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -2005,6 +2096,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, return -ENOMEM; } + netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_sdio_napi_poll, + ATH10K_NAPI_BUDGET); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n", func->num, func->vendor, func->device, @@ -2020,6 +2114,12 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } + ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL); + if (!ar_sdio->vsg_buffer) { + ret = -ENOMEM; + goto err_core_destroy; + } + ar_sdio->irq_data.irq_en_reg = devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs), GFP_KERNEL); @@ -2028,7 +2128,7 @@ static int ath10k_sdio_probe(struct sdio_func *func, goto err_core_destroy; } - ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_CMDBUF_SIZE, GFP_KERNEL); + ar_sdio->bmi_buf = devm_kzalloc(ar->dev, BMI_MAX_LARGE_CMDBUF_SIZE, GFP_KERNEL); if (!ar_sdio->bmi_buf) { ret = -ENOMEM; goto err_core_destroy; @@ -2057,6 +2157,9 @@ static int ath10k_sdio_probe(struct sdio_func *func, for (i = 0; i < ATH10K_SDIO_BUS_REQUEST_MAX_NUM; i++) ath10k_sdio_free_bus_req(ar, &ar_sdio->bus_req[i]); + skb_queue_head_init(&ar_sdio->rx_head); + INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work); + dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device); switch (dev_id_base) { case QCA_MANUFACTURER_ID_AR6005_BASE: @@ -2080,6 +2183,8 @@ static int ath10k_sdio_probe(struct sdio_func *func, bus_params.chip_id = 0; bus_params.hl_msdu_ids = true; + ar->hw->max_mtu = ETH_DATA_LEN; + ret = ath10k_core_register(ar, &bus_params); if (ret) { ath10k_err(ar, "failed to register driver core: %d\n", ret); @@ -2106,6 +2211,9 @@ static void ath10k_sdio_remove(struct sdio_func *func) func->num, func->vendor, func->device); ath10k_core_unregister(ar); + + netif_napi_del(&ar->napi); + ath10k_core_destroy(ar); flush_workqueue(ar_sdio->workqueue); diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h index b8c7ac0330bd..33195f49acab 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.h +++ b/drivers/net/wireless/ath/ath10k/sdio.h @@ -89,10 +89,10 @@ * to the maximum value (HTC_HOST_MAX_MSG_PER_RX_BUNDLE). * * in this case the driver must allocate - * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) skb's. + * (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) skb's. */ #define ATH10K_SDIO_MAX_RX_MSGS \ - (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * HTC_HOST_MAX_MSG_PER_RX_BUNDLE) + (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL 0x00000868u #define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF 0xFFFEFFFF @@ -126,7 +126,6 @@ struct ath10k_sdio_rx_data { bool part_of_bundle; bool last_in_bundle; bool trailer_only; - int status; }; struct ath10k_sdio_irq_proc_regs { @@ -138,8 +137,8 @@ struct ath10k_sdio_irq_proc_regs { u8 rx_lookahead_valid; u8 host_int_status2; u8 gmbox_rx_avail; - __le32 rx_lookahead[2]; - __le32 rx_gmbox_lookahead_alias[2]; + __le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX]; + __le32 int_status_enable; }; struct ath10k_sdio_irq_enable_regs { @@ -187,6 +186,9 @@ struct ath10k_sdio { struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM]; /* free list of bus requests */ struct list_head bus_req_freeq; + + struct sk_buff_head rx_head; + /* protects access to bus_req_freeq */ spinlock_t lock; @@ -196,6 +198,13 @@ struct ath10k_sdio { struct ath10k *ar; struct ath10k_sdio_irq_data irq_data; + /* temporary buffer for sdio read. + * It is allocated when probe, and used for receive bundled packets, + * the read for bundled packets is not parallel, so it does not need + * protected. + */ + u8 *vsg_buffer; + /* temporary buffer for BMI requests */ u8 *bmi_buf; @@ -206,6 +215,8 @@ struct ath10k_sdio { struct list_head wr_asyncq; /* protects access to wr_asyncq */ spinlock_t wr_async_lock; + + struct work_struct async_work_rx; }; static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 16177497bba7..7e85c4916e7f 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1563,13 +1563,16 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ret = ath10k_qmi_init(ar, msa_size); if (ret) { ath10k_warn(ar, "failed to register wlfw qmi client: %d\n", ret); - goto err_core_destroy; + goto err_power_off; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n"); return 0; +err_power_off: + ath10k_hw_power_off(ar); + err_free_irq: ath10k_snoc_free_irq(ar); diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 1bffe3fbea3f..7a9b9bbcdbfc 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -65,7 +65,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi event cmd attribute: %d\n", + "failed to put testmode wmi event cmd attribute: %d\n", ret); kfree_skb(nl_skb); goto out; @@ -74,7 +74,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id); if (ret) { ath10k_warn(ar, - "failed to to put testmode wmi even cmd_id: %d\n", + "failed to put testmode wmi event cmd_id: %d\n", ret); kfree_skb(nl_skb); goto out; diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 69a1ec53df29..4e68debda9bf 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -841,7 +841,7 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, const struct wmi_tlv_mgmt_rx_ev *ev; const u8 *frame; u32 msdu_len; - int ret; + int ret, i; tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); if (IS_ERR(tb)) { @@ -865,6 +865,9 @@ static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, arg->phy_mode = ev->phy_mode; arg->rate = ev->rate; + for (i = 0; i < ARRAY_SIZE(ev->rssi); i++) + arg->rssi[i] = ev->rssi[i]; + msdu_len = __le32_to_cpu(arg->buf_len); if (skb->len < (frame - skb->data) + msdu_len) { @@ -3707,6 +3710,7 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, struct wmi_tlv *tlv; struct sk_buff *skb; __le32 *channel_list; + u16 tlv_len; size_t len; void *ptr; u32 i; @@ -3764,10 +3768,12 @@ ath10k_wmi_tlv_op_gen_config_pno_start(struct ath10k *ar, /* nlo_configured_parameters(nlo_list) */ cmd->no_of_ssids = __cpu_to_le32(min_t(u8, pno->uc_networks_count, WMI_NLO_MAX_SSIDS)); + tlv_len = __le32_to_cpu(cmd->no_of_ssids) * + sizeof(struct nlo_configured_parameters); tlv = ptr; tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); - tlv->len = __cpu_to_le16(len); + tlv->len = __cpu_to_le16(tlv_len); ptr += sizeof(*tlv); nlo_list = ptr; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9f564e2b7a14..13f7531d945d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2463,10 +2463,10 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rx_status; u32 channel; u32 phy_mode; - u32 snr; + u32 snr, rssi; u32 rate; u16 fc; - int ret; + int ret, i; ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg); if (ret) { @@ -2525,6 +2525,20 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; + + BUILD_BUG_ON(ARRAY_SIZE(status->chain_signal) != ARRAY_SIZE(arg.rssi)); + + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { + status->chains &= ~BIT(i); + rssi = __le32_to_cpu(arg.rssi[i]); + ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt rssi[%d]:%d\n", i, arg.rssi[i]); + + if (rssi != ATH10K_INVALID_RSSI && rssi != 0) { + status->chain_signal[i] = ATH10K_DEFAULT_NOISE_FLOOR + rssi; + status->chains |= BIT(i); + } + } + status->rate_idx = ath10k_mac_bitrate_to_idx(sband, rate / 100); hdr = (struct ieee80211_hdr *)skb->data; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 74adce1dd3a9..972d53d77654 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6786,6 +6786,7 @@ struct wmi_peer_delete_resp_ev_arg { struct wmi_mac_addr peer_addr; }; +#define WMI_MGMT_RX_NUM_RSSI 4 struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; @@ -6794,6 +6795,7 @@ struct wmi_mgmt_rx_ev_arg { __le32 buf_len; __le32 status; /* %WMI_RX_STATUS_ */ struct wmi_mgmt_rx_ext_info ext_info; + __le32 rssi[WMI_MGMT_RX_NUM_RSSI]; }; struct wmi_ch_info_ev_arg { diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig new file mode 100644 index 000000000000..cfab4fb86aef --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config ATH11K + tristate "Qualcomm Technologies 802.11ax chipset support" + depends on MAC80211 && HAS_DMA + depends on REMOTEPROC + depends on ARCH_QCOM || COMPILE_TEST + select ATH_COMMON + select QCOM_QMI_HELPERS + ---help--- + This module adds support for Qualcomm Technologies 802.11ax family of + chipsets. + + If you choose to build a module, it'll be called ath11k. + +config ATH11K_DEBUG + bool "QCA ath11k debugging" + depends on ATH11K + ---help--- + Enables debug support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_DEBUGFS + bool "QCA ath11k debugfs support" + depends on ATH11K && DEBUG_FS + ---help--- + Enable ath11k debugfs support + + If unsure, say Y to make it easier to debug problems. + +config ATH11K_TRACING + bool "ath11k tracing support" + depends on ATH11K && EVENT_TRACING + ---help--- + Select this to use ath11k tracing infrastructure. diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile new file mode 100644 index 000000000000..a91d75c1cfeb --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_ATH11K) += ath11k.o +ath11k-y += core.o \ + hal.o \ + hal_tx.o \ + hal_rx.o \ + ahb.o \ + wmi.o \ + mac.o \ + reg.o \ + htc.o \ + qmi.o \ + dp.o \ + dp_tx.o \ + dp_rx.o \ + debug.o \ + ce.o \ + peer.o + +ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o +ath11k-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o +ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o +ath11k-$(CONFIG_ATH11K_TRACING) += trace.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c new file mode 100644 index 000000000000..e7e3e64c07aa --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -0,0 +1,1003 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/dma-mapping.h> +#include "ahb.h" +#include "debug.h" +#include <linux/remoteproc.h> + +static const struct of_device_id ath11k_ahb_of_match[] = { + /* TODO: Should we change the compatible string to something similar + * to one that ath10k uses? + */ + { .compatible = "qcom,ipq8074-wifi", + .data = (void *)ATH11K_HW_IPQ8074, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(65535), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE9 host->target HTT */ + { + .pipenum = __cpu_to_le32(9), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE10 target->host HTT */ + { + .pipenum = __cpu_to_le32(10), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT_H2H), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE11 Not used */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(0), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(3), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(7), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(9), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(2), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(0), + }, + { /* not used */ + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + .pipenum = __cpu_to_le32(4), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(1), + }, + { + .service_id = __cpu_to_le32(ATH11K_HTC_SVC_ID_PKT_LOG), + .pipedir = __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + .pipenum = __cpu_to_le32(5), + }, + + /* (Additions here) */ + + { /* terminator entry */ } +}; + +#define ATH11K_IRQ_CE0_OFFSET 4 + +static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { + "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +#define ATH11K_TX_RING_MASK_0 0x1 +#define ATH11K_TX_RING_MASK_1 0x2 +#define ATH11K_TX_RING_MASK_2 0x4 + +#define ATH11K_RX_RING_MASK_0 0x1 +#define ATH11K_RX_RING_MASK_1 0x2 +#define ATH11K_RX_RING_MASK_2 0x4 +#define ATH11K_RX_RING_MASK_3 0x8 + +#define ATH11K_RX_ERR_RING_MASK_0 0x1 + +#define ATH11K_RX_WBM_REL_RING_MASK_0 0x1 + +#define ATH11K_REO_STATUS_RING_MASK_0 0x1 + +#define ATH11K_RXDMA2HOST_RING_MASK_0 0x1 +#define ATH11K_RXDMA2HOST_RING_MASK_1 0x2 +#define ATH11K_RXDMA2HOST_RING_MASK_2 0x4 + +#define ATH11K_HOST2RXDMA_RING_MASK_0 0x1 +#define ATH11K_HOST2RXDMA_RING_MASK_1 0x2 +#define ATH11K_HOST2RXDMA_RING_MASK_2 0x4 + +#define ATH11K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH11K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH11K_RX_MON_STATUS_RING_MASK_2 0x4 + +const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_TX_RING_MASK_0, + ATH11K_TX_RING_MASK_1, + ATH11K_TX_RING_MASK_2, +}; + +const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, + ATH11K_RX_MON_STATUS_RING_MASK_0, + ATH11K_RX_MON_STATUS_RING_MASK_1, + ATH11K_RX_MON_STATUS_RING_MASK_2, +}; + +const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + 0, 0, 0, 0, 0, 0, 0, + ATH11K_RX_RING_MASK_0, + ATH11K_RX_RING_MASK_1, + ATH11K_RX_RING_MASK_2, + ATH11K_RX_RING_MASK_3, +}; + +const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_ERR_RING_MASK_0, +}; + +const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RX_WBM_REL_RING_MASK_0, +}; + +const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_REO_STATUS_RING_MASK_0, +}; + +const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_RXDMA2HOST_RING_MASK_0, + ATH11K_RXDMA2HOST_RING_MASK_1, + ATH11K_RXDMA2HOST_RING_MASK_2, +}; + +const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { + ATH11K_HOST2RXDMA_RING_MASK_0, + ATH11K_HOST2RXDMA_RING_MASK_1, + ATH11K_HOST2RXDMA_RING_MASK_2, +}; + +/* enum ext_irq_num - irq numbers that can be used by external modules + * like datapath + */ +enum ext_irq_num { + host2wbm_desc_feed = 16, + host2reo_re_injection, + host2reo_command, + host2rxdma_monitor_ring3, + host2rxdma_monitor_ring2, + host2rxdma_monitor_ring1, + reo2host_exception, + wbm2host_rx_release, + reo2host_status, + reo2host_destination_ring4, + reo2host_destination_ring3, + reo2host_destination_ring2, + reo2host_destination_ring1, + rxdma2host_monitor_destination_mac3, + rxdma2host_monitor_destination_mac2, + rxdma2host_monitor_destination_mac1, + ppdu_end_interrupts_mac3, + ppdu_end_interrupts_mac2, + ppdu_end_interrupts_mac1, + rxdma2host_monitor_status_ring_mac3, + rxdma2host_monitor_status_ring_mac2, + rxdma2host_monitor_status_ring_mac1, + host2rxdma_host_buf_ring_mac3, + host2rxdma_host_buf_ring_mac2, + host2rxdma_host_buf_ring_mac1, + rxdma2host_destination_ring_mac3, + rxdma2host_destination_ring_mac2, + rxdma2host_destination_ring_mac1, + host2tcl_input_ring4, + host2tcl_input_ring3, + host2tcl_input_ring2, + host2tcl_input_ring1, + wbm2host_tx_completions_ring3, + wbm2host_tx_completions_ring2, + wbm2host_tx_completions_ring1, + tcl2host_status_ring, +}; + +static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + tasklet_kill(&ce_pipe->intr_tq); + } +} + +static void ath11k_ahb_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void __ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) +{ + struct sk_buff *skb; + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + + while ((skb = __skb_dequeue(&irq_grp->pending_q))) + dev_kfree_skb_any(skb); + } +} + +static void ath11k_ahb_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath11k_ahb_setbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val | BIT(bit)); +} + +static void ath11k_ahb_clearbit32(struct ath11k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath11k_ahb_read32(ab, offset); + ath11k_ahb_write32(ab, offset, val & ~BIT(bit)); +} + +static void ath11k_ahb_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_setbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) +{ + const struct ce_pipe_config *ce_config; + + ce_config = &target_ce_config_wlan[ce_id]; + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_OUT) + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_ADDRESS); + + if (__le32_to_cpu(ce_config->pipedir) & PIPEDIR_IN) { + ath11k_ahb_clearbit32(ab, ce_id, CE_HOST_IE_2_ADDRESS); + ath11k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + CE_HOST_IE_3_ADDRESS); + } +} + +static void ath11k_ahb_sync_ce_irqs(struct ath11k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath11k_ahb_sync_ext_irqs(struct ath11k_base *ab) +{ + int i, j; + int irq_idx; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath11k_ahb_ce_irqs_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_enable(ab, i); + } +} + +static void ath11k_ahb_ce_irqs_disable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + ath11k_ahb_ce_irq_disable(ab, i); + } +} + +int ath11k_ahb_start(struct ath11k_base *ab) +{ + ath11k_ahb_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); + + return 0; +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + napi_enable(&irq_grp->napi); + ath11k_ahb_ext_grp_enable(irq_grp); + } +} + +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) +{ + __ath11k_ahb_ext_irq_disable(ab); + ath11k_ahb_sync_ext_irqs(ab); +} + +void ath11k_ahb_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_ahb_ce_irqs_disable(ab); + ath11k_ahb_sync_ce_irqs(ab); + ath11k_ahb_kill_tasklets(ab); + del_timer_sync(&ab->rx_replenish_retry); + ath11k_ce_cleanup_pipes(ab); +} + +int ath11k_ahb_power_up(struct ath11k_base *ab) +{ + int ret; + + ret = rproc_boot(ab->tgt_rproc); + if (ret) + ath11k_err(ab, "failed to boot the remote processor Q6\n"); + + return ret; +} + +void ath11k_ahb_power_down(struct ath11k_base *ab) +{ + rproc_shutdown(ab->tgt_rproc); +} + +static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) +{ + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce_len = ARRAY_SIZE(target_ce_config_wlan) - 1; + cfg->tgt_ce = target_ce_config_wlan; + cfg->svc_to_ce_map_len = ARRAY_SIZE(target_service_to_ce_map_wlan); + cfg->svc_to_ce_map = target_service_to_ce_map_wlan; +} + +static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +{ + int i, j; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); + } +} + +static void ath11k_ahb_free_irq(struct ath11k_base *ab) +{ + int irq_idx; + int i; + + for (i = 0; i < CE_COUNT; i++) { + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); + } + + ath11k_ahb_free_ext_irq(ab); +} + +static void ath11k_ahb_ce_tasklet(unsigned long data) +{ + struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data; + + ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath11k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath11k_ahb_ce_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ce_pipe *ce_pipe = arg; + + ath11k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + + tasklet_schedule(&ce_pipe->intr_tq); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath11k_ext_irq_grp *irq_grp = container_of(napi, + struct ath11k_ext_irq_grp, + napi); + struct ath11k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath11k_ahb_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ext_irq_grp *irq_grp = arg; + + ath11k_ahb_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) +{ + int i, j; + int irq; + int ret; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + u32 num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + init_dummy_netdev(&irq_grp->napi_ndev); + netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT); + __skb_queue_head_init(&irq_grp->pending_q); + + for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { + if (ath11k_tx_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + + if (ath11k_rx_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + + if (ath11k_rx_err_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + + if (ath11k_rx_wbm_rel_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ath11k_reo_status_ring_mask[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_status; + + if (j < MAX_RADIOS) { + if (ath11k_rxdma2host_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + rxdma2host_destination_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (ath11k_host2rxdma_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + host2rxdma_host_buf_ring_mac1 + - ath11k_core_get_hw_mac_id(ab, j); + } + + if (rx_mon_status_ring_mask[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + ppdu_end_interrupts_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + irq_grp->irqs[num_irq++] = + rxdma2host_monitor_status_ring_mac1 - + ath11k_core_get_hw_mac_id(ab, j); + } + } + } + irq_grp->num_irq = num_irq; + + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; + + irq = platform_get_irq_byname(ab->pdev, + irq_name[irq_idx]); + ab->irq_num[irq_idx] = irq; + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = request_irq(irq, ath11k_ahb_ext_interrupt_handler, + IRQF_TRIGGER_RISING, + irq_name[irq_idx], irq_grp); + if (ret) { + ath11k_err(ab, "failed request_irq for %d\n", + irq); + } + } + } + + return 0; +} + +static int ath11k_ahb_config_irq(struct ath11k_base *ab) +{ + int irq, irq_idx, i; + int ret; + + /* Configure CE irqs */ + for (i = 0; i < CE_COUNT; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_IRQ_CE0_OFFSET + i; + + tasklet_init(&ce_pipe->intr_tq, ath11k_ahb_ce_tasklet, + (unsigned long)ce_pipe); + irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); + ret = request_irq(irq, ath11k_ahb_ce_interrupt_handler, + IRQF_TRIGGER_RISING, irq_name[irq_idx], + ce_pipe); + if (ret) + return ret; + + ab->irq_num[irq_idx] = irq; + } + + /* Configure external interrupts */ + ret = ath11k_ahb_ext_irq_config(ab); + + return ret; +} + +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + int i; + + for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { + entry = &target_service_to_ce_map_wlan[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + switch (__le32_to_cpu(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + break; + case PIPEDIR_OUT: + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + break; + case PIPEDIR_INOUT: + WARN_ON(dl_set); + WARN_ON(ul_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + *ul_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + ul_set = true; + break; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static int ath11k_ahb_probe(struct platform_device *pdev) +{ + struct ath11k_base *ab; + const struct of_device_id *of_id; + struct resource *mem_res; + void __iomem *mem; + int ret; + + of_id = of_match_device(ath11k_ahb_of_match, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "failed to find matching device tree id\n"); + return -EINVAL; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + dev_err(&pdev->dev, "failed to get IO memory resource\n"); + return -ENXIO; + } + + mem = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(mem)) { + dev_err(&pdev->dev, "ioremap error\n"); + return PTR_ERR(mem); + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); + return ret; + } + + ab = ath11k_core_alloc(&pdev->dev); + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; + } + + ab->pdev = pdev; + ab->hw_rev = (enum ath11k_hw_rev)of_id->data; + ab->mem = mem; + ab->mem_len = resource_size(mem_res); + platform_set_drvdata(pdev, ab); + + ret = ath11k_hal_srng_init(ab); + if (ret) + goto err_core_free; + + ret = ath11k_ce_alloc_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath11k_ahb_init_qmi_ce_config(ab); + + ret = ath11k_ahb_config_irq(ab); + if (ret) { + ath11k_err(ab, "failed to configure irq: %d\n", ret); + goto err_ce_free; + } + + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); + goto err_ce_free; + } + + return 0; + +err_ce_free: + ath11k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + +err_core_free: + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int ath11k_ahb_remove(struct platform_device *pdev) +{ + struct ath11k_base *ab = platform_get_drvdata(pdev); + + reinit_completion(&ab->driver_recovery); + + if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags)) + wait_for_completion_timeout(&ab->driver_recovery, + ATH11K_AHB_RECOVERY_TIMEOUT); + + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + + ath11k_core_deinit(ab); + ath11k_ahb_free_irq(ab); + + ath11k_hal_srng_deinit(ab); + ath11k_ce_free_pipes(ab); + ath11k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ath11k_ahb_driver = { + .driver = { + .name = "ath11k", + .of_match_table = ath11k_ahb_of_match, + }, + .probe = ath11k_ahb_probe, + .remove = ath11k_ahb_remove, +}; + +int ath11k_ahb_init(void) +{ + return platform_driver_register(&ath11k_ahb_driver); +} + +void ath11k_ahb_exit(void) +{ + platform_driver_unregister(&ath11k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath11k/ahb.h b/drivers/net/wireless/ath/ath11k/ahb.h new file mode 100644 index 000000000000..93f46dfe22df --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ahb.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ +#ifndef ATH11K_AHB_H +#define ATH11K_AHB_H + +#include "core.h" + +#define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ) +struct ath11k_base; + +static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) +{ + return ioread32(ab->mem + offset); +} + +static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) +{ + iowrite32(value, ab->mem + offset); +} + +void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab); +void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab); +int ath11k_ahb_start(struct ath11k_base *ab); +void ath11k_ahb_stop(struct ath11k_base *ab); +int ath11k_ahb_power_up(struct ath11k_base *ab); +void ath11k_ahb_power_down(struct ath11k_base *ab); +int ath11k_ahb_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe); + +int ath11k_ahb_init(void); +void ath11k_ahb_exit(void); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c new file mode 100644 index 000000000000..cdd40c8fc867 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -0,0 +1,808 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "dp_rx.h" +#include "debug.h" + +static const struct ce_attr host_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE9: host->target WMI (mac2) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE10: target->host HTT */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE11: Not used */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + +static int ath11k_ce_rx_buf_enqueue_pipe(struct ath11k_ce_pipe *pipe, + struct sk_buff *skb, dma_addr_t paddr) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct hal_srng *srng; + unsigned int write_index; + unsigned int nentries_mask = ring->nentries_mask; + u32 *desc; + int ret; + + lockdep_assert_held(&ab->ce.ce_lock); + + write_index = ring->write_index; + + srng = &ab->hal.srng_list[ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ret = -ENOSPC; + goto exit; + } + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) { + ret = -ENOSPC; + goto exit; + } + + ath11k_hal_ce_dst_set_desc(desc, paddr); + + ring->skb[write_index] = skb; + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ring->write_index = write_index; + + pipe->rx_buf_needed--; + + ret = 0; +exit: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return ret; +} + +static int ath11k_ce_rx_post_pipe(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + dma_addr_t paddr; + int ret = 0; + + if (!(pipe->dest_ring || pipe->status_ring)) + return 0; + + spin_lock_bh(&ab->ce.ce_lock); + while (pipe->rx_buf_needed) { + skb = dev_alloc_skb(pipe->buf_sz); + if (!skb) { + ret = -ENOMEM; + goto exit; + } + + WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4)); + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, paddr))) { + ath11k_warn(ab, "failed to dma map ce rx buf\n"); + dev_kfree_skb_any(skb); + ret = -EIO; + goto exit; + } + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + ret = ath11k_ce_rx_buf_enqueue_pipe(pipe, skb, paddr); + + if (ret) { + ath11k_warn(ab, "failed to enqueue rx buf: %d\n", ret); + dma_unmap_single(ab->dev, paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + goto exit; + } + } + +exit: + spin_unlock_bh(&ab->ce.ce_lock); + return ret; +} + +static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe, + struct sk_buff **skb, int *nbytes) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + u32 *desc; + int ret = 0; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->dest_ring->sw_index; + nentries_mask = pipe->dest_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->status_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_dst_get_next_entry(ab, srng); + if (!desc) { + ret = -EIO; + goto err; + } + + *nbytes = ath11k_hal_ce_dst_status_get_length(desc); + if (*nbytes == 0) { + ret = -EIO; + goto err; + } + + *skb = pipe->dest_ring->skb[sw_index]; + pipe->dest_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->dest_ring->sw_index = sw_index; + + pipe->rx_buf_needed++; +err: + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + struct sk_buff_head list; + unsigned int nbytes, max_nbytes; + int ret; + + __skb_queue_head_init(&list); + while (ath11k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) { + max_nbytes = skb->len + skb_tailroom(skb); + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + max_nbytes, DMA_FROM_DEVICE); + + if (unlikely(max_nbytes < nbytes)) { + ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + nbytes, max_nbytes); + dev_kfree_skb_any(skb); + continue; + } + + skb_put(skb, nbytes); + __skb_queue_tail(&list, skb); + } + + while ((skb = __skb_dequeue(&list))) { + ath11k_dbg(ab, ATH11K_DBG_AHB, "rx ce pipe %d len %d\n", + pipe->pipe_num, skb->len); + pipe->recv_cb(ab, skb); + } + + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret && ret != -ENOSPC) { + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + pipe->pipe_num, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + } +} + +static struct sk_buff *ath11k_ce_completed_send_next(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct hal_srng *srng; + unsigned int sw_index; + unsigned int nentries_mask; + struct sk_buff *skb; + u32 *desc; + + spin_lock_bh(&ab->ce.ce_lock); + + sw_index = pipe->src_ring->sw_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + desc = ath11k_hal_srng_src_reap_next(ab, srng); + if (!desc) { + skb = ERR_PTR(-EIO); + goto err_unlock; + } + + skb = pipe->src_ring->skb[sw_index]; + + pipe->src_ring->skb[sw_index] = NULL; + + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + pipe->src_ring->sw_index = sw_index; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return skb; +} + +static void ath11k_ce_send_done_cb(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct sk_buff *skb; + + while (!IS_ERR(skb = ath11k_ce_completed_send_next(pipe))) { + if (!skb) + continue; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } +} + +static int ath11k_ce_init_ring(struct ath11k_base *ab, + struct ath11k_ce_ring *ce_ring, + int ce_id, enum hal_ring_type type) +{ + struct hal_srng_params params = { 0 }; + int ret; + + params.ring_base_paddr = ce_ring->base_addr_ce_space; + params.ring_base_vaddr = ce_ring->base_addr_owner_space; + params.num_entries = ce_ring->nentries; + + switch (type) { + case HAL_CE_SRC: + if (!(CE_ATTR_DIS_INTR & host_ce_config_wlan[ce_id].flags)) + params.intr_batch_cntr_thres_entries = 1; + break; + case HAL_CE_DST: + params.max_buffer_len = host_ce_config_wlan[ce_id].src_sz_max; + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_timer_thres_us = 1024; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.low_threshold = ce_ring->nentries - 3; + } + break; + case HAL_CE_DST_STATUS: + if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = 0x1000; + } + break; + default: + ath11k_warn(ab, "Invalid CE ring type %d\n", type); + return -EINVAL; + } + + /* TODO: Init other params needed by HAL to init the ring */ + + ret = ath11k_hal_srng_setup(ab, type, ce_id, 0, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ce_id); + return ret; + } + ce_ring->hal_ring_id = ret; + + return 0; +} + +static struct ath11k_ce_ring * +ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz) +{ + struct ath11k_ce_ring *ce_ring; + dma_addr_t base_addr; + + ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL); + if (ce_ring == NULL) + return ERR_PTR(-ENOMEM); + + ce_ring->nentries = nentries; + ce_ring->nentries_mask = nentries - 1; + + /* Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + ce_ring->base_addr_owner_space_unaligned = + dma_alloc_coherent(ab->dev, + nentries * desc_sz + CE_DESC_RING_ALIGN, + &base_addr, GFP_KERNEL); + if (!ce_ring->base_addr_owner_space_unaligned) { + kfree(ce_ring); + return ERR_PTR(-ENOMEM); + } + + ce_ring->base_addr_ce_space_unaligned = base_addr; + + ce_ring->base_addr_owner_space = PTR_ALIGN( + ce_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + ce_ring->base_addr_ce_space = ALIGN( + ce_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + return ce_ring; +} + +static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + const struct ce_attr *attr = &host_ce_config_wlan[ce_id]; + struct ath11k_ce_ring *ring; + int nentries; + int desc_sz; + + pipe->attr_flags = attr->flags; + + if (attr->src_nentries) { + pipe->send_cb = ath11k_ce_send_done_cb; + nentries = roundup_pow_of_two(attr->src_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->src_ring = ring; + } + + if (attr->dest_nentries) { + pipe->recv_cb = attr->recv_cb; + nentries = roundup_pow_of_two(attr->dest_nentries); + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->dest_ring = ring; + + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + ring = ath11k_ce_alloc_ring(ab, nentries, desc_sz); + if (IS_ERR(ring)) + return PTR_ERR(ring); + pipe->status_ring = ring; + } + + return 0; +} + +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; + + if (pipe->send_cb) + pipe->send_cb(pipe); + + if (pipe->recv_cb) + ath11k_ce_recv_process_cb(pipe); +} + +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + + if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb) + pipe->send_cb(pipe); +} + +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id) +{ + struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id]; + struct hal_srng *srng; + u32 *desc; + unsigned int write_index, sw_index; + unsigned int nentries_mask; + int ret = 0; + u8 byte_swap_data = 0; + int num_used; + + /* Check if some entries could be regained by handling tx completion if + * the CE has interrupts disabled and the used entries is more than the + * defined usage threshold. + */ + if (pipe->attr_flags & CE_ATTR_DIS_INTR) { + spin_lock_bh(&ab->ce.ce_lock); + write_index = pipe->src_ring->write_index; + + sw_index = pipe->src_ring->sw_index; + + if (write_index >= sw_index) + num_used = write_index - sw_index; + else + num_used = pipe->src_ring->nentries - sw_index + + write_index; + + spin_unlock_bh(&ab->ce.ce_lock); + + if (num_used > ATH11K_CE_USAGE_THRESHOLD) + ath11k_ce_poll_send_completed(ab, pipe->pipe_num); + } + + if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + return -ESHUTDOWN; + + spin_lock_bh(&ab->ce.ce_lock); + + write_index = pipe->src_ring->write_index; + nentries_mask = pipe->src_ring->nentries_mask; + + srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + if (unlikely(ath11k_hal_srng_src_num_free(ab, srng, false) < 1)) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + desc = ath11k_hal_srng_src_get_next_reaped(ab, srng); + if (!desc) { + ath11k_hal_srng_access_end(ab, srng); + ret = -ENOBUFS; + goto err_unlock; + } + + if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA) + byte_swap_data = 1; + + ath11k_hal_ce_src_set_desc(desc, ATH11K_SKB_CB(skb)->paddr, + skb->len, transfer_id, byte_swap_data); + + pipe->src_ring->skb[write_index] = skb; + pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask, + write_index); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return 0; + +err_unlock: + spin_unlock_bh(&srng->lock); + + spin_unlock_bh(&ab->ce.ce_lock); + + return ret; +} + +static void ath11k_ce_rx_pipe_cleanup(struct ath11k_ce_pipe *pipe) +{ + struct ath11k_base *ab = pipe->ab; + struct ath11k_ce_ring *ring = pipe->dest_ring; + struct sk_buff *skb; + int i; + + if (!(ring && pipe->buf_sz)) + return; + + for (i = 0; i < ring->nentries; i++) { + skb = ring->skb[i]; + if (!skb) + continue; + + ring->skb[i] = NULL; + dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } +} + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int pipe_num; + + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + pipe = &ab->ce.ce_pipe[pipe_num]; + ath11k_ce_rx_pipe_cleanup(pipe); + + /* Cleanup any src CE's which have interrupts disabled */ + ath11k_ce_poll_send_completed(ab, pipe_num); + + /* NOTE: Should we also clean up tx buffer in all pipes? */ + } +} + +void ath11k_ce_rx_post_buf(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + ret = ath11k_ce_rx_post_pipe(pipe); + if (ret) { + if (ret == -ENOSPC) + continue; + + ath11k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n", + i, ret); + mod_timer(&ab->rx_replenish_retry, + jiffies + ATH11K_CE_RX_POST_RETRY_JIFFIES); + + return; + } + } +} + +void ath11k_ce_rx_replenish_retry(struct timer_list *t) +{ + struct ath11k_base *ab = from_timer(ab, t, rx_replenish_retry); + + ath11k_ce_rx_post_buf(ab); +} + +int ath11k_ce_init_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + ret = ath11k_ce_init_ring(ab, pipe->src_ring, i, + HAL_CE_SRC); + if (ret) { + ath11k_warn(ab, "failed to init src ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->src_ring->write_index = 0; + pipe->src_ring->sw_index = 0; + } + + if (pipe->dest_ring) { + ret = ath11k_ce_init_ring(ab, pipe->dest_ring, i, + HAL_CE_DST); + if (ret) { + ath11k_warn(ab, "failed to init dest ring: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->rx_buf_needed = pipe->dest_ring->nentries ? + pipe->dest_ring->nentries - 2 : 0; + + pipe->dest_ring->write_index = 0; + pipe->dest_ring->sw_index = 0; + } + + if (pipe->status_ring) { + ret = ath11k_ce_init_ring(ab, pipe->status_ring, i, + HAL_CE_DST_STATUS); + if (ret) { + ath11k_warn(ab, "failed to init dest status ing: %d\n", + ret); + /* Should we clear any partial init */ + return ret; + } + + pipe->status_ring->write_index = 0; + pipe->status_ring->sw_index = 0; + } + } + + return 0; +} + +void ath11k_ce_free_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int desc_sz; + int i; + + for (i = 0; i < CE_COUNT; i++) { + pipe = &ab->ce.ce_pipe[i]; + + if (pipe->src_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_SRC); + dma_free_coherent(ab->dev, + pipe->src_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->src_ring->base_addr_owner_space, + pipe->src_ring->base_addr_ce_space); + kfree(pipe->src_ring); + pipe->src_ring = NULL; + } + + if (pipe->dest_ring) { + desc_sz = ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST); + dma_free_coherent(ab->dev, + pipe->dest_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->dest_ring->base_addr_owner_space, + pipe->dest_ring->base_addr_ce_space); + kfree(pipe->dest_ring); + pipe->dest_ring = NULL; + } + + if (pipe->status_ring) { + desc_sz = + ath11k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS); + dma_free_coherent(ab->dev, + pipe->status_ring->nentries * desc_sz + + CE_DESC_RING_ALIGN, + pipe->status_ring->base_addr_owner_space, + pipe->status_ring->base_addr_ce_space); + kfree(pipe->status_ring); + pipe->status_ring = NULL; + } + } +} + +int ath11k_ce_alloc_pipes(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *pipe; + int i; + int ret; + const struct ce_attr *attr; + + spin_lock_init(&ab->ce.ce_lock); + + for (i = 0; i < CE_COUNT; i++) { + attr = &host_ce_config_wlan[i]; + pipe = &ab->ce.ce_pipe[i]; + pipe->pipe_num = i; + pipe->ab = ab; + pipe->buf_sz = attr->src_sz_max; + + ret = ath11k_ce_alloc_pipe(ab, i); + if (ret) { + /* Free any parial successful allocation */ + ath11k_ce_free_pipes(ab); + return ret; + } + } + + return 0; +} + +/* For Big Endian Host, Copy Engine byte_swap is enabled + * When Copy Engine does byte_swap, need to byte swap again for the + * Host to get/put buffer content in the correct byte order + */ +void ath11k_ce_byte_swap(void *mem, u32 len) +{ + int i; + + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + if (!mem) + return; + + for (i = 0; i < (len / 4); i++) { + *(u32 *)mem = swab32(*(u32 *)mem); + mem += 4; + } + } +} + +int ath11k_ce_get_attr_flags(int ce_id) +{ + if (ce_id >= CE_COUNT) + return -EINVAL; + + return host_ce_config_wlan[ce_id].flags; +} diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h new file mode 100644 index 000000000000..e355dfda48bf --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CE_H +#define ATH11K_CE_H + +#define CE_COUNT 12 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Host software's Copy Engine configuration. */ +#ifdef __BIG_ENDIAN +#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA +#else +#define CE_ATTR_FLAGS 0 +#endif + +/* Threshold to poll for tx completion in case of Interrupt disabled CE's */ +#define ATH11K_CE_USAGE_THRESHOLD 32 + +void ath11k_ce_byte_swap(void *mem, u32 len); + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ +#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ + +/* CE address/mask */ +#define CE_HOST_IE_ADDRESS 0x00A1803C +#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS + +#define CE_HOST_IE_3_SHIFT 0xC + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +#define ATH11K_CE_RX_POST_RETRY_JIFFIES 50 + +struct ath11k_base; + +/* + * Establish a mapping between a service/direction and a pipe. + * Configuration information for a Copy Engine pipe and services. + * Passed from Host to Target through QMI message and must be in + * little endian format. + */ +struct service_to_pipe { + __le32 service_id; + __le32 pipedir; + __le32 pipenum; +}; + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target through QMI message during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + __le32 pipenum; + __le32 pipedir; + __le32 nentries; + __le32 nbytes_max; + __le32 flags; + __le32 reserved; +}; + +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); +}; + +#define CE_DESC_RING_ALIGN 8 + +struct ath11k_ce_ring { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + void *base_addr_owner_space_unaligned; + /* CE address space */ + u32 base_addr_ce_space_unaligned; + + /* Actual start of descriptors. + * Aligned to descriptor-size boundary. + * Points into reserved DMA-coherent area, above. + */ + /* Host address space */ + void *base_addr_owner_space; + + /* CE address space */ + u32 base_addr_ce_space; + + /* HAL ring id */ + u32 hal_ring_id; + + /* keep last */ + struct sk_buff *skb[0]; +}; + +struct ath11k_ce_pipe { + struct ath11k_base *ab; + u16 pipe_num; + unsigned int attr_flags; + unsigned int buf_sz; + unsigned int rx_buf_needed; + + void (*send_cb)(struct ath11k_ce_pipe *); + void (*recv_cb)(struct ath11k_base *, struct sk_buff *); + + struct tasklet_struct intr_tq; + struct ath11k_ce_ring *src_ring; + struct ath11k_ce_ring *dest_ring; + struct ath11k_ce_ring *status_ring; +}; + +struct ath11k_ce { + struct ath11k_ce_pipe ce_pipe[CE_COUNT]; + /* Protects rings of all ce pipes */ + spinlock_t ce_lock; +}; + +void ath11k_ce_cleanup_pipes(struct ath11k_base *ab); +void ath11k_ce_rx_replenish_retry(struct timer_list *t); +void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id); +int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, + u16 transfer_id); +void ath11k_ce_rx_post_buf(struct ath11k_base *ab); +int ath11k_ce_init_pipes(struct ath11k_base *ab); +int ath11k_ce_alloc_pipes(struct ath11k_base *ab); +void ath11k_ce_free_pipes(struct ath11k_base *ab); +int ath11k_ce_get_attr_flags(int ce_id); +void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id); +#endif diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c new file mode 100644 index 000000000000..9e823056e673 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -0,0 +1,795 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/remoteproc.h> +#include <linux/firmware.h> +#include "ahb.h" +#include "core.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "debug.h" + +unsigned int ath11k_debug_mask; +module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); + +static const struct ath11k_hw_params ath11k_hw_params = { + .name = "ipq8074", + .fw = { + .dir = IPQ8074_FW_DIR, + .board_size = IPQ8074_MAX_BOARD_DATA_SZ, + .cal_size = IPQ8074_MAX_CAL_DATA_SZ, + }, +}; + +/* Map from pdev index to hw mac index */ +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx) +{ + switch (pdev_idx) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 1; + default: + ath11k_warn(ab, "Invalid pdev idx %d\n", pdev_idx); + return ATH11K_INVALID_HW_MAC_ID; + } +} + +static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, + size_t name_len) +{ + /* Note: bus is fixed to ahb. When other bus type supported, + * make it to dynamic. + */ + scnprintf(name, name_len, + "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d", + ab->qmi.target.chip_id, + ab->qmi.target.board_id); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name); + + return 0; +} + +static const struct firmware *ath11k_fetch_fw_file(struct ath11k_base *ab, + const char *dir, + const char *file) +{ + char filename[100]; + const struct firmware *fw; + int ret; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = firmware_request_nowarn(&fw, filename, ab->dev); + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot fw request '%s': %d\n", + filename, ret); + + if (ret) + return ERR_PTR(ret); + ath11k_warn(ab, "Downloading BDF: %s, size: %zu\n", + filename, fw->size); + + return fw; +} + +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + if (!IS_ERR(bd->fw)) + release_firmware(bd->fw); + + memset(bd, 0, sizeof(*bd)); +} + +static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const void *buf, size_t buf_len, + const char *boardname, + int bd_ie_type) +{ + const struct ath11k_fw_ie *hdr; + bool name_match_found; + int ret, board_ie_id; + size_t board_ie_len; + const void *board_ie_data; + + name_match_found = false; + + /* go through ATH11K_BD_IE_BOARD_ elements */ + while (buf_len > sizeof(struct ath11k_fw_ie)) { + hdr = buf; + board_ie_id = le32_to_cpu(hdr->id); + board_ie_len = le32_to_cpu(hdr->len); + board_ie_data = hdr->data; + + buf_len -= sizeof(*hdr); + buf += sizeof(*hdr); + + if (buf_len < ALIGN(board_ie_len, 4)) { + ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n", + buf_len, ALIGN(board_ie_len, 4)); + ret = -EINVAL; + goto out; + } + + switch (board_ie_id) { + case ATH11K_BD_IE_BOARD_NAME: + ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "", + board_ie_data, board_ie_len); + + if (board_ie_len != strlen(boardname)) + break; + + ret = memcmp(board_ie_data, boardname, strlen(boardname)); + if (ret) + break; + + name_match_found = true; + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found match for name '%s'", + boardname); + break; + case ATH11K_BD_IE_BOARD_DATA: + if (!name_match_found) + /* no match found */ + break; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, + "boot found board data for '%s'", boardname); + + bd->data = board_ie_data; + bd->len = board_ie_len; + + ret = 0; + goto out; + default: + ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n", + board_ie_id); + break; + } + + /* jump over the padding */ + board_ie_len = ALIGN(board_ie_len, 4); + + buf_len -= board_ie_len; + buf += board_ie_len; + } + + /* no match found */ + ret = -ENOENT; + +out: + return ret; +} + +static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, + struct ath11k_board_data *bd, + const char *boardname) +{ + size_t len, magic_len; + const u8 *data; + char *filename = ATH11K_BOARD_API2_FILE; + size_t ie_len; + struct ath11k_fw_ie *hdr; + int ret, ie_id; + + if (!bd->fw) + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + filename); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + data = bd->fw->data; + len = bd->fw->size; + + /* magic has extra null byte padded */ + magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; + if (len < magic_len) { + ath11k_err(ab, "failed to find magic value in %s/%s, file too short: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH11K_BOARD_MAGIC, magic_len)) { + ath11k_err(ab, "found invalid board magic\n"); + ret = -EINVAL; + goto err; + } + + /* magic is padded to 4 bytes */ + magic_len = ALIGN(magic_len, 4); + if (len < magic_len) { + ath11k_err(ab, "failed: %s/%s too small to contain board data, len: %zu\n", + ab->hw_params.fw.dir, filename, len); + ret = -EINVAL; + goto err; + } + + data += magic_len; + len -= magic_len; + + while (len > sizeof(struct ath11k_fw_ie)) { + hdr = (struct ath11k_fw_ie *)data; + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data = hdr->data; + + if (len < ALIGN(ie_len, 4)) { + ath11k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n", + ie_id, ie_len, len); + return -EINVAL; + } + + switch (ie_id) { + case ATH11K_BD_IE_BOARD: + ret = ath11k_core_parse_bd_ie_board(ab, bd, data, + ie_len, + boardname, + ATH11K_BD_IE_BOARD); + if (ret == -ENOENT) + /* no match found, continue */ + break; + else if (ret) + /* there was an error, bail out */ + goto err; + /* either found or error, so stop searching */ + goto out; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + +out: + if (!bd->data || !bd->len) { + ath11k_err(ab, + "failed to fetch board data for %s from %s/%s\n", + boardname, ab->hw_params.fw.dir, filename); + ret = -ENODATA; + goto err; + } + + return 0; + +err: + ath11k_core_free_bdf(ab, bd); + return ret; +} + +static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, + struct ath11k_board_data *bd) +{ + bd->fw = ath11k_fetch_fw_file(ab, + ab->hw_params.fw.dir, + ATH11K_DEFAULT_BOARD_FILE); + if (IS_ERR(bd->fw)) + return PTR_ERR(bd->fw); + + bd->data = bd->fw->data; + bd->len = bd->fw->size; + + return 0; +} + +#define BOARD_NAME_SIZE 100 +int ath11k_core_fetch_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd) +{ + char boardname[BOARD_NAME_SIZE]; + int ret; + + ret = ath11k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE); + if (ret) { + ath11k_err(ab, "failed to create board name: %d", ret); + return ret; + } + + ab->bd_api = 2; + ret = ath11k_core_fetch_board_data_api_n(ab, bd, boardname); + if (!ret) + goto success; + + ab->bd_api = 1; + ret = ath11k_core_fetch_board_data_api_1(ab, bd); + if (ret) { + ath11k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n", + ab->hw_params.fw.dir); + return ret; + } + +success: + ath11k_dbg(ab, ATH11K_DBG_BOOT, "using board api %d\n", ab->bd_api); + return 0; +} + +static void ath11k_core_stop(struct ath11k_base *ab) +{ + if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath11k_qmi_firmware_stop(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); + + /* De-Init of components as needed */ +} + +static int ath11k_core_soc_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_qmi_init_service(ab); + if (ret) { + ath11k_err(ab, "failed to initialize qmi :%d\n", ret); + return ret; + } + + ret = ath11k_debug_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create ath11k debugfs\n"); + goto err_qmi_deinit; + } + + ret = ath11k_ahb_power_up(ab); + if (ret) { + ath11k_err(ab, "failed to power up :%d\n", ret); + goto err_debugfs_reg; + } + + return 0; + +err_debugfs_reg: + ath11k_debug_soc_destroy(ab); +err_qmi_deinit: + ath11k_qmi_deinit_service(ab); + return ret; +} + +static void ath11k_core_soc_destroy(struct ath11k_base *ab) +{ + ath11k_debug_soc_destroy(ab); + ath11k_dp_free(ab); + ath11k_reg_free(ab); + ath11k_qmi_deinit_service(ab); +} + +static int ath11k_core_pdev_create(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_debug_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create core pdev debugfs: %d\n", ret); + return ret; + } + + ret = ath11k_mac_register(ab); + if (ret) { + ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); + goto err_pdev_debug; + } + + ret = ath11k_dp_pdev_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to attach DP pdev: %d\n", ret); + goto err_mac_unregister; + } + + return 0; + +err_mac_unregister: + ath11k_mac_unregister(ab); + +err_pdev_debug: + ath11k_debug_pdev_destroy(ab); + + return ret; +} + +static void ath11k_core_pdev_destroy(struct ath11k_base *ab) +{ + ath11k_mac_unregister(ab); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_debug_pdev_destroy(ab); +} + +static int ath11k_core_start(struct ath11k_base *ab, + enum ath11k_firmware_mode mode) +{ + int ret; + + ret = ath11k_qmi_firmware_start(ab, mode); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + return ret; + } + + ret = ath11k_wmi_attach(ab); + if (ret) { + ath11k_err(ab, "failed to attach wmi: %d\n", ret); + goto err_firmware_stop; + } + + ret = ath11k_htc_init(ab); + if (ret) { + ath11k_err(ab, "failed to init htc: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_ahb_start(ab); + if (ret) { + ath11k_err(ab, "failed to start HIF: %d\n", ret); + goto err_wmi_detach; + } + + ret = ath11k_htc_wait_target(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to connect to HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_dp_htt_connect(&ab->dp); + if (ret) { + ath11k_err(ab, "failed to connect to HTT: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_connect(ab); + if (ret) { + ath11k_err(ab, "failed to connect wmi: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_htc_start(&ab->htc); + if (ret) { + ath11k_err(ab, "failed to start HTC: %d\n", ret); + goto err_hif_stop; + } + + ret = ath11k_wmi_wait_for_service_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi service ready event: %d\n", + ret); + goto err_hif_stop; + } + + ret = ath11k_mac_allocate(ab); + if (ret) { + ath11k_err(ab, "failed to create new hw device with mac80211 :%d\n", + ret); + goto err_hif_stop; + } + + ath11k_dp_pdev_pre_alloc(ab); + + ret = ath11k_dp_pdev_reo_setup(ab); + if (ret) { + ath11k_err(ab, "failed to initialize reo destination rings: %d\n", ret); + goto err_mac_destroy; + } + + ret = ath11k_wmi_cmd_init(ab); + if (ret) { + ath11k_err(ab, "failed to send wmi init cmd: %d\n", ret); + goto err_reo_cleanup; + } + + ret = ath11k_wmi_wait_for_unified_ready(ab); + if (ret) { + ath11k_err(ab, "failed to receive wmi unified ready event: %d\n", + ret); + goto err_reo_cleanup; + } + + ret = ath11k_dp_tx_htt_h2t_ver_req_msg(ab); + if (ret) { + ath11k_err(ab, "failed to send htt version request message: %d\n", + ret); + goto err_reo_cleanup; + } + + return 0; + +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); +err_mac_destroy: + ath11k_mac_destroy(ab); +err_hif_stop: + ath11k_ahb_stop(ab); +err_wmi_detach: + ath11k_wmi_detach(ab); +err_firmware_stop: + ath11k_qmi_firmware_stop(ab); + + return ret; +} + +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_ce_init_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to initialize CE: %d\n", ret); + return ret; + } + + ret = ath11k_dp_alloc(ab); + if (ret) { + ath11k_err(ab, "failed to init DP: %d\n", ret); + return ret; + } + + mutex_lock(&ab->core_lock); + ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL); + if (ret) { + ath11k_err(ab, "failed to start core: %d\n", ret); + goto err_dp_free; + } + + ret = ath11k_core_pdev_create(ab); + if (ret) { + ath11k_err(ab, "failed to create pdev core: %d\n", ret); + goto err_core_stop; + } + ath11k_ahb_ext_irq_enable(ab); + mutex_unlock(&ab->core_lock); + + return 0; + +err_core_stop: + ath11k_core_stop(ab); + ath11k_mac_destroy(ab); +err_dp_free: + ath11k_dp_free(ab); + mutex_unlock(&ab->core_lock); + return ret; +} + +static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) +{ + int ret; + + mutex_lock(&ab->core_lock); + ath11k_ahb_ext_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_ahb_stop(ab); + ath11k_wmi_detach(ab); + ath11k_dp_pdev_reo_cleanup(ab); + mutex_unlock(&ab->core_lock); + + ath11k_dp_free(ab); + ath11k_hal_srng_deinit(ab); + + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1; + + ret = ath11k_hal_srng_init(ab); + if (ret) + return ret; + + clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) + goto err_hal_srng_deinit; + + clear_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + + return 0; + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + return ret; +} + +void ath11k_core_halt(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ar->conf_mutex); + + ar->num_created_vdevs = 0; + + ath11k_mac_scan_finish(ar); + ath11k_mac_peer_cleanup_all(ar); + cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->regd_update_work); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); + synchronize_rcu(); + INIT_LIST_HEAD(&ar->arvifs); + idr_init(&ar->txmgmt_idr); +} + +static void ath11k_core_restart(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); + struct ath11k *ar; + struct ath11k_pdev *pdev; + int i, ret = 0; + + spin_lock_bh(&ab->base_lock); + ab->stats.fw_crash_counter++; + spin_unlock_bh(&ab->base_lock); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + ieee80211_stop_queues(ar->hw); + ath11k_mac_drain_tx(ar); + complete(&ar->scan.started); + complete(&ar->scan.completed); + complete(&ar->peer_assoc_done); + complete(&ar->install_key_done); + complete(&ar->vdev_setup_done); + complete(&ar->bss_survey_done); + + wake_up(&ar->dp.tx_empty_waitq); + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + } + + wake_up(&ab->wmi_ab.tx_credits_wq); + wake_up(&ab->peer_mapping_wq); + + ret = ath11k_core_reconfigure_on_crash(ab); + if (ret) { + ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); + return; + } + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (!ar || ar->state == ATH11K_STATE_OFF) + continue; + + mutex_lock(&ar->conf_mutex); + + switch (ar->state) { + case ATH11K_STATE_ON: + ar->state = ATH11K_STATE_RESTARTING; + ath11k_core_halt(ar); + ieee80211_restart_hw(ar->hw); + break; + case ATH11K_STATE_OFF: + ath11k_warn(ab, + "cannot restart radio %d that hasn't been started\n", + i); + break; + case ATH11K_STATE_RESTARTING: + break; + case ATH11K_STATE_RESTARTED: + ar->state = ATH11K_STATE_WEDGED; + /* fall through */ + case ATH11K_STATE_WEDGED: + ath11k_warn(ab, + "device is wedged, will not restart radio %d\n", i); + break; + } + mutex_unlock(&ar->conf_mutex); + } + complete(&ab->driver_recovery); +} + +int ath11k_core_init(struct ath11k_base *ab) +{ + struct device *dev = ab->dev; + struct rproc *prproc; + phandle rproc_phandle; + int ret; + + if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { + ath11k_err(ab, "failed to get q6_rproc handle\n"); + return -ENOENT; + } + + prproc = rproc_get_by_phandle(rproc_phandle); + if (!prproc) { + ath11k_err(ab, "failed to get rproc\n"); + return -EINVAL; + } + ab->tgt_rproc = prproc; + ab->hw_params = ath11k_hw_params; + + ret = ath11k_core_soc_create(ab); + if (ret) { + ath11k_err(ab, "failed to create soc core: %d\n", ret); + return ret; + } + + return 0; +} + +void ath11k_core_deinit(struct ath11k_base *ab) +{ + mutex_lock(&ab->core_lock); + + ath11k_core_pdev_destroy(ab); + ath11k_core_stop(ab); + + mutex_unlock(&ab->core_lock); + + ath11k_ahb_power_down(ab); + ath11k_mac_destroy(ab); + ath11k_core_soc_destroy(ab); +} + +void ath11k_core_free(struct ath11k_base *ab) +{ + kfree(ab); +} + +struct ath11k_base *ath11k_core_alloc(struct device *dev) +{ + struct ath11k_base *ab; + + ab = kzalloc(sizeof(*ab), GFP_KERNEL); + if (!ab) + return NULL; + + init_completion(&ab->driver_recovery); + + ab->workqueue = create_singlethread_workqueue("ath11k_wq"); + if (!ab->workqueue) + goto err_sc_free; + + mutex_init(&ab->core_lock); + spin_lock_init(&ab->base_lock); + + INIT_LIST_HEAD(&ab->peers); + init_waitqueue_head(&ab->peer_mapping_wq); + init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + ab->dev = dev; + + return ab; + +err_sc_free: + kfree(ab); + return NULL; +} + +static int __init ath11k_init(void) +{ + int ret; + + ret = ath11k_ahb_init(); + if (ret) + printk(KERN_ERR "failed to register ath11k ahb driver: %d\n", + ret); + return ret; +} +module_init(ath11k_init); + +static void __exit ath11k_exit(void) +{ + ath11k_ahb_exit(); +} +module_exit(ath11k_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax wireless chip"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h new file mode 100644 index 000000000000..25cdcf71d0c4 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -0,0 +1,826 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_CORE_H +#define ATH11K_CORE_H + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/bitfield.h> +#include "qmi.h" +#include "htc.h" +#include "wmi.h" +#include "hal.h" +#include "dp.h" +#include "ce.h" +#include "mac.h" +#include "hw.h" +#include "hal_rx.h" +#include "reg.h" + +#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +#define ATH11K_TX_MGMT_NUM_PENDING_MAX 512 + +#define ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI 64 + +/* Pending management packets threshold for dropping probe responses */ +#define ATH11K_PRB_RSP_DROP_THRESHOLD ((ATH11K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4) + +#define ATH11K_INVALID_HW_MAC_ID 0xFF + +enum ath11k_supported_bw { + ATH11K_BW_20 = 0, + ATH11K_BW_40 = 1, + ATH11K_BW_80 = 2, + ATH11K_BW_160 = 3, +}; + +enum wme_ac { + WME_AC_BE, + WME_AC_BK, + WME_AC_VI, + WME_AC_VO, + WME_NUM_AC +}; + +#define ATH11K_HT_MCS_MAX 7 +#define ATH11K_VHT_MCS_MAX 9 +#define ATH11K_HE_MCS_MAX 11 + +static inline enum wme_ac ath11k_tid_to_ac(u32 tid) +{ + return (((tid == 0) || (tid == 3)) ? WME_AC_BE : + ((tid == 1) || (tid == 2)) ? WME_AC_BK : + ((tid == 4) || (tid == 5)) ? WME_AC_VI : + WME_AC_VO); +} + +struct ath11k_skb_cb { + dma_addr_t paddr; + u8 eid; + struct ath11k *ar; + struct ieee80211_vif *vif; +} __packed; + +struct ath11k_skb_rxcb { + dma_addr_t paddr; + bool is_first_msdu; + bool is_last_msdu; + bool is_continuation; + struct hal_rx_desc *rx_desc; + u8 err_rel_src; + u8 err_code; + u8 mac_id; + u8 unmapped; +}; + +enum ath11k_hw_rev { + ATH11K_HW_IPQ8074, +}; + +enum ath11k_firmware_mode { + /* the default mode, standard 802.11 functionality */ + ATH11K_FIRMWARE_MODE_NORMAL, + + /* factory tests etc */ + ATH11K_FIRMWARE_MODE_FTM, +}; + +#define ATH11K_IRQ_NUM_MAX 52 +#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11 +#define ATH11K_EXT_IRQ_NUM_MAX 16 + +extern const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +extern const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + +struct ath11k_ext_irq_grp { + struct ath11k_base *ab; + u32 irqs[ATH11K_EXT_IRQ_NUM_MAX]; + u32 num_irq; + u32 grp_id; + struct napi_struct napi; + struct net_device napi_ndev; + /* Queue of pending packets, not expected to be accessed concurrently + * to avoid locking overhead. + */ + struct sk_buff_head pending_q; +}; + +#define HEHANDLE_CAP_PHYINFO_SIZE 3 +#define HECAP_PHYINFO_SIZE 9 +#define HECAP_MACINFO_SIZE 5 +#define HECAP_TXRX_MCS_NSS_SIZE 2 +#define HECAP_PPET16_PPET8_MAX_SIZE 25 + +#define HE_PPET16_PPET8_SIZE 8 + +/* 802.11ax PPE (PPDU packet Extension) threshold */ +struct he_ppe_threshold { + u32 numss_m1; + u32 ru_mask; + u32 ppet16_ppet8_ru3_ru0[HE_PPET16_PPET8_SIZE]; +}; + +struct ath11k_he { + u8 hecap_macinfo[HECAP_MACINFO_SIZE]; + u32 hecap_rxmcsnssmap; + u32 hecap_txmcsnssmap; + u32 hecap_phyinfo[HEHANDLE_CAP_PHYINFO_SIZE]; + struct he_ppe_threshold hecap_ppet; + u32 heop_param; +}; + +#define MAX_RADIOS 3 + +enum { + WMI_HOST_TP_SCALE_MAX = 0, + WMI_HOST_TP_SCALE_50 = 1, + WMI_HOST_TP_SCALE_25 = 2, + WMI_HOST_TP_SCALE_12 = 3, + WMI_HOST_TP_SCALE_MIN = 4, + WMI_HOST_TP_SCALE_SIZE = 5, +}; + +enum ath11k_scan_state { + ATH11K_SCAN_IDLE, + ATH11K_SCAN_STARTING, + ATH11K_SCAN_RUNNING, + ATH11K_SCAN_ABORTING, +}; + +enum ath11k_dev_flags { + ATH11K_CAC_RUNNING, + ATH11K_FLAG_CORE_REGISTERED, + ATH11K_FLAG_CRASH_FLUSH, + ATH11K_FLAG_RAW_MODE, + ATH11K_FLAG_HW_CRYPTO_DISABLED, + ATH11K_FLAG_BTCOEX, + ATH11K_FLAG_RECOVERY, + ATH11K_FLAG_UNREGISTERING, + ATH11K_FLAG_REGISTERED, +}; + +enum ath11k_monitor_flags { + ATH11K_FLAG_MONITOR_ENABLED, +}; + +struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + u32 beacon_interval; + u32 dtim_period; + u16 ast_hash; + u16 tcl_metadata; + u8 hal_addr_search_flags; + u8 search_type; + + struct ath11k *ar; + struct ieee80211_vif *vif; + + u16 tx_seq_no; + struct wmi_wmm_params_all_arg wmm_params; + struct list_head list; + union { + struct { + u32 uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + u8 tim_bitmap[16]; + u8 tim_len; + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + u32 noa_len; + u8 *noa_data; + } ap; + } u; + + bool is_started; + bool is_up; + u32 aid; + u8 bssid[ETH_ALEN]; + struct cfg80211_bitrate_mask bitrate_mask; + int num_legacy_stations; + int rtscts_prot_mode; + int txpower; +}; + +struct ath11k_vif_iter { + u32 vdev_id; + struct ath11k_vif *arvif; +}; + +struct ath11k_rx_peer_stats { + u64 num_msdu; + u64 num_mpdu_fcs_ok; + u64 num_mpdu_fcs_err; + u64 tcp_msdu_count; + u64 udp_msdu_count; + u64 other_msdu_count; + u64 ampdu_msdu_count; + u64 non_ampdu_msdu_count; + u64 stbc_count; + u64 beamformed_count; + u64 mcs_count[HAL_RX_MAX_MCS + 1]; + u64 nss_count[HAL_RX_MAX_NSS]; + u64 bw_count[HAL_RX_BW_MAX]; + u64 gi_count[HAL_RX_GI_MAX]; + u64 coding_count[HAL_RX_SU_MU_CODING_MAX]; + u64 tid_count[IEEE80211_NUM_TIDS + 1]; + u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; + u64 reception_type[HAL_RX_RECEPTION_TYPE_MAX]; + u64 rx_duration; +}; + +#define ATH11K_HE_MCS_NUM 12 +#define ATH11K_VHT_MCS_NUM 10 +#define ATH11K_BW_NUM 4 +#define ATH11K_NSS_NUM 4 +#define ATH11K_LEGACY_NUM 12 +#define ATH11K_GI_NUM 4 +#define ATH11K_HT_MCS_NUM 32 + +enum ath11k_pkt_rx_err { + ATH11K_PKT_RX_ERR_FCS, + ATH11K_PKT_RX_ERR_TKIP, + ATH11K_PKT_RX_ERR_CRYPT, + ATH11K_PKT_RX_ERR_PEER_IDX_INVAL, + ATH11K_PKT_RX_ERR_MAX, +}; + +enum ath11k_ampdu_subfrm_num { + ATH11K_AMPDU_SUBFRM_NUM_10, + ATH11K_AMPDU_SUBFRM_NUM_20, + ATH11K_AMPDU_SUBFRM_NUM_30, + ATH11K_AMPDU_SUBFRM_NUM_40, + ATH11K_AMPDU_SUBFRM_NUM_50, + ATH11K_AMPDU_SUBFRM_NUM_60, + ATH11K_AMPDU_SUBFRM_NUM_MORE, + ATH11K_AMPDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_amsdu_subfrm_num { + ATH11K_AMSDU_SUBFRM_NUM_1, + ATH11K_AMSDU_SUBFRM_NUM_2, + ATH11K_AMSDU_SUBFRM_NUM_3, + ATH11K_AMSDU_SUBFRM_NUM_4, + ATH11K_AMSDU_SUBFRM_NUM_MORE, + ATH11K_AMSDU_SUBFRM_NUM_MAX, +}; + +enum ath11k_counter_type { + ATH11K_COUNTER_TYPE_BYTES, + ATH11K_COUNTER_TYPE_PKTS, + ATH11K_COUNTER_TYPE_MAX, +}; + +enum ath11k_stats_type { + ATH11K_STATS_TYPE_SUCC, + ATH11K_STATS_TYPE_FAIL, + ATH11K_STATS_TYPE_RETRY, + ATH11K_STATS_TYPE_AMPDU, + ATH11K_STATS_TYPE_MAX, +}; + +struct ath11k_htt_data_stats { + u64 legacy[ATH11K_COUNTER_TYPE_MAX][ATH11K_LEGACY_NUM]; + u64 ht[ATH11K_COUNTER_TYPE_MAX][ATH11K_HT_MCS_NUM]; + u64 vht[ATH11K_COUNTER_TYPE_MAX][ATH11K_VHT_MCS_NUM]; + u64 he[ATH11K_COUNTER_TYPE_MAX][ATH11K_HE_MCS_NUM]; + u64 bw[ATH11K_COUNTER_TYPE_MAX][ATH11K_BW_NUM]; + u64 nss[ATH11K_COUNTER_TYPE_MAX][ATH11K_NSS_NUM]; + u64 gi[ATH11K_COUNTER_TYPE_MAX][ATH11K_GI_NUM]; +}; + +struct ath11k_htt_tx_stats { + struct ath11k_htt_data_stats stats[ATH11K_STATS_TYPE_MAX]; + u64 tx_duration; + u64 ba_fails; + u64 ack_fails; +}; + +struct ath11k_per_ppdu_tx_stats { + u16 succ_pkts; + u16 failed_pkts; + u16 retry_pkts; + u32 succ_bytes; + u32 failed_bytes; + u32 retry_bytes; +}; + +struct ath11k_sta { + struct ath11k_vif *arvif; + + /* the following are protected by ar->data_lock */ + u32 changed; /* IEEE80211_RC_* */ + u32 bw; + u32 nss; + u32 smps; + + struct work_struct update_wk; + struct ieee80211_tx_info tx_info; + struct rate_info txrate; + struct rate_info last_txrate; + u64 rx_duration; + u64 tx_duration; + u8 rssi_comb; + struct ath11k_htt_tx_stats *tx_stats; + struct ath11k_rx_peer_stats *rx_stats; +}; + +#define ATH11K_NUM_CHANS 41 +#define ATH11K_MAX_5G_CHAN 173 + +enum ath11k_state { + ATH11K_STATE_OFF, + ATH11K_STATE_ON, + ATH11K_STATE_RESTARTING, + ATH11K_STATE_RESTARTED, + ATH11K_STATE_WEDGED, + /* Add other states as required */ +}; + +/* Antenna noise floor */ +#define ATH11K_DEFAULT_NOISE_FLOOR -95 + +struct ath11k_fw_stats { + struct dentry *debugfs_fwstats; + u32 pdev_id; + u32 stats_id; + struct list_head pdevs; + struct list_head vdevs; + struct list_head bcn; +}; + +struct ath11k_dbg_htt_stats { + u8 type; + u8 reset; + struct debug_htt_stats_req *stats_req; + /* protects shared stats req buffer */ + spinlock_t lock; +}; + +struct ath11k_debug { + struct dentry *debugfs_pdev; + struct ath11k_dbg_htt_stats htt_stats; + u32 extd_tx_stats; + struct ath11k_fw_stats fw_stats; + struct completion fw_stats_complete; + bool fw_stats_done; + u32 extd_rx_stats; + u32 pktlog_filter; + u32 pktlog_mode; + u32 pktlog_peer_valid; + u8 pktlog_peer_addr[ETH_ALEN]; +}; + +struct ath11k_per_peer_tx_stats { + u32 succ_bytes; + u32 retry_bytes; + u32 failed_bytes; + u16 succ_pkts; + u16 retry_pkts; + u16 failed_pkts; + u32 duration; + u8 ba_fails; + bool is_ampdu; +}; + +#define ATH11K_FLUSH_TIMEOUT (5 * HZ) + +struct ath11k_vdev_stop_status { + bool stop_in_progress; + u32 vdev_id; +}; + +struct ath11k { + struct ath11k_base *ab; + struct ath11k_pdev *pdev; + struct ieee80211_hw *hw; + struct ieee80211_ops *ops; + struct ath11k_pdev_wmi *wmi; + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; + u32 ht_cap_info; + u32 vht_cap_info; + struct ath11k_he ar_he; + enum ath11k_state state; + struct { + struct completion started; + struct completion completed; + struct completion on_channel; + struct delayed_work timeout; + enum ath11k_scan_state state; + bool is_roc; + int vdev_id; + int roc_freq; + bool roc_notify; + } scan; + + struct { + struct ieee80211_supported_band sbands[NUM_NL80211_BANDS]; + struct ieee80211_sband_iftype_data + iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + } mac; + unsigned long dev_flags; + unsigned int filter_flags; + unsigned long monitor_flags; + u32 min_tx_power; + u32 max_tx_power; + u32 txpower_limit_2g; + u32 txpower_limit_5g; + u32 txpower_scale; + u32 power_scale; + u32 chan_tx_pwr; + u32 num_stations; + u32 max_num_stations; + bool monitor_present; + /* To synchronize concurrent synchronous mac80211 callback operations, + * concurrent debugfs configuration and concurrent FW statistics events. + */ + struct mutex conf_mutex; + /* protects the radio specific data like debug stats, ppdu_stats_info stats, + * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, + * channel context data, survey info, test mode data. + */ + spinlock_t data_lock; + + struct list_head arvifs; + /* should never be NULL; needed for regular htt rx */ + struct ieee80211_channel *rx_channel; + + /* valid during scan; needed for mgmt rx during scan */ + struct ieee80211_channel *scan_channel; + + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; + u8 num_rx_chains; + u8 num_tx_chains; + /* pdev_idx starts from 0 whereas pdev->pdev_id starts with 1 */ + u8 pdev_idx; + u8 lmac_id; + + struct completion peer_assoc_done; + + int install_key_status; + struct completion install_key_done; + + int last_wmi_vdev_start_status; + struct ath11k_vdev_stop_status vdev_stop_status; + struct completion vdev_setup_done; + + int num_peers; + int max_num_peers; + u32 num_started_vdevs; + u32 num_created_vdevs; + + struct idr txmgmt_idr; + /* protects txmgmt_idr data */ + spinlock_t txmgmt_idr_lock; + atomic_t num_pending_mgmt_tx; + + /* cycle count is reported twice for each visited channel during scan. + * access protected by data_lock + */ + u32 survey_last_rx_clear_count; + u32 survey_last_cycle_count; + + /* Channel info events are expected to come in pairs without and with + * COMPLETE flag set respectively for each channel visit during scan. + * + * However there are deviations from this rule. This flag is used to + * avoid reporting garbage data. + */ + bool ch_info_can_report_survey; + struct survey_info survey[ATH11K_NUM_CHANS]; + struct completion bss_survey_done; + + struct work_struct regd_update_work; + + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + + struct ath11k_per_peer_tx_stats peer_tx_stats; + struct list_head ppdu_stats_info; + u32 ppdu_stat_list_depth; + + struct ath11k_per_peer_tx_stats cached_stats; + u32 last_ppdu_id; + u32 cached_ppdu_id; +#ifdef CONFIG_ATH11K_DEBUGFS + struct ath11k_debug debug; +#endif + bool dfs_block_radar_events; +}; + +struct ath11k_band_cap { + u32 max_bw_supported; + u32 ht_cap_info; + u32 he_cap_info[2]; + u32 he_mcs; + u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE]; + struct ath11k_ppe_threshold he_ppet; +}; + +struct ath11k_pdev_cap { + u32 supported_bands; + u32 ampdu_density; + u32 vht_cap; + u32 vht_mcs; + u32 he_mcs; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 tx_chain_mask_shift; + u32 rx_chain_mask_shift; + struct ath11k_band_cap band[NUM_NL80211_BANDS]; +}; + +struct ath11k_pdev { + struct ath11k *ar; + u32 pdev_id; + struct ath11k_pdev_cap cap; + u8 mac_addr[ETH_ALEN]; +}; + +struct ath11k_board_data { + const struct firmware *fw; + const void *data; + size_t len; +}; + +/* IPQ8074 HW channel counters frequency value in hertz */ +#define IPQ8074_CC_FREQ_HERTZ 320000 + +struct ath11k_soc_dp_rx_stats { + u32 err_ring_pkts; + u32 invalid_rbm; + u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; + u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; + u32 hal_reo_error[DP_REO_DST_RING_MAX]; +}; + +/* Master structure to hold the hw data which may be used in core module */ +struct ath11k_base { + enum ath11k_hw_rev hw_rev; + struct platform_device *pdev; + struct device *dev; + struct ath11k_qmi qmi; + struct ath11k_wmi_base wmi_ab; + struct completion fw_ready; + struct rproc *tgt_rproc; + int num_radios; + /* HW channel counters frequency value in hertz common to all MACs */ + u32 cc_freq_hz; + + struct ath11k_htc htc; + + struct ath11k_dp dp; + + void __iomem *mem; + unsigned long mem_len; + + const struct ath11k_hif_ops *hif_ops; + + struct ath11k_ce ce; + struct timer_list rx_replenish_retry; + struct ath11k_hal hal; + /* To synchronize core_start/core_stop */ + struct mutex core_lock; + /* Protects data like peers */ + spinlock_t base_lock; + struct ath11k_pdev pdevs[MAX_RADIOS]; + struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; + bool wmi_ready; + u32 wlan_init_status; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct napi_struct *napi; + struct ath11k_targ_cap target_caps; + u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE]; + bool pdevs_macaddr_valid; + int bd_api; + struct ath11k_hw_params hw_params; + const struct firmware *cal_file; + + /* Below regd's are protected by ab->data_lock */ + /* This is the regd set for every radio + * by the firmware during initializatin + */ + struct ieee80211_regdomain *default_regd[MAX_RADIOS]; + /* This regd is set during dynamic country setting + * This may or may not be used during the runtime + */ + struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + + /* Current DFS Regulatory */ + enum ath11k_dfs_region dfs_region; +#ifdef CONFIG_ATH11K_DEBUGFS + struct dentry *debugfs_soc; + struct dentry *debugfs_ath11k; +#endif + struct ath11k_soc_dp_rx_stats soc_stats; + + unsigned long dev_flags; + struct completion driver_recovery; + struct workqueue_struct *workqueue; + struct work_struct restart_work; + struct { + /* protected by data_lock */ + u32 fw_crash_counter; + } stats; +}; + +struct ath11k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + /* Cycles spent transmitting frames */ + u32 tx_frame_count; + /* Cycles spent receiving frames */ + u32 rx_frame_count; + /* Total channel busy time, evidently */ + u32 rx_clear_count; + /* Total on-channel time */ + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + /* Num HTT cookies queued to dispatch list */ + s32 comp_queued; + /* Num HTT cookies dispatched */ + s32 comp_delivered; + /* Num MSDU queued to WAL */ + s32 msdu_enqued; + /* Num MPDU queue to WAL */ + s32 mpdu_enqued; + /* Num MSDUs dropped by WMM limit */ + s32 wmm_drop; + /* Num Local frames queued */ + s32 local_enqued; + /* Num Local frames done */ + s32 local_freed; + /* Num queued to HW */ + s32 hw_queued; + /* Num PPDU reaped from HW */ + s32 hw_reaped; + /* Num underruns */ + s32 underrun; + /* Num PPDUs cleaned up in TX abort */ + s32 tx_abort; + /* Num MPDUs requed by SW */ + s32 mpdus_requed; + /* excessive retries */ + u32 tx_ko; + /* data hw rate code */ + u32 data_rc; + /* Scheduler self triggers */ + u32 self_triggers; + /* frames dropped due to excessive sw retries */ + u32 sw_retry_failure; + /* illegal rate phy errors */ + u32 illgl_rate_phy_err; + /* wal pdev continuous xretry */ + u32 pdev_cont_xretry; + /* wal pdev tx timeouts */ + u32 pdev_tx_timeout; + /* wal pdev resets */ + u32 pdev_resets; + /* frames dropped due to non-availability of stateless TIDs */ + u32 stateless_tid_alloc_failure; + /* PhY/BB underrun */ + u32 phy_underrun; + /* MPDU is more than txop limit */ + u32 txop_ovf; + + /* PDEV RX stats */ + /* Cnts any change in ring routing mid-ppdu */ + s32 mid_ppdu_route_change; + /* Total number of statuses processed */ + s32 status_rcvd; + /* Extra frags on rings 0-3 */ + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + /* MSDUs / MPDUs delivered to HTT */ + s32 htt_msdus; + s32 htt_mpdus; + /* MSDUs / MPDUs delivered to local stack */ + s32 loc_msdus; + s32 loc_mpdus; + /* AMSDUs that have more MSDUs than the status ring size */ + s32 oversize_amsdu; + /* Number of PHY errors */ + s32 phy_errs; + /* Number of PHY errors drops */ + s32 phy_err_drop; + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + s32 mpdu_errs; +}; + +struct ath11k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + +struct ath11k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + +void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); +void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash); +struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); +struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); +int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab); +int ath11k_core_init(struct ath11k_base *ath11k); +void ath11k_core_deinit(struct ath11k_base *ath11k); +struct ath11k_base *ath11k_core_alloc(struct device *dev); +void ath11k_core_free(struct ath11k_base *ath11k); +int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, + struct ath11k_board_data *bd); +void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); + +void ath11k_core_halt(struct ath11k *ar); +u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx); + +static inline const char *ath11k_scan_state_str(enum ath11k_scan_state state) +{ + switch (state) { + case ATH11K_SCAN_IDLE: + return "idle"; + case ATH11K_SCAN_STARTING: + return "starting"; + case ATH11K_SCAN_RUNNING: + return "running"; + case ATH11K_SCAN_ABORTING: + return "aborting"; + } + + return "unknown"; +} + +static inline struct ath11k_skb_cb *ATH11K_SKB_CB(struct sk_buff *skb) +{ + return (struct ath11k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +static inline struct ath11k_skb_rxcb *ATH11K_SKB_RXCB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath11k_skb_rxcb) > sizeof(skb->cb)); + return (struct ath11k_skb_rxcb *)skb->cb; +} + +static inline struct ath11k_vif *ath11k_vif_to_arvif(struct ieee80211_vif *vif) +{ + return (struct ath11k_vif *)vif->drv_priv; +} + +#endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c new file mode 100644 index 000000000000..f48daf17f2d2 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -0,0 +1,1060 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "debug_htt_stats.h" +#include "peer.h" + +void ath11k_info(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_info(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_err(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_err(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + dev_warn_ratelimited(ab->dev, "%pV", &vaf); + /* TODO: Trace the log */ + va_end(args); +} + +#ifdef CONFIG_ATH11K_DEBUG +void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ath11k_debug_mask & mask) + dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + + /* TODO: trace log */ + + va_end(args); +} + +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + char linebuf[256]; + size_t linebuflen; + const void *ptr; + + if (ath11k_debug_mask & mask) { + if (msg) + __ath11k_dbg(ab, mask, "%s\n", msg); + + for (ptr = buf; (ptr - buf) < len; ptr += 16) { + linebuflen = 0; + linebuflen += scnprintf(linebuf + linebuflen, + sizeof(linebuf) - linebuflen, + "%s%08x: ", + (prefix ? prefix : ""), + (unsigned int)(ptr - buf)); + hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1, + linebuf + linebuflen, + sizeof(linebuf) - linebuflen, true); + dev_printk(KERN_DEBUG, ab->dev, "%s\n", linebuf); + } + } +} + +#endif + +#ifdef CONFIG_ATH11K_DEBUGFS +static void ath11k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath11k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath11k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath11k_debug_fw_stats_reset(struct ath11k *ar) +{ + spin_lock_bh(&ar->data_lock); + ar->debug.fw_stats_done = false; + ath11k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); + ath11k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); + spin_unlock_bh(&ar->data_lock); +} + +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct ath11k_fw_stats stats = {}; + struct ath11k *ar; + struct ath11k_pdev *pdev; + bool is_end; + static unsigned int num_vdev, num_bcn; + size_t total_vdevs_started = 0; + int i, ret; + + INIT_LIST_HEAD(&stats.pdevs); + INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); + + ret = ath11k_wmi_pull_fw_stats(ab, skb, &stats); + if (ret) { + ath11k_warn(ab, "failed to pull fw stats: %d\n", ret); + goto free; + } + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); + if (!ar) { + rcu_read_unlock(); + ath11k_warn(ab, "failed to get ar for pdev_id %d: %d\n", + stats.pdev_id, ret); + goto free; + } + + spin_lock_bh(&ar->data_lock); + + if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs); + ar->debug.fw_stats_done = true; + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats.vdevs)) { + ath11k_warn(ab, "empty vdev stats"); + goto complete; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += ar->num_started_vdevs; + } + + is_end = ((++num_vdev) == total_vdevs_started ? true : false); + + list_splice_tail_init(&stats.vdevs, + &ar->debug.fw_stats.vdevs); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_vdev = 0; + } + goto complete; + } + + if (stats.stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats.bcn)) { + ath11k_warn(ab, "empty bcn stats"); + goto complete; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + is_end = ((++num_bcn) == ar->num_started_vdevs ? true : false); + + list_splice_tail_init(&stats.bcn, + &ar->debug.fw_stats.bcn); + + if (is_end) { + ar->debug.fw_stats_done = true; + num_bcn = 0; + } + } +complete: + complete(&ar->debug.fw_stats_complete); + rcu_read_unlock(); + spin_unlock_bh(&ar->data_lock); + +free: + ath11k_fw_stats_pdevs_free(&stats.pdevs); + ath11k_fw_stats_vdevs_free(&stats.vdevs); + ath11k_fw_stats_bcn_free(&stats.bcn); +} + +static int ath11k_debug_fw_stats_request(struct ath11k *ar, + struct stats_request_params *req_param) +{ + struct ath11k_base *ab = ar->ab; + unsigned long timeout, time_left; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + timeout = jiffies + msecs_to_jiffies(3 * HZ); + + ath11k_debug_fw_stats_reset(ar); + + reinit_completion(&ar->debug.fw_stats_complete); + + ret = ath11k_wmi_send_stats_request_cmd(ar, req_param); + + if (ret) { + ath11k_warn(ab, "could not request fw stats (%d)\n", + ret); + return ret; + } + + time_left = + wait_for_completion_timeout(&ar->debug.fw_stats_complete, + 1 * HZ); + if (!time_left) + return -ETIMEDOUT; + + for (;;) { + if (time_after(jiffies, timeout)) + break; + + spin_lock_bh(&ar->data_lock); + if (ar->debug.fw_stats_done) { + spin_unlock_bh(&ar->data_lock); + break; + } + spin_unlock_bh(&ar->data_lock); + } + return 0; +} + +static int ath11k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_base *ab = ar->ab; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_pdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath11k_open_pdev_stats, + .release = ath11k_release_pdev_stats, + .read = ath11k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.pdev_id = ar->pdev->pdev_id; + /* VDEV stats is always sent for all active VDEVs from FW */ + req_param.vdev_id = 0; + req_param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + goto err_free; + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_vdev_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath11k_open_vdev_stats, + .release = ath11k_release_vdev_stats, + .read = ath11k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct ath11k_vif *arvif; + struct stats_request_params req_param; + void *buf = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto err_unlock; + } + + buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + req_param.stats_id = WMI_REQUEST_BCN_STAT; + req_param.pdev_id = ar->pdev->pdev_id; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + req_param.vdev_id = arvif->vdev_id; + ret = ath11k_debug_fw_stats_request(ar, &req_param); + if (ret) { + ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + goto err_free; + } + } + + ath11k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, req_param.stats_id, + buf); + + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath11k_fw_stats_bcn_free(&ar->debug.fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = buf; + + mutex_unlock(&ar->conf_mutex); + return 0; + +err_free: + vfree(buf); + +err_unlock: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath11k_release_bcn_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + + return 0; +} + +static ssize_t ath11k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath11k_open_bcn_stats, + .release = ath11k_release_bcn_stats, + .read = ath11k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = + "To simulate firmware crash write one of the keywords to this file:\n" + "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n" + "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n"; + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +/* Simulate firmware crash: + * 'soft': Call wmi command causing firmware hang. This firmware hang is + * recoverable by warm firmware reset. + * 'hard': Force firmware crash by setting any vdev parameter for not allowed + * vdev id. This is hard firmware crash because it is recoverable only by cold + * firmware reset. + */ +static ssize_t ath11k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_pdev *pdev; + struct ath11k *ar = ab->pdevs[0].ar; + char buf[32] = {0}; + ssize_t rc; + int i, ret, radioup = 0; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar && ar->state == ATH11K_STATE_ON) { + radioup = 1; + break; + } + } + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + if (radioup == 0) { + ret = -ENETDOWN; + goto exit; + } + + if (!strcmp(buf, "assert")) { + ath11k_info(ab, "simulating firmware assert crash\n"); + ret = ath11k_wmi_force_fw_hang_cmd(ar, + ATH11K_WMI_FW_HANG_ASSERT_TYPE, + ATH11K_WMI_FW_HANG_DELAY); + } else { + ret = -EINVAL; + goto exit; + } + + if (ret) { + ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret); + goto exit; + } + + ret = count; + +exit: + return ret; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath11k_read_simulate_fw_crash, + .write = ath11k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u32 filter; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &filter)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (filter == ar->debug.extd_tx_stats) { + ret = count; + goto out; + } + + ar->debug.extd_tx_stats = filter; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x\n", + ar->debug.extd_tx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_tx_stats = { + .read = ath11k_read_enable_extd_tx_stats, + .write = ath11k_write_enable_extd_tx_stats, + .open = simple_open +}; + +static ssize_t ath11k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 enable, rx_filter = 0, ring_id; + int ret; + + if (kstrtouint_from_user(ubuf, count, 0, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable > 1) { + ret = -EINVAL; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath11k_mac_mon_status_filter_default; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } + + ar->debug.extd_rx_stats = enable; + ret = count; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath11k_read_extd_rx_stats, + .write = ath11k_write_extd_rx_stats, + .open = simple_open, +}; + +static ssize_t ath11k_debug_dump_soc_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k_soc_dp_rx_stats *soc_stats = &ab->soc_stats; + int len = 0, i, retval; + const int size = 4096; + static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { + "Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC", + "Unencrypt", "MSDU len", "MSDU limit", "WiFi parse", + "AMSDU parse", "SA timeout", "DA timeout", + "Flow timeout", "Flush req"}; + static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { + "Desc addr zero", "Desc inval", "AMPDU in non BA", + "Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump", + "Frame OOR", "BAR OOR", "No BA session", + "Frame SN equal SSN", "PN check fail", "2k err", + "PN err", "Desc blocked"}; + + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n"); + len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", + soc_stats->err_ring_pkts); + len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", + soc_stats->invalid_rbm); + len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); + for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + rxdma_err[i], soc_stats->rxdma_error[i]); + + len += scnprintf(buf + len, size - len, "\nREO errors:\n"); + for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + reo_err[i], soc_stats->reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); + len += scnprintf(buf + len, size - len, + "ring0: %u\nring1: %u\nring2: %u\nring3: %u\n", + soc_stats->hal_reo_error[0], + soc_stats->hal_reo_error[1], + soc_stats->hal_reo_error[2], + soc_stats->hal_reo_error[3]); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_soc_rx_stats = { + .read = ath11k_debug_dump_soc_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + ab->debugfs_soc = debugfs_create_dir(ab->hw_params.name, ab->debugfs_ath11k); + + if (IS_ERR_OR_NULL(ab->debugfs_soc)) { + if (IS_ERR(ab->debugfs_soc)) + return PTR_ERR(ab->debugfs_soc); + return -ENOMEM; + } + + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + + debugfs_create_file("soc_rx_stats", 0600, ab->debugfs_soc, ab, + &fops_soc_rx_stats); + + return 0; +} + +void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_ath11k); + ab->debugfs_ath11k = NULL; +} + +int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + ab->debugfs_ath11k = debugfs_create_dir("ath11k", NULL); + + if (IS_ERR_OR_NULL(ab->debugfs_ath11k)) { + if (IS_ERR(ab->debugfs_ath11k)) + return PTR_ERR(ab->debugfs_ath11k); + return -ENOMEM; + } + + return 0; +} + +void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ + debugfs_remove_recursive(ab->debugfs_soc); + ab->debugfs_soc = NULL; +} + +void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + ar->debug.fw_stats.debugfs_fwstats = fwstats_dir; + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); + + INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs); + INIT_LIST_HEAD(&ar->debug.fw_stats.bcn); + + init_completion(&ar->debug.fw_stats_complete); +} + +static ssize_t ath11k_write_pktlog_filter(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 rx_filter = 0, ring_id, filter, mode; + u8 buf[128] = {0}; + int ret; + ssize_t rc; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (rc < 0) { + ret = rc; + goto out; + } + buf[rc] = '\0'; + + ret = sscanf(buf, "0x%x %u", &filter, &mode); + if (ret != 2) { + ret = -EINVAL; + goto out; + } + + if (filter) { + ret = ath11k_wmi_pdev_pktlog_enable(ar, filter); + if (ret) { + ath11k_warn(ar->ab, + "failed to enable pktlog filter %x: %d\n", + ar->debug.pktlog_filter, ret); + goto out; + } + } else { + ret = ath11k_wmi_pdev_pktlog_disable(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret); + goto out; + } + } + +#define HTT_RX_FILTER_TLV_LITE_MODE \ + (HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_START) + + if (mode == ATH11K_PKTLOG_MODE_FULL) { + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE | + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | + HTT_RX_FILTER_TLV_FLAGS_ATTENTION; + } else if (mode == ATH11K_PKTLOG_MODE_LITE) { + rx_filter = HTT_RX_FILTER_TLV_LITE_MODE; + } + + tlv_filter.rx_filter = rx_filter; + if (rx_filter) { + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } + + ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", + filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); + + ar->debug.pktlog_filter = filter; + ar->debug.pktlog_mode = mode; + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_pktlog_filter(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) + +{ + char buf[32] = {0}; + struct ath11k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n", + ar->debug.pktlog_filter, + ar->debug.pktlog_mode); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_pktlog_filter = { + .read = ath11k_read_pktlog_filter, + .write = ath11k_write_pktlog_filter, + .open = simple_open +}; + +static ssize_t ath11k_write_simulate_radar(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + int ret; + + ret = ath11k_wmi_simulate_radar(ar); + if (ret) + return ret; + + return count; +} + +static const struct file_operations fops_simulate_radar = { + .write = ath11k_write_simulate_radar, + .open = simple_open +}; + +int ath11k_debug_register(struct ath11k *ar) +{ + struct ath11k_base *ab = ar->ab; + char pdev_name[5]; + char buf[100] = {0}; + + snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); + + if (IS_ERR_OR_NULL(ar->debug.debugfs_pdev)) { + if (IS_ERR(ar->debug.debugfs_pdev)) + return PTR_ERR(ar->debug.debugfs_pdev); + + return -ENOMEM; + } + + /* Create a symlink under ieee80211/phy* */ + snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev); + debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf); + + ath11k_debug_htt_stats_init(ar); + + ath11k_debug_fw_stats_init(ar); + + debugfs_create_file("ext_tx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_tx_stats); + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); + debugfs_create_file("pktlog_filter", 0644, + ar->debug.debugfs_pdev, ar, + &fops_pktlog_filter); + + if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { + debugfs_create_file("dfs_simulate_radar", 0200, + ar->debug.debugfs_pdev, ar, + &fops_simulate_radar); + debugfs_create_bool("dfs_block_radar_events", 0200, + ar->debug.debugfs_pdev, + &ar->dfs_block_radar_events); + } + + return 0; +} + +void ath11k_debug_unregister(struct ath11k *ar) +{ +} +#endif /* CONFIG_ATH11K_DEBUGFS */ diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h new file mode 100644 index 000000000000..a317a7bdb9a2 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef _ATH11K_DEBUG_H_ +#define _ATH11K_DEBUG_H_ + +#include "hal_tx.h" +#include "trace.h" + +#define ATH11K_TX_POWER_MAX_VAL 70 +#define ATH11K_TX_POWER_MIN_VAL 0 + +enum ath11k_debug_mask { + ATH11K_DBG_AHB = 0x00000001, + ATH11K_DBG_WMI = 0x00000002, + ATH11K_DBG_HTC = 0x00000004, + ATH11K_DBG_DP_HTT = 0x00000008, + ATH11K_DBG_MAC = 0x00000010, + ATH11K_DBG_BOOT = 0x00000020, + ATH11K_DBG_QMI = 0x00000040, + ATH11K_DBG_DATA = 0x00000080, + ATH11K_DBG_MGMT = 0x00000100, + ATH11K_DBG_REG = 0x00000200, + ATH11K_DBG_TESTMODE = 0x00000400, + ATH11k_DBG_HAL = 0x00000800, + ATH11K_DBG_ANY = 0xffffffff, +}; + +/* htt_dbg_ext_stats_type */ +enum ath11k_dbg_htt_ext_stats_type { + ATH11K_DBG_HTT_EXT_STATS_RESET = 0, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX = 2, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ = 3, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH11K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ = 7, + ATH11K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH11K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH11K_DBG_HTT_EXT_STATS_PEER_INFO = 11, + ATH11K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ = 13, + ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO = 14, + ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH11K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST = 18, + ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH11K_DBG_HTT_EXT_STATS_TWT_SESSIONS = 20, + ATH11K_DBG_HTT_EXT_STATS_REO_RESOURCE_STATS = 21, + ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + + /* keep this last */ + ATH11K_DBG_HTT_NUM_EXT_STATS, +}; + +struct debug_htt_stats_req { + bool done; + u8 pdev_id; + u8 type; + u8 peer_addr[ETH_ALEN]; + struct completion cmpln; + u32 buf_len; + u8 buf[0]; +}; + +#define ATH11K_HTT_STATS_BUF_SIZE (1024 * 512) + +#define ATH11K_FW_STATS_BUF_SIZE (1024 * 1024) + +#define ATH11K_HTT_PKTLOG_MAX_SIZE 2048 + +enum ath11k_pktlog_filter { + ATH11K_PKTLOG_RX = 0x000000001, + ATH11K_PKTLOG_TX = 0x000000002, + ATH11K_PKTLOG_RCFIND = 0x000000004, + ATH11K_PKTLOG_RCUPDATE = 0x000000008, + ATH11K_PKTLOG_EVENT_SMART_ANT = 0x000000020, + ATH11K_PKTLOG_EVENT_SW = 0x000000040, + ATH11K_PKTLOG_ANY = 0x00000006f, +}; + +enum ath11k_pktlog_mode { + ATH11K_PKTLOG_MODE_LITE = 1, + ATH11K_PKTLOG_MODE_FULL = 2, +}; + +enum ath11k_pktlog_enum { + ATH11K_PKTLOG_TYPE_TX_CTRL = 1, + ATH11K_PKTLOG_TYPE_TX_STAT = 2, + ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3, + ATH11K_PKTLOG_TYPE_RX_STAT = 5, + ATH11K_PKTLOG_TYPE_RC_FIND = 6, + ATH11K_PKTLOG_TYPE_RC_UPDATE = 7, + ATH11K_PKTLOG_TYPE_TX_VIRT_ADDR = 8, + ATH11K_PKTLOG_TYPE_RX_CBF = 10, + ATH11K_PKTLOG_TYPE_RX_STATBUF = 22, + ATH11K_PKTLOG_TYPE_PPDU_STATS = 23, + ATH11K_PKTLOG_TYPE_LITE_RX = 24, +}; + +__printf(2, 3) void ath11k_info(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...); +__printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...); + +extern unsigned int ath11k_debug_mask; + +#ifdef CONFIG_ATH11K_DEBUG +__printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *fmt, ...); +void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len); +#else /* CONFIG_ATH11K_DEBUG */ +static inline int __ath11k_dbg(struct ath11k_base *ab, + enum ath11k_debug_mask dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath11k_dbg_dump(struct ath11k_base *ab, + enum ath11k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} +#endif /* CONFIG_ATH11K_DEBUG */ + +#ifdef CONFIG_ATH11K_DEBUGFS +int ath11k_debug_soc_create(struct ath11k_base *ab); +void ath11k_debug_soc_destroy(struct ath11k_base *ab); +int ath11k_debug_pdev_create(struct ath11k_base *ab); +void ath11k_debug_pdev_destroy(struct ath11k_base *ab); +int ath11k_debug_register(struct ath11k *ar); +void ath11k_debug_unregister(struct ath11k *ar); +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb); +void ath11k_debug_fw_stats_process(struct ath11k_base *ab, struct sk_buff *skb); + +void ath11k_debug_fw_stats_init(struct ath11k *ar); +int ath11k_dbg_htt_stats_req(struct ath11k *ar); + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return (ar->debug.pktlog_mode == ATH11K_PKTLOG_MODE_LITE); +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return (!ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode); +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return (ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode && + ether_addr_equal(addr, ar->debug.pktlog_peer_addr)); +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_tx_stats; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return ar->debug.extd_rx_stats; +} +#else +static inline int ath11k_debug_soc_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_soc_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_pdev_create(struct ath11k_base *ab) +{ + return 0; +} + +static inline void ath11k_debug_pdev_destroy(struct ath11k_base *ab) +{ +} + +static inline int ath11k_debug_register(struct ath11k *ar) +{ + return 0; +} + +static inline void ath11k_debug_unregister(struct ath11k *ar) +{ +} + +static inline void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_process(struct ath11k_base *ab, + struct sk_buff *skb) +{ +} + +static inline void ath11k_debug_fw_stats_init(struct ath11k *ar) +{ +} + +static inline int ath11k_debug_is_extd_tx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_debug_is_extd_rx_stats_enabled(struct ath11k *ar) +{ + return 0; +} + +static inline int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + return 0; +} + +static inline bool ath11k_debug_is_pktlog_lite_mode_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_rx_stats_enabled(struct ath11k *ar) +{ + return false; +} + +static inline bool ath11k_debug_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr) +{ + return false; +} +#endif /* CONFIG_ATH11K_DEBUGFS */ + +#ifdef CONFIG_MAC80211_DEBUGFS +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx); +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts); +#else /* !CONFIG_MAC80211_DEBUGFS */ +static inline void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ +} + +static inline void +ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ +} + +#endif /* CONFIG_MAC80211_DEBUGFS*/ + +#define ath11k_dbg(ar, dbg_mask, fmt, ...) \ +do { \ + if (ath11k_debug_mask & dbg_mask) \ + __ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \ +} while (0) + +#endif /* _ATH11K_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.c b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c new file mode 100644 index 000000000000..090fffa5e53c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.c @@ -0,0 +1,4431 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> +#include "core.h" +#include "dp_tx.h" +#include "dp_rx.h" +#include "debug.h" +#include "debug_htt_stats.h" + +#define HTT_DBG_OUT(buf, len, fmt, ...) \ + scnprintf(buf, len, fmt "\n", ##__VA_ARGS__) + +#define HTT_MAX_STRING_LEN 256 +#define HTT_MAX_PRINT_CHAR_PER_ELEM 15 + +#define HTT_TLV_HDR_LEN 4 + +#define ARRAY_TO_STRING(out, arr, len) \ + do { \ + int index = 0; u8 i; \ + for (i = 0; i < len; i++) { \ + index += snprintf(out + index, HTT_MAX_STRING_LEN - index, \ + " %u:%u,", i, arr[i]); \ + if (index < 0 || index >= HTT_MAX_STRING_LEN) \ + break; \ + } \ + } while (0) + +static inline void htt_print_stats_string_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_string_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + u16 index = 0; + char data[HTT_MAX_STRING_LEN] = {0}; + + tag_len = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_STRING_TLV:"); + + for (i = 0; i < tag_len; i++) { + index += snprintf(&data[index], + HTT_MAX_STRING_LEN - index, + "%.*s", 4, (char *)&(htt_stats_buf->data[i])); + if (index >= HTT_MAX_STRING_LEN) + break; + } + + len += HTT_DBG_OUT(buf + len, buf_len - len, "data = %s\n", data); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun = %u", + htt_stats_buf->underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_paused = %u", + htt_stats_buf->hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_flush = %u", + htt_stats_buf->hw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_filt = %u", + htt_stats_buf->hw_filt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_requeued = %u", + htt_stats_buf->mpdu_requed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_xretry = %u", + htt_stats_buf->tx_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "data_rc = %u", + htt_stats_buf->data_rc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_dropped_xretry = %u", + htt_stats_buf->mpdu_dropped_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "illegal_rate_phy_err = %u", + htt_stats_buf->illgl_rate_phy_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cont_xretry = %u", + htt_stats_buf->cont_xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_timeout = %u", + htt_stats_buf->tx_timeout); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_resets = %u", + htt_stats_buf->pdev_resets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_underrun = %u", + htt_stats_buf->phy_underrun); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_ovf = %u", + htt_stats_buf->txop_ovf); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted = %u", + htt_stats_buf->seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_failed_queueing = %u", + htt_stats_buf->seq_failed_queueing); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_completed = %u", + htt_stats_buf->seq_completed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_restarted = %u", + htt_stats_buf->seq_restarted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_seq_posted = %u", + htt_stats_buf->mu_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_switch_hw_paused = %u", + htt_stats_buf->seq_switch_hw_paused); + len += HTT_DBG_OUT(buf + len, buf_len - len, "next_seq_posted_dsr = %u", + htt_stats_buf->next_seq_posted_dsr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_posted_isr = %u", + htt_stats_buf->seq_posted_isr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "seq_ctrl_cached = %u", + htt_stats_buf->seq_ctrl_cached); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count_tqm = %u", + htt_stats_buf->mpdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count_tqm = %u", + htt_stats_buf->msdu_count_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_removed_tqm = %u", + htt_stats_buf->mpdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_removed_tqm = %u", + htt_stats_buf->msdu_removed_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_sw_flush = %u", + htt_stats_buf->mpdus_sw_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_truncated = %u", + htt_stats_buf->mpdus_truncated); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_ack_failed = %u", + htt_stats_buf->mpdus_ack_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_expired = %u", + htt_stats_buf->mpdus_expired); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_seq_hw_retry = %u", + htt_stats_buf->mpdus_seq_hw_retry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt_valid = %u", + htt_stats_buf->coex_abort_mpdu_cnt_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "coex_abort_mpdu_cnt = %u", + htt_stats_buf->coex_abort_mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_total_ppdus_tried_ota = %u", + htt_stats_buf->num_total_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_tried_ota = %u", + htt_stats_buf->num_data_ppdus_tried_ota); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_enqued = %u", + htt_stats_buf->local_ctrl_mgmt_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_ctrl_mgmt_freed = %u", + htt_stats_buf->local_ctrl_mgmt_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_enqued = %u", + htt_stats_buf->local_data_enqued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_data_freed = %u", + htt_stats_buf->local_data_freed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried = %u", + htt_stats_buf->mpdu_tried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "isr_wait_seq_posted = %u", + htt_stats_buf->isr_wait_seq_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_low = %u", + htt_stats_buf->tx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_active_dur_us_high = %u\n", + htt_stats_buf->tx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_urrn_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_urrn_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char urrn_stats[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_URRN_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_URRN_TLV_V:"); + + ARRAY_TO_STRING(urrn_stats, htt_stats_buf->urrn_stats, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "urrn_stats = %s\n", urrn_stats); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_flush_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_flush_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char flush_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_FLUSH_REASON_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_FLUSH_TLV_V:"); + + ARRAY_TO_STRING(flush_errs, htt_stats_buf->flush_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_errs = %s\n", flush_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_SIFS_TLV_V:"); + + ARRAY_TO_STRING(sifs_status, htt_stats_buf->sifs_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_status = %s\n", + sifs_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_phy_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_phy_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_PHY_ERR_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_STATS_PHY_ERR_TLV_V:"); + + ARRAY_TO_STRING(phy_errs, htt_stats_buf->phy_errs, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sifs_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sifs_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sifs_hist_status[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SIFS_HIST_TLV_V:"); + + ARRAY_TO_STRING(sifs_hist_status, htt_stats_buf->sifs_hist_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sifs_hist_status = %s\n", + sifs_hist_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TX_PPDU_STATS_TLV_V:"); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_legacy_su = %u", + htt_stats_buf->num_data_ppdus_legacy_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su = %u", + htt_stats_buf->num_data_ppdus_ac_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su = %u", + htt_stats_buf->num_data_ppdus_ax_su); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ac_su_txbf = %u", + htt_stats_buf->num_data_ppdus_ac_su_txbf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_data_ppdus_ax_su_txbf = %u\n", + htt_stats_buf->num_data_ppdus_ax_su_txbf); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER\n"); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_intr_misc_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_intr_misc_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_INTR_MISC_TLV:"); + memcpy(hw_intr_name, &(htt_stats_buf->hw_intr_name[0]), + HTT_STATS_MAX_HW_INTR_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_intr_name = %s ", hw_intr_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mask = %u", + htt_stats_buf->mask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_hw_stats_wd_timeout_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_wd_timeout_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WD_TIMEOUT_TLV:"); + memcpy(hw_module_name, &(htt_stats_buf->hw_module_name[0]), + HTT_STATS_MAX_HW_MODULE_NAME_LEN); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_module_name = %s ", + hw_module_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_pdev_errs_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_pdev_errs_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_PDEV_ERRS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort = %u", + htt_stats_buf->tx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_abort_fail_count = %u", + htt_stats_buf->tx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort = %u", + htt_stats_buf->rx_abort); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_abort_fail_count = %u", + htt_stats_buf->rx_abort_fail_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "warm_reset = %u", + htt_stats_buf->warm_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cold_reset = %u", + htt_stats_buf->cold_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flush = %u", + htt_stats_buf->tx_flush); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_glb_reset = %u", + htt_stats_buf->tx_glb_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_txq_reset = %u", + htt_stats_buf->tx_txq_reset); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_timeout_reset = %u\n", + htt_stats_buf->rx_timeout_reset); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_msdu_flow_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_msdu_flow_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_MSDU_FLOW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_update_timestamp = %u", + htt_stats_buf->last_update_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_add_timestamp = %u", + htt_stats_buf->last_add_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_remove_timestamp = %u", + htt_stats_buf->last_remove_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_processed_msdu_count = %u", + htt_stats_buf->total_processed_msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cur_msdu_count_in_flowq = %u", + htt_stats_buf->cur_msdu_count_in_flowq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_flow_no = %u", + htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0xF0000) >> + 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_rule = %u", + (htt_stats_buf->tx_flow_no__tid_num__drop_rule & 0x100000) >> + 20); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_enqueue_count = %u", + htt_stats_buf->last_cycle_enqueue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_dequeue_count = %u", + htt_stats_buf->last_cycle_dequeue_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_cycle_drop_count = %u", + htt_stats_buf->last_cycle_drop_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_drop_th = %u\n", + htt_stats_buf->current_drop_th); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_queued = %u", + htt_stats_buf->hw_queued); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hw_reaped = %u", + htt_stats_buf->hw_reaped); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdus_hw_filter = %u", + htt_stats_buf->mpdus_hw_filter); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u\n", + htt_stats_buf->block_module_id); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tid_stats_v1_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tid_stats_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TID_STATS_V1_TLV:"); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sched_pending = %u", + htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ppdu_in_hwq = %u", + (htt_stats_buf->num_sched_pending__num_ppdu_in_hwq & + 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_flags = 0x%x", + htt_stats_buf->tid_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_bytes = %u", + htt_stats_buf->max_qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_qdepth_n_msdus = %u", + htt_stats_buf->max_qdepth_n_msdus); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rsvd = %u", + htt_stats_buf->rsvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_bytes = %u", + htt_stats_buf->qdepth_bytes); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_msdu = %u", + htt_stats_buf->qdepth_num_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qdepth_num_mpdu = %u", + htt_stats_buf->qdepth_num_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_scheduled_tsmp = %u", + htt_stats_buf->last_scheduled_tsmp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_module_id = %u", + htt_stats_buf->pause_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_module_id = %u", + htt_stats_buf->block_module_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "allow_n_flags = 0x%x", + htt_stats_buf->allow_n_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sendn_frms_allowed = %u\n", + htt_stats_buf->sendn_frms_allowed); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_tid_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_tid_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tid_name[MAX_HTT_TID_NAME + 1] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_TID_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id__tid_num & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_num = %u", + (htt_stats_buf->sw_peer_id__tid_num & 0xFFFF0000) >> 16); + memcpy(tid_name, &(htt_stats_buf->tid_name[0]), MAX_HTT_TID_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tid_name = %s ", tid_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_in_reorder = %u", + htt_stats_buf->dup_in_reorder); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_outside_window = %u", + htt_stats_buf->dup_past_outside_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dup_past_within_window = %u", + htt_stats_buf->dup_past_within_window); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdesc_err_decrypt = %u\n", + htt_stats_buf->rxdesc_err_decrypt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_counter_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_counter_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char counter_name[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_COUNTER_TLV:"); + + ARRAY_TO_STRING(counter_name, + htt_stats_buf->counter_name, + HTT_MAX_COUNTER_NAME); + len += HTT_DBG_OUT(buf + len, buf_len - len, "counter_name = %s ", counter_name); + len += HTT_DBG_OUT(buf + len, buf_len - len, "count = %u\n", + htt_stats_buf->count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_stats_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_cnt = %u", + htt_stats_buf->ppdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt = %u", + htt_stats_buf->mpdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_cnt = %u", + htt_stats_buf->msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pause_bitmap = %u", + htt_stats_buf->pause_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "block_bitmap = %u", + htt_stats_buf->block_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_rssi = %d", + htt_stats_buf->rssi); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_count = %llu", + htt_stats_buf->peer_enqueued_count_low | + ((u64)htt_stats_buf->peer_enqueued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dequeued_count = %llu", + htt_stats_buf->peer_dequeued_count_low | + ((u64)htt_stats_buf->peer_dequeued_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dropped_count = %llu", + htt_stats_buf->peer_dropped_count_low | + ((u64)htt_stats_buf->peer_dropped_count_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "transmitted_ppdu_bytes = %llu", + htt_stats_buf->ppdu_transmitted_bytes_low | + ((u64)htt_stats_buf->ppdu_transmitted_bytes_high << 32)); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ttl_removed_count = %u", + htt_stats_buf->peer_ttl_removed_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "inactive_time = %u\n", + htt_stats_buf->inactive_time); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_peer_details_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_peer_details_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PEER_DETAILS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_type = %u", + htt_stats_buf->peer_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sw_peer_id = %u", + htt_stats_buf->sw_peer_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_pdev_ast_idx & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ast_idx = %u", + (htt_stats_buf->vdev_pdev_ast_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mac_addr = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->mac_addr.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF), + (htt_stats_buf->mac_addr.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_flags = 0x%x", + htt_stats_buf->peer_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_flags = 0x%x\n", + htt_stats_buf->qpeer_flags); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS]; + u8 j; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_su_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_su_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mu_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mu_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], + htt_stats_buf->tx_gi[j], + HTT_TX_PEER_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, + htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_peer_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_peer_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char *rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS]; + char *rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS]; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PEER_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PEER_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s\n", str_buf); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(rx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_MU_MIMO_MPDU_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_queued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_tried_usr = %u", + htt_stats_buf->mu_mimo_mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_failed_usr = %u", + htt_stats_buf->mu_mimo_mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdus_requeued_usr = %u", + htt_stats_buf->mu_mimo_mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_err_no_ba_usr = %u", + htt_stats_buf->mu_mimo_err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_mpdu_underrun_usr = %u", + htt_stats_buf->mu_mimo_mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ampdu_underrun_usr = %u\n", + htt_stats_buf->mu_mimo_ampdu_underrun_usr); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_mu_mimo_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_MU_MIMO_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u\n", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_stats_cmn_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_stats_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + /* TODO: HKDBG */ + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_STATS_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__hwq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwq_id = %u", + (htt_stats_buf->mac_id__hwq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "xretry = %u", + htt_stats_buf->xretry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_cnt = %u", + htt_stats_buf->underrun_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cnt = %u", + htt_stats_buf->flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "filt_cnt = %u", + htt_stats_buf->filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_bmap = %u", + htt_stats_buf->null_mpdu_bmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "user_ack_failure = %u", + htt_stats_buf->user_ack_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_id_proc = %u", + htt_stats_buf->sched_id_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "null_mpdu_tx_count = %u", + htt_stats_buf->null_mpdu_tx_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_bmap_not_recvd = %u", + htt_stats_buf->mpdu_bmap_not_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_bar = %u", + htt_stats_buf->num_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_tried_cnt = %u", + htt_stats_buf->mpdu_tried_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queued_cnt = %u", + htt_stats_buf->mpdu_queued_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_ack_fail_cnt = %u", + htt_stats_buf->mpdu_ack_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_filt_cnt = %u", + htt_stats_buf->mpdu_filt_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "false_mpdu_ack_count = %u", + htt_stats_buf->false_mpdu_ack_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_timeout = %u\n", + htt_stats_buf->txq_timeout); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_difs_latency_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_difs_latency_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS); + char difs_latency_hist[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_DIFS_LATENCY_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hist_intvl = %u", + htt_stats_buf->hist_intvl); + + ARRAY_TO_STRING(difs_latency_hist, htt_stats_buf->difs_latency_hist, + data_len); + len += HTT_DBG_OUT(buf + len, buf_len - len, "difs_latency_hist = %s\n", + difs_latency_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 data_len; + char cmd_result[HTT_MAX_STRING_LEN] = {0}; + + data_len = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_CMD_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_result, htt_stats_buf->cmd_result, data_len); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_result = %s\n", cmd_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_cmd_stall_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_cmd_stall_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char cmd_stall_status[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_CMD_STALL_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_HWQ_CMD_STALL_STATS_TLV_V:"); + + ARRAY_TO_STRING(cmd_stall_status, htt_stats_buf->cmd_stall_status, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmd_stall_status = %s\n", + cmd_stall_status); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_fes_result_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_fes_result_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u16 num_elems; + char fes_result[HTT_MAX_STRING_LEN] = {0}; + + num_elems = min_t(u16, (tag_len >> 2), HTT_TX_HWQ_MAX_FES_RESULT_STATS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_FES_RESULT_STATS_TLV_V:"); + + ARRAY_TO_STRING(fes_result, htt_stats_buf->fes_result, num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fes_result = %s\n", fes_result); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char tried_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = ((tag_len - + sizeof(htt_stats_buf->hist_bin_size)) >> 2); + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TRIED_MPDU_CNT_HIST_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "TRIED_MPDU_CNT_HIST_BIN_SIZE : %u", + htt_stats_buf->hist_bin_size); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(tried_mpdu_cnt_hist, + htt_stats_buf->tried_mpdu_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "tried_mpdu_cnt_hist = %s\n", + tried_mpdu_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_hwq_txop_used_cnt_hist_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char txop_used_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u32 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_HWQ_TXOP_USED_CNT_HIST_TLV_V:"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(txop_used_cnt_hist, + htt_stats_buf->txop_used_cnt_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txop_used_cnt_hist = %s\n", + txop_used_cnt_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_sounding_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + s32 i; + const struct htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + const u32 *cbf_20 = htt_stats_buf->cbf_20; + const u32 *cbf_40 = htt_stats_buf->cbf_40; + const u32 *cbf_80 = htt_stats_buf->cbf_80; + const u32 *cbf_160 = htt_stats_buf->cbf_160; + + if (htt_stats_buf->tx_sounding_mode == HTT_TX_AC_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } else if (htt_stats_buf->tx_sounding_mode == HTT_TX_AX_SOUNDING_MODE) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_20 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u ", + cbf_20[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_20[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_40 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_40[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_40[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_80 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_80[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_80[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_cbf_160 = IBF : %u, SU_SIFS : %u, SU_RBO : %u, MU_SIFS : %u, MU_RBO : %u", + cbf_160[HTT_IMPLICIT_TXBF_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS], + cbf_160[HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS]); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Sounding User %u = 20MHz: %u, 40MHz : %u, 80MHz: %u, 160MHz: %u ", + i, + htt_stats_buf->sounding[0], + htt_stats_buf->sounding[1], + htt_stats_buf->sounding[2], + htt_stats_buf->sounding[3]); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "su_bar = %u", + htt_stats_buf->su_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts = %u", + htt_stats_buf->rts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cts2self = %u", + htt_stats_buf->cts2self); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_null = %u", + htt_stats_buf->qos_null); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_1 = %u", + htt_stats_buf->delayed_bar_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_2 = %u", + htt_stats_buf->delayed_bar_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_3 = %u", + htt_stats_buf->delayed_bar_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_4 = %u", + htt_stats_buf->delayed_bar_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_5 = %u", + htt_stats_buf->delayed_bar_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_6 = %u", + htt_stats_buf->delayed_bar_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "delayed_bar_7 = %u\n", + htt_stats_buf->delayed_bar_7); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa = %u", + htt_stats_buf->ac_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp = %u", + htt_stats_buf->ac_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa = %u", + htt_stats_buf->ac_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp = %u", + htt_stats_buf->ac_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ac_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brpoll_3 = %u\n", + htt_stats_buf->ac_mu_mimo_brpoll_3); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa = %u", + htt_stats_buf->ax_su_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp = %u", + htt_stats_buf->ax_su_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa = %u", + htt_stats_buf->ax_mu_mimo_ndpa); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp = %u", + htt_stats_buf->ax_mu_mimo_ndp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_1 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_2 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_3 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_3); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_4 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_4); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_5 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_5); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_6 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_6); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brpoll_7 = %u", + htt_stats_buf->ax_mu_mimo_brpoll_7); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger = %u", + htt_stats_buf->ax_basic_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger = %u", + htt_stats_buf->ax_bsr_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger = %u", + htt_stats_buf->ax_mu_bar_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger = %u\n", + htt_stats_buf->ax_mu_rts_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ac_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ac_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AC_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndp_err = %u", + htt_stats_buf->ac_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_su_ndpa_err = %u", + htt_stats_buf->ac_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndpa_err = %u", + htt_stats_buf->ac_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_ndp_err = %u", + htt_stats_buf->ac_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp1_err = %u", + htt_stats_buf->ac_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp2_err = %u", + htt_stats_buf->ac_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_brp3_err = %u\n", + htt_stats_buf->ac_mu_mimo_brp3_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_selfgen_ax_err_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_selfgen_ax_err_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_SELFGEN_AX_ERR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndp_err = %u", + htt_stats_buf->ax_su_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_su_ndpa_err = %u", + htt_stats_buf->ax_su_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndpa_err = %u", + htt_stats_buf->ax_mu_mimo_ndpa_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_ndp_err = %u", + htt_stats_buf->ax_mu_mimo_ndp_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp1_err = %u", + htt_stats_buf->ax_mu_mimo_brp1_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp2_err = %u", + htt_stats_buf->ax_mu_mimo_brp2_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp3_err = %u", + htt_stats_buf->ax_mu_mimo_brp3_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp4_err = %u", + htt_stats_buf->ax_mu_mimo_brp4_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp5_err = %u", + htt_stats_buf->ax_mu_mimo_brp5_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp6_err = %u", + htt_stats_buf->ax_mu_mimo_brp6_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_brp7_err = %u", + htt_stats_buf->ax_mu_mimo_brp7_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_basic_trigger_err = %u", + htt_stats_buf->ax_basic_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_bsr_trigger_err = %u", + htt_stats_buf->ax_bsr_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_bar_trigger_err = %u", + htt_stats_buf->ax_mu_bar_trigger_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_rts_trigger_err = %u\n", + htt_stats_buf->ax_mu_rts_trigger_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_sch_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mu_mimo_sch_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 i; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_SCH_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_posted = %u", + htt_stats_buf->mu_mimo_sch_posted); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_sch_failed = %u", + htt_stats_buf->mu_mimo_sch_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mu_mimo_ppdu_posted = %u\n", + htt_stats_buf->mu_mimo_ppdu_posted); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "11ac MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ac_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax MU_MIMO SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_sch_nusers_%u = %u", + i, htt_stats_buf->ax_mu_mimo_sch_nusers[i]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\n11ax OFDMA SCH STATS:"); + + for (i = 0; i < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS; i++) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_ofdma_sch_nusers_%u = %u", + i, htt_stats_buf->ax_ofdma_sch_nusers[i]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_mpdu_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AC_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_MU_MIMO_AX_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (htt_stats_buf->tx_sched_mode == HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX) { + if (!htt_stats_buf->user_index) + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_AX_MU_OFDMA_MPDU_STATS:\n"); + + if (htt_stats_buf->user_index < HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_queued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_queued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_tried_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_tried_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_failed_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_failed_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdus_requeued_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdus_requeued_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_err_no_ba_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->err_no_ba_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_mpdu_underrun_usr_%u = %u", + htt_stats_buf->user_index, + htt_stats_buf->mpdu_underrun_usr); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_ofdma_ampdu_underrun_usr_%u = %u\n", + htt_stats_buf->user_index, + htt_stats_buf->ampdu_underrun_usr); + } + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_posted_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_posted_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_posted[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_POSTED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_posted, htt_stats_buf->sched_cmd_posted, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_posted = %s\n", + sched_cmd_posted); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_cmd_reaped_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_cmd_reaped_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_cmd_reaped[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), HTT_TX_PDEV_SCHED_TX_MODE_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SCHED_TXQ_CMD_REAPED_TLV_V:"); + + ARRAY_TO_STRING(sched_cmd_reaped, htt_stats_buf->sched_cmd_reaped, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_reaped = %s\n", + sched_cmd_reaped); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_order_su_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_order_su_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_order_su[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_order_su_num_entries = + min_t(u32, (tag_len >> 2), HTT_TX_PDEV_NUM_SCHED_ORDER_LOG); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_ORDER_SU_TLV_V:"); + + ARRAY_TO_STRING(sched_order_su, htt_stats_buf->sched_order_su, + sched_order_su_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_order_su = %s\n", + sched_order_su); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_sched_txq_sched_ineligibility_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sched_txq_sched_ineligibility_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char sched_ineligibility[HTT_MAX_STRING_LEN] = {0}; + /* each entry is u32, i.e. 4 bytes */ + u32 sched_ineligibility_num_entries = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_SCHED_TXQ_SCHED_INELIGIBILITY_V:"); + + ARRAY_TO_STRING(sched_ineligibility, htt_stats_buf->sched_ineligibility, + sched_ineligibility_num_entries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_ineligibility = %s\n", + sched_ineligibility); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_pdev_stats_sched_per_txq_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_stats_sched_per_txq_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_PDEV_STATS_SCHED_PER_TXQ_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__txq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txq_id = %u", + (htt_stats_buf->mac_id__txq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_policy = %u", + htt_stats_buf->sched_policy); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_posted_timestamp = %u", + htt_stats_buf->last_sched_cmd_posted_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "last_sched_cmd_compl_timestamp = %u", + htt_stats_buf->last_sched_cmd_compl_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_lwm_count = %u", + htt_stats_buf->sched_2_tac_lwm_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_2_tac_ring_full = %u", + htt_stats_buf->sched_2_tac_ring_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmd_post_failure = %u", + htt_stats_buf->sched_cmd_post_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_active_tids = %u", + htt_stats_buf->num_active_tids); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_ps_schedules = %u", + htt_stats_buf->num_ps_schedules); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_cmds_pending = %u", + htt_stats_buf->sched_cmds_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_register = %u", + htt_stats_buf->num_tid_register); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tid_unregister = %u", + htt_stats_buf->num_tid_unregister); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_qstats_queried = %u", + htt_stats_buf->num_qstats_queried); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qstats_update_pending = %u", + htt_stats_buf->qstats_update_pending); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_qstats_query_timestamp = %u", + htt_stats_buf->last_qstats_query_timestamp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_cmdq_full = %u", + htt_stats_buf->num_tqm_cmdq_full); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_de_sched_algo_trigger = %u", + htt_stats_buf->num_de_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_rt_sched_algo_trigger = %u", + htt_stats_buf->num_rt_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tqm_sched_algo_trigger = %u", + htt_stats_buf->num_tqm_sched_algo_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_sched = %u\n", + htt_stats_buf->notify_sched); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dur_based_sendn_term = %u\n", + htt_stats_buf->dur_based_sendn_term); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_stats_tx_sched_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_stats_tx_sched_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_STATS_TX_SCHED_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "current_timestamp = %u\n", + htt_stats_buf->current_timestamp); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_gen_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_gen_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char gen_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_GEN_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(gen_mpdu_end_reason, htt_stats_buf->gen_mpdu_end_reason, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_end_reason = %s\n", + gen_mpdu_end_reason); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_stats_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_end_reason[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_TX_TQM_MAX_LIST_MPDU_END_REASON); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_TQM_LIST_MPDU_STATS_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_end_reason, htt_stats_buf->list_mpdu_end_reason, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_end_reason = %s\n", + list_mpdu_end_reason); + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_list_mpdu_cnt_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_list_mpdu_cnt_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char list_mpdu_cnt_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), + HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_LIST_MPDU_CNT_TLV_V:"); + + ARRAY_TO_STRING(list_mpdu_cnt_hist, htt_stats_buf->list_mpdu_cnt_hist, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist = %s\n", + list_mpdu_cnt_hist); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_tqm_pdev_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_pdev_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_PDEV_STATS_TLV_V:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_count = %u", + htt_stats_buf->msdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_count = %u", + htt_stats_buf->mpdu_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu = %u", + htt_stats_buf->remove_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu = %u", + htt_stats_buf->remove_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl = %u", + htt_stats_buf->remove_msdu_ttl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_bar = %u", + htt_stats_buf->send_bar); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bar_sync = %u", + htt_stats_buf->bar_sync); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu = %u", + htt_stats_buf->notify_mpdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_trigger = %u", + htt_stats_buf->hwsch_trigger); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_tlv_proc = %u", + htt_stats_buf->ack_tlv_proc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_list_cmd = %u", + htt_stats_buf->gen_list_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_tried_cmd = %u", + htt_stats_buf->remove_mpdu_tried_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_ttl_cmd = %u", + htt_stats_buf->remove_msdu_ttl_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue = %u", + htt_stats_buf->enqueue); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueue_notify = %u", + htt_stats_buf->enqueue_notify); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_at_head = %u", + htt_stats_buf->notify_mpdu_at_head); + len += HTT_DBG_OUT(buf + len, buf_len - len, "notify_mpdu_state_valid = %u", + htt_stats_buf->notify_mpdu_state_valid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify1 = %u", + htt_stats_buf->sched_udp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_udp_notify2 = %u", + htt_stats_buf->sched_udp_notify2); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify1 = %u", + htt_stats_buf->sched_nonudp_notify1); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sched_nonudp_notify2 = %u\n", + htt_stats_buf->sched_nonudp_notify2); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmn_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "max_cmdq_id = %u", + htt_stats_buf->max_cmdq_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "list_mpdu_cnt_hist_intvl = %u", + htt_stats_buf->list_mpdu_cnt_hist_intvl); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu = %u", + htt_stats_buf->add_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty = %u", + htt_stats_buf->q_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty = %u", + htt_stats_buf->q_not_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drop_notification = %u", + htt_stats_buf->drop_notification); + len += HTT_DBG_OUT(buf + len, buf_len - len, "desc_threshold = %u\n", + htt_stats_buf->desc_threshold); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_error_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_error_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_ERROR_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_empty_failure = %u", + htt_stats_buf->q_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "q_not_empty_failure = %u", + htt_stats_buf->q_not_empty_failure); + len += HTT_DBG_OUT(buf + len, buf_len - len, "add_msdu_failure = %u\n", + htt_stats_buf->add_msdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_tqm_cmdq_status_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_tqm_cmdq_status_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_TQM_CMDQ_STATUS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__cmdq_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cmdq_id = %u\n", + (htt_stats_buf->mac_id__cmdq_id__word & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sync_cmd = %u", + htt_stats_buf->sync_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "write_cmd = %u", + htt_stats_buf->write_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "gen_mpdu_cmd = %u", + htt_stats_buf->gen_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_queue_stats_cmd = %u", + htt_stats_buf->mpdu_queue_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_head_info_cmd = %u", + htt_stats_buf->mpdu_head_info_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "msdu_flow_stats_cmd = %u", + htt_stats_buf->msdu_flow_stats_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_mpdu_cmd = %u", + htt_stats_buf->remove_mpdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "remove_msdu_cmd = %u", + htt_stats_buf->remove_msdu_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flush_cache_cmd = %u", + htt_stats_buf->flush_cache_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_mpduq_cmd = %u", + htt_stats_buf->update_mpduq_cmd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "update_msduq_cmd = %u\n", + htt_stats_buf->update_msduq_cmd); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_eapol_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_eapol_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_EAPOL_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m1_packets = %u", + htt_stats_buf->m1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m2_packets = %u", + htt_stats_buf->m2_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m3_packets = %u", + htt_stats_buf->m3_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "m4_packets = %u", + htt_stats_buf->m4_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g1_packets = %u", + htt_stats_buf->g1_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "g2_packets = %u\n", + htt_stats_buf->g2_packets); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_failed_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_failed_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_FAILED_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bss_peer_not_found = %u", + htt_stats_buf->ap_bss_peer_not_found); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ap_bcast_mcast_no_peer = %u", + htt_stats_buf->ap_bcast_mcast_no_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sta_delete_in_progress = %u", + htt_stats_buf->sta_delete_in_progress); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ibss_no_bss_peer = %u", + htt_stats_buf->ibss_no_bss_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_vdev_type = %u", + htt_stats_buf->invalid_vdev_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_ast_peer_entry = %u", + htt_stats_buf->invalid_ast_peer_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "peer_entry_invalid = %u", + htt_stats_buf->peer_entry_invalid); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ethertype_not_ip = %u", + htt_stats_buf->ethertype_not_ip); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eapol_lookup_failed = %u", + htt_stats_buf->eapol_lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qpeer_not_allow_data = %u", + htt_stats_buf->qpeer_not_allow_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_tid_override = %u", + htt_stats_buf->fse_tid_override); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ipv6_jumbogram_zero_length = %u", + htt_stats_buf->ipv6_jumbogram_zero_length); + len += HTT_DBG_OUT(buf + len, buf_len - len, "qos_to_non_qos_in_prog = %u\n", + htt_stats_buf->qos_to_non_qos_in_prog); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CLASSIFY_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arp_packets = %u", + htt_stats_buf->arp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "igmp_packets = %u", + htt_stats_buf->igmp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dhcp_packets = %u", + htt_stats_buf->dhcp_packets); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_inspected = %u", + htt_stats_buf->host_inspected); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_included = %u", + htt_stats_buf->htt_included); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_mcs = %u", + htt_stats_buf->htt_valid_mcs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_nss = %u", + htt_stats_buf->htt_valid_nss); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_preamble_type = %u", + htt_stats_buf->htt_valid_preamble_type); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_chainmask = %u", + htt_stats_buf->htt_valid_chainmask); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_guard_interval = %u", + htt_stats_buf->htt_valid_guard_interval); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_retries = %u", + htt_stats_buf->htt_valid_retries); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_bw_info = %u", + htt_stats_buf->htt_valid_bw_info); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_power = %u", + htt_stats_buf->htt_valid_power); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_key_flags = 0x%x", + htt_stats_buf->htt_valid_key_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_valid_no_encryption = %u", + htt_stats_buf->htt_valid_no_encryption); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_entry_count = %u", + htt_stats_buf->fse_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_be = %u", + htt_stats_buf->fse_priority_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_high = %u", + htt_stats_buf->fse_priority_high); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_priority_low = %u", + htt_stats_buf->fse_priority_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_be = %u", + htt_stats_buf->fse_traffic_ptrn_be); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_over_sub = %u", + htt_stats_buf->fse_traffic_ptrn_over_sub); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_bursty = %u", + htt_stats_buf->fse_traffic_ptrn_bursty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_interactive = %u", + htt_stats_buf->fse_traffic_ptrn_interactive); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_traffic_ptrn_periodic = %u", + htt_stats_buf->fse_traffic_ptrn_periodic); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_alloc = %u", + htt_stats_buf->fse_hwqueue_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_created = %u", + htt_stats_buf->fse_hwqueue_created); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_hwqueue_send_to_host = %u", + htt_stats_buf->fse_hwqueue_send_to_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mcast_entry = %u", + htt_stats_buf->mcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "bcast_entry = %u", + htt_stats_buf->bcast_entry); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_update_peer_cache = %u", + htt_stats_buf->htt_update_peer_cache); + len += HTT_DBG_OUT(buf + len, buf_len - len, "htt_learning_frame = %u", + htt_stats_buf->htt_learning_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fse_invalid_peer = %u", + htt_stats_buf->fse_invalid_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mec_notify = %u\n", + htt_stats_buf->mec_notify); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_classify_status_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_classify_status_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_CLASSIFY_STATUS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "eok = %u", + htt_stats_buf->eok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "classify_done = %u", + htt_stats_buf->classify_done); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lookup_failed = %u", + htt_stats_buf->lookup_failed); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_dhcp = %u", + htt_stats_buf->send_host_dhcp); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_mcast = %u", + htt_stats_buf->send_host_mcast); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host_unknown_dest = %u", + htt_stats_buf->send_host_unknown_dest); + len += HTT_DBG_OUT(buf + len, buf_len - len, "send_host = %u", + htt_stats_buf->send_host); + len += HTT_DBG_OUT(buf + len, buf_len - len, "status_invalid = %u\n", + htt_stats_buf->status_invalid); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_packets_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_packets_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_PACKETS_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "enqueued_pkts = %u", + htt_stats_buf->enqueued_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm = %u", + htt_stats_buf->to_tqm); + len += HTT_DBG_OUT(buf + len, buf_len - len, "to_tqm_bypass = %u\n", + htt_stats_buf->to_tqm_bypass); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_enqueue_discard_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_enqueue_discard_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_ENQUEUE_DISCARD_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "discarded_pkts = %u", + htt_stats_buf->discarded_pkts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_frames = %u", + htt_stats_buf->local_frames); + len += HTT_DBG_OUT(buf + len, buf_len - len, "is_ext_msdu = %u\n", + htt_stats_buf->is_ext_msdu); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_de_compl_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_compl_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_COMPL_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_dummy_frame = %u", + htt_stats_buf->tcl_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_dummy_frame = %u", + htt_stats_buf->tqm_dummy_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_notify_frame = %u", + htt_stats_buf->tqm_notify_frame); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw2wbm_enq = %u", + htt_stats_buf->fw2wbm_enq); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tqm_bypass_frame = %u\n", + htt_stats_buf->tqm_bypass_frame); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_fw2wbm_ring_full_hist_tlv(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_fw2wbm_ring_full_hist_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw2wbm_ring_full_hist[HTT_MAX_STRING_LEN] = {0}; + u16 num_elements = tag_len >> 2; + u32 required_buffer_size = HTT_MAX_PRINT_CHAR_PER_ELEM * num_elements; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_TX_DE_FW2WBM_RING_FULL_HIST_TLV"); + + if (required_buffer_size < HTT_MAX_STRING_LEN) { + ARRAY_TO_STRING(fw2wbm_ring_full_hist, + htt_stats_buf->fw2wbm_ring_full_hist, + num_elements); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "fw2wbm_ring_full_hist = %s\n", + fw2wbm_ring_full_hist); + } else { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "INSUFFICIENT PRINT BUFFER "); + } + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_tx_de_cmn_stats_tlv(const void *tag_buf, struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_de_cmn_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_DE_CMN_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl2fw_entry_count = %u", + htt_stats_buf->tcl2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "not_to_fw = %u", + htt_stats_buf->not_to_fw); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev_vdev_peer = %u", + htt_stats_buf->invalid_pdev_vdev_peer); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcl_res_invalid_addrx = %u", + htt_stats_buf->tcl_res_invalid_addrx); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm2fw_entry_count = %u", + htt_stats_buf->wbm2fw_entry_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "invalid_pdev = %u\n", + htt_stats_buf->invalid_pdev); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char low_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + char high_wm_hit_count[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr = %u", + htt_stats_buf->base_addr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_elems = %u", + htt_stats_buf->num_elems__prefetch_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_tail_idx = %u", + (htt_stats_buf->num_elems__prefetch_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_idx = %u", + htt_stats_buf->head_idx__tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_idx = %u", + (htt_stats_buf->head_idx__tail_idx & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_head_idx = %u", + htt_stats_buf->shadow_head_idx__shadow_tail_idx & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "shadow_tail_idx = %u", + (htt_stats_buf->shadow_head_idx__shadow_tail_idx & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_tail_incr = %u", + htt_stats_buf->num_tail_incr); + len += HTT_DBG_OUT(buf + len, buf_len - len, "lwm_thresh = %u", + htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwm_thresh = %u", + (htt_stats_buf->lwm_thresh__hwm_thresh & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "overrun_hit_count = %u", + htt_stats_buf->overrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "underrun_hit_count = %u", + htt_stats_buf->underrun_hit_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prod_blockwait_count = %u", + htt_stats_buf->prod_blockwait_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "cons_blockwait_count = %u", + htt_stats_buf->cons_blockwait_count); + + ARRAY_TO_STRING(low_wm_hit_count, htt_stats_buf->low_wm_hit_count, + HTT_STATS_LOW_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "low_wm_hit_count = %s ", + low_wm_hit_count); + + ARRAY_TO_STRING(high_wm_hit_count, htt_stats_buf->high_wm_hit_count, + HTT_STATS_HIGH_WM_BINS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "high_wm_hit_count = %s\n", + high_wm_hit_count); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_ring_if_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_ring_if_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RING_IF_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_user_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_user_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char dwords_used_by_user_n[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = tag_len >> 2; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_USER_TLV_V:"); + + ARRAY_TO_STRING(dwords_used_by_user_n, + htt_stats_buf->dwords_used_by_user_n, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dwords_used_by_user_n = %s\n", + dwords_used_by_user_n); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_client_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_client_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CLIENT_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "client_id = %u", + htt_stats_buf->client_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_min = %u", + htt_stats_buf->buf_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_max = %u", + htt_stats_buf->buf_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_busy = %u", + htt_stats_buf->buf_busy); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_alloc = %u", + htt_stats_buf->buf_alloc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_avail = %u", + htt_stats_buf->buf_avail); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_users = %u\n", + htt_stats_buf->num_users); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sfm_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sfm_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SFM_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "buf_total = %u", + htt_stats_buf->buf_total); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mem_empty = %u", + htt_stats_buf->mem_empty); + len += HTT_DBG_OUT(buf + len, buf_len - len, "deallocate_bufs = %u", + htt_stats_buf->deallocate_bufs); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_id = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "arena = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0xFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ep = %u", + (htt_stats_buf->mac_id__ring_id__arena__ep & 0x1000000) >> 24); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_lsb = 0x%x", + htt_stats_buf->base_addr_lsb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "base_addr_msb = 0x%x", + htt_stats_buf->base_addr_msb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ring_size = %u", + htt_stats_buf->ring_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "elem_size = %u", + htt_stats_buf->elem_size); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_avail_words = %u", + htt_stats_buf->num_avail_words__num_valid_words & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_valid_words = %u", + (htt_stats_buf->num_avail_words__num_valid_words & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "head_ptr = %u", + htt_stats_buf->head_ptr__tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tail_ptr = %u", + (htt_stats_buf->head_ptr__tail_ptr & 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "consumer_empty = %u", + htt_stats_buf->consumer_empty__producer_full & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "producer_full = %u", + (htt_stats_buf->consumer_empty__producer_full & + 0xFFFF0000) >> 16); + len += HTT_DBG_OUT(buf + len, buf_len - len, "prefetch_count = %u", + htt_stats_buf->prefetch_count__internal_tail_ptr & 0xFFFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "internal_tail_ptr = %u\n", + (htt_stats_buf->prefetch_count__internal_tail_ptr & + 0xFFFF0000) >> 16); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_sring_cmn_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_sring_cmn_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_SRING_CMN_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u\n", + htt_stats_buf->num_records); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + char *tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS]; + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + tx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_ldpc = %u", + htt_stats_buf->tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ac_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u", + htt_stats_buf->ax_mu_mimo_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_ldpc = %u", + htt_stats_buf->ofdma_tx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_success = %u", + htt_stats_buf->rts_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ack_rssi = %u", + htt_stats_buf->ack_rssi); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 11 Mbps: %u", + htt_stats_buf->tx_legacy_cck_rate[0], + htt_stats_buf->tx_legacy_cck_rate[1], + htt_stats_buf->tx_legacy_cck_rate[2], + htt_stats_buf->tx_legacy_cck_rate[3]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u", + htt_stats_buf->tx_legacy_ofdm_rate[0], + htt_stats_buf->tx_legacy_ofdm_rate[1], + htt_stats_buf->tx_legacy_ofdm_rate[2], + htt_stats_buf->tx_legacy_ofdm_rate[3], + htt_stats_buf->tx_legacy_ofdm_rate[4], + htt_stats_buf->tx_legacy_ofdm_rate[5], + htt_stats_buf->tx_legacy_ofdm_rate[6], + htt_stats_buf->tx_legacy_ofdm_rate[7]); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_mcs, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_nss, + HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ac_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ac_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ax_mu_mimo_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ax_mu_mimo_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->ofdma_tx_bw, + HTT_TX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_bw = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_stbc, + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_pream, + HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_pream = %s ", str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u", + htt_stats_buf->tx_he_ltf[1], + htt_stats_buf->tx_he_ltf[2], + htt_stats_buf->tx_he_ltf[3]); + + /* SU GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AC MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ac_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ac_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* AX MU-MIMO GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ax_mu_mimo_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ax_mu_mimo_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + /* DL OFDMA GI Stats */ + for (j = 0; j < HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(tx_gi[j], htt_stats_buf->ofdma_tx_gi[j], + HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofdma_tx_gi[%u] = %s ", + j, tx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->tx_dcm, + HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tx_dcm = %s\n", str_buf); + + for (j = 0; j < HTT_TX_PEER_STATS_NUM_GI_COUNTERS; j++) + kfree(tx_gi[j]); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u8 j; + char *rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + char *rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS]; + char str_buf[HTT_MAX_STRING_LEN] = {0}; + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + rssi_chain[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) + rx_gi[j] = kmalloc(HTT_MAX_STRING_LEN, GFP_ATOMIC); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "nsts = %u", + htt_stats_buf->nsts); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ldpc = %u", + htt_stats_buf->rx_ldpc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rts_cnt = %u", + htt_stats_buf->rts_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_mgmt = %u", + htt_stats_buf->rssi_mgmt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_data = %u", + htt_stats_buf->rssi_data); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_comb = %u", + htt_stats_buf->rssi_comb); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_in_dbm = %d", + htt_stats_buf->rssi_in_dbm); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_mcs, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_mcs = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_nss, + HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_nss = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_dcm, + HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_dcm = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_stbc, + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_stbc = %s ", str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_bw, + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_bw = %s ", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + ARRAY_TO_STRING(rssi_chain[j], htt_stats_buf->rssi_chain[j], + HTT_RX_PDEV_STATS_NUM_BW_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rssi_chain[%u] = %s ", + j, rssi_chain[j]); + } + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + ARRAY_TO_STRING(rx_gi[j], htt_stats_buf->rx_gi[j], + HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_gi[%u] = %s ", + j, rx_gi[j]); + } + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_pream, + HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_pream = %s", str_buf); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + kfree(rssi_chain[j]); + + for (j = 0; j < HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) + kfree(rx_gi[j]); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_su_ext = %u", + htt_stats_buf->rx_11ax_su_ext); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ac_mumimo = %u", + htt_stats_buf->rx_11ac_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_mumimo = %u", + htt_stats_buf->rx_11ax_mumimo); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_11ax_ofdma = %u", + htt_stats_buf->rx_11ax_ofdma); + len += HTT_DBG_OUT(buf + len, buf_len - len, "txbf = %u", + htt_stats_buf->txbf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_cck_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_cck_rate = %s ", + str_buf); + + memset(str_buf, 0x0, HTT_MAX_STRING_LEN); + ARRAY_TO_STRING(str_buf, htt_stats_buf->rx_legacy_ofdm_rate, + HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_legacy_ofdm_rate = %s ", + str_buf); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_low = %u", + htt_stats_buf->rx_active_dur_us_low); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", + htt_stats_buf->rx_active_dur_us_high); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_soc_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_SOC_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_reo_ring_data_msdu = %u", + htt_stats_buf->fw_reo_ring_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_bcmc = %u", + htt_stats_buf->fw_to_host_data_msdu_bcmc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_to_host_data_msdu_uc = %u", + htt_stats_buf->fw_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_remote_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_remote_free_buf_indication_cnt = %u", + htt_stats_buf->ofld_remote_free_buf_indication_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_buf_to_host_data_msdu_uc = %u", + htt_stats_buf->ofld_buf_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "reo_fw_ring_to_host_data_msdu_uc = %u", + htt_stats_buf->reo_fw_ring_to_host_data_msdu_uc); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_sw_ring_reap = %u", + htt_stats_buf->wbm_sw_ring_reap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_forward_to_host_cnt = %u", + htt_stats_buf->wbm_forward_to_host_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wbm_target_recycle_cnt = %u", + htt_stats_buf->wbm_target_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "target_refill_ring_recycle_cnt = %u", + htt_stats_buf->target_refill_ring_recycle_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_empty_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_empty_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_empty_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_EMPTY_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_empty_cnt, + htt_stats_buf->refill_ring_empty_cnt, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_empty_cnt = %s\n", + refill_ring_empty_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v *htt_stats_buf = + tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char rxdma_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_RXDMA_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_RXDMA_ERR_TLV_V:"); + + ARRAY_TO_STRING(rxdma_err_cnt, + htt_stats_buf->rxdma_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rxdma_err = %s\n", + rxdma_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char reo_err_cnt[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_REO_MAX_ERR_CODE); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REO_ERR_TLV_V:"); + + ARRAY_TO_STRING(reo_err_cnt, + htt_stats_buf->reo_err, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "reo_err = %s\n", + reo_err_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_reo_debug_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_reo_resource_stats_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_REO_RESOURCE_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sample_id = %u", + htt_stats_buf->sample_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_max = %u", + htt_stats_buf->total_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_avg = %u", + htt_stats_buf->total_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_sample = %u", + htt_stats_buf->total_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_avg = %u", + htt_stats_buf->non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "non_zeros_sample = %u", + htt_stats_buf->non_zeros_sample); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_max = %u", + htt_stats_buf->last_non_zeros_max); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_min %u", + htt_stats_buf->last_non_zeros_min); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_avg %u", + htt_stats_buf->last_non_zeros_avg); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_non_zeros_sample %u\n", + htt_stats_buf->last_non_zeros_sample); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char refill_ring_num_refill[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_REFILL_MAX_RING); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_SOC_FW_REFILL_RING_NUM_REFILL_TLV_V:"); + + ARRAY_TO_STRING(refill_ring_num_refill, + htt_stats_buf->refill_ring_num_refill, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "refill_ring_num_refill = %s\n", + refill_ring_num_refill); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_rx_pdev_fw_stats_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mgmt_subtype[HTT_MAX_STRING_LEN] = {0}; + char fw_ring_ctrl_subtype[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ppdu_recvd = %u", + htt_stats_buf->ppdu_recvd); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_ok = %u", + htt_stats_buf->mpdu_cnt_fcs_ok); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mpdu_cnt_fcs_err = %u", + htt_stats_buf->mpdu_cnt_fcs_err); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_msdu_cnt = %u", + htt_stats_buf->tcp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "tcp_ack_msdu_cnt = %u", + htt_stats_buf->tcp_ack_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "udp_msdu_cnt = %u", + htt_stats_buf->udp_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "other_msdu_cnt = %u", + htt_stats_buf->other_msdu_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_ind = %u", + htt_stats_buf->fw_ring_mpdu_ind); + + ARRAY_TO_STRING(fw_ring_mgmt_subtype, + htt_stats_buf->fw_ring_mgmt_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mgmt_subtype = %s ", + fw_ring_mgmt_subtype); + + ARRAY_TO_STRING(fw_ring_ctrl_subtype, + htt_stats_buf->fw_ring_ctrl_subtype, + HTT_STATS_SUBTYPE_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ctrl_subtype = %s ", + fw_ring_ctrl_subtype); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mcast_data_msdu = %u", + htt_stats_buf->fw_ring_mcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_bcast_data_msdu = %u", + htt_stats_buf->fw_ring_bcast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_ucast_data_msdu = %u", + htt_stats_buf->fw_ring_ucast_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_null_data_msdu = %u", + htt_stats_buf->fw_ring_null_data_msdu); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_drop = %u", + htt_stats_buf->fw_ring_mpdu_drop); + len += HTT_DBG_OUT(buf + len, buf_len - len, "ofld_local_data_ind_cnt = %u", + htt_stats_buf->ofld_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "ofld_local_data_buf_recycle_cnt = %u", + htt_stats_buf->ofld_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "drx_local_data_ind_cnt = %u", + htt_stats_buf->drx_local_data_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "drx_local_data_buf_recycle_cnt = %u", + htt_stats_buf->drx_local_data_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_ind_cnt = %u", + htt_stats_buf->local_nondata_ind_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "local_nondata_buf_recycle_cnt = %u", + htt_stats_buf->local_nondata_buf_recycle_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_status_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_refill_cnt = %u", + htt_stats_buf->fw_link_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_link_buf_ring_empty_cnt = %u", + htt_stats_buf->fw_link_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "host_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->host_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_pkt_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_pkt_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "mon_status_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_status_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_status_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_status_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_refill_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_refill_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_desc_buf_ring_empty_cnt = %u", + htt_stats_buf->mon_desc_buf_ring_empty_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_update_cnt = %u", + htt_stats_buf->mon_dest_ring_update_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mon_dest_ring_full_cnt = %u", + htt_stats_buf->mon_dest_ring_full_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_cnt = %u", + htt_stats_buf->rx_suspend_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_suspend_fail_cnt = %u", + htt_stats_buf->rx_suspend_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_cnt = %u", + htt_stats_buf->rx_resume_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_resume_fail_cnt = %u", + htt_stats_buf->rx_resume_fail_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_switch_cnt = %u", + htt_stats_buf->rx_ring_switch_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_ring_restore_cnt = %u", + htt_stats_buf->rx_ring_restore_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_flush_cnt = %u", + htt_stats_buf->rx_flush_cnt); + len += HTT_DBG_OUT(buf + len, buf_len - len, "rx_recovery_reset_cnt = %u\n", + htt_stats_buf->rx_recovery_reset_cnt); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_ring_mpdu_err[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_RX_PDEV_FW_RING_MPDU_ERR_TLV_V:"); + + ARRAY_TO_STRING(fw_ring_mpdu_err, + htt_stats_buf->fw_ring_mpdu_err, + HTT_RX_STATS_RXDMA_MAX_ERR); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_ring_mpdu_err = %s\n", + fw_ring_mpdu_err); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_mpdu_drop_tlv_v(const void *tag_buf, + u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_mpdu_drop_tlv_v *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char fw_mpdu_drop[HTT_MAX_STRING_LEN] = {0}; + u16 num_elems = min_t(u16, (tag_len >> 2), HTT_RX_STATS_FW_DROP_REASON_MAX); + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_MPDU_DROP_TLV_V:"); + + ARRAY_TO_STRING(fw_mpdu_drop, + htt_stats_buf->fw_mpdu_drop, + num_elems); + len += HTT_DBG_OUT(buf + len, buf_len - len, "fw_mpdu_drop = %s\n", fw_mpdu_drop); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_rx_pdev_fw_stats_phy_err_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_rx_pdev_fw_stats_phy_err_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + char phy_errs[HTT_MAX_STRING_LEN] = {0}; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_RX_PDEV_FW_STATS_PHY_ERR_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id__word = %u", + htt_stats_buf->mac_id__word); + len += HTT_DBG_OUT(buf + len, buf_len - len, "total_phy_err_nct = %u", + htt_stats_buf->total_phy_err_cnt); + + ARRAY_TO_STRING(phy_errs, + htt_stats_buf->phy_err, + HTT_STATS_PHY_ERR_MAX); + len += HTT_DBG_OUT(buf + len, buf_len - len, "phy_errs = %s\n", phy_errs); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_cca_stats_hist_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_cca_stats_hist_v1_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "\nHTT_PDEV_CCA_STATS_HIST_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "chan_num = %u", + htt_stats_buf->chan_num); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_records = %u", + htt_stats_buf->num_records); + len += HTT_DBG_OUT(buf + len, buf_len - len, "valid_cca_counters_bitmap = 0x%x", + htt_stats_buf->valid_cca_counters_bitmap); + len += HTT_DBG_OUT(buf + len, buf_len - len, "collection_interval = %u\n", + htt_stats_buf->collection_interval); + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HTT_PDEV_STATS_CCA_COUNTERS_TLV:(in usec)"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "| tx_frame| rx_frame| rx_clear| my_rx_frame| cnt| med_rx_idle| med_tx_idle_global| cca_obss|"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_cca_counters_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, + "|%10u| %10u| %10u| %11u| %10u| %11u| %18u| %10u|", + htt_stats_buf->tx_frame_usec, + htt_stats_buf->rx_frame_usec, + htt_stats_buf->rx_clear_usec, + htt_stats_buf->my_rx_frame_usec, + htt_stats_buf->usec_cnt, + htt_stats_buf->med_rx_idle_usec, + htt_stats_buf->med_tx_idle_global_usec, + htt_stats_buf->cca_obss_usec); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_print_hw_stats_whal_tx_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_hw_stats_whal_tx_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_HW_STATS_WHAL_TX_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "mac_id = %u", + htt_stats_buf->mac_id__word & 0xFF); + len += HTT_DBG_OUT(buf + len, buf_len - len, "last_unpause_ppdu_id = %u", + htt_stats_buf->last_unpause_ppdu_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_unpause_wait_tqm_write = %u", + htt_stats_buf->hwsch_unpause_wait_tqm_write); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dummy_tlv_skipped = %u", + htt_stats_buf->hwsch_dummy_tlv_skipped); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "hwsch_misaligned_offset_received = %u", + htt_stats_buf->hwsch_misaligned_offset_received); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_reset_count = %u", + htt_stats_buf->hwsch_reset_count); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_dev_reset_war = %u", + htt_stats_buf->hwsch_dev_reset_war); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_delayed_pause = %u", + htt_stats_buf->hwsch_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "hwsch_long_delayed_pause = %u", + htt_stats_buf->hwsch_long_delayed_pause); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_ppdu_no_response = %u", + htt_stats_buf->sch_rx_ppdu_no_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_selfgen_response = %u", + htt_stats_buf->sch_selfgen_response); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sch_rx_sifs_resp_trigger= %u\n", + htt_stats_buf->sch_rx_sifs_resp_trigger); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_sessions_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_sessions_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSIONS_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "pdev_id = %u", + htt_stats_buf->pdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "num_sessions = %u\n", + htt_stats_buf->num_sessions); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_stats_twt_session_tlv(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_stats_twt_session_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "HTT_PDEV_STATS_TWT_SESSION_TLV:"); + len += HTT_DBG_OUT(buf + len, buf_len - len, "vdev_id = %u", + htt_stats_buf->vdev_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "peer_mac = %02x:%02x:%02x:%02x:%02x:%02x", + htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF00) >> 8, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF0000) >> 16, + (htt_stats_buf->peer_mac.mac_addr_l32 & 0xFF000000) >> 24, + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF), + (htt_stats_buf->peer_mac.mac_addr_h16 & 0xFF00) >> 8); + len += HTT_DBG_OUT(buf + len, buf_len - len, "flow_id_flags = %u", + htt_stats_buf->flow_id_flags); + len += HTT_DBG_OUT(buf + len, buf_len - len, "dialog_id = %u", + htt_stats_buf->dialog_id); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_dura_us = %u", + htt_stats_buf->wake_dura_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "wake_intvl_us = %u", + htt_stats_buf->wake_intvl_us); + len += HTT_DBG_OUT(buf + len, buf_len - len, "sp_offset_us = %u\n", + htt_stats_buf->sp_offset_us); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void +htt_print_pdev_obss_pd_stats_tlv_v(const void *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + const struct htt_pdev_obss_pd_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + + len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx success PPDU = %u", + htt_stats_buf->num_obss_tx_ppdu_success); + len += HTT_DBG_OUT(buf + len, buf_len - len, "OBSS Tx failures PPDU = %u\n", + htt_stats_buf->num_obss_tx_ppdu_failure); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static inline void htt_htt_stats_debug_dump(const u32 *tag_buf, + struct debug_htt_stats_req *stats_req) +{ + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH11K_HTT_STATS_BUF_SIZE; + u32 tlv_len = 0, i = 0, word_len = 0; + + tlv_len = FIELD_GET(HTT_TLV_LEN, *tag_buf) + HTT_TLV_HDR_LEN; + word_len = (tlv_len % 4) == 0 ? (tlv_len / 4) : ((tlv_len / 4) + 1); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + len += HTT_DBG_OUT(buf + len, buf_len - len, + "HKDBG TLV DUMP: (tag_len=%u bytes, words=%u)", + tlv_len, word_len); + + for (i = 0; i + 3 < word_len; i += 4) { + len += HTT_DBG_OUT(buf + len, buf_len - len, + "0x%08x 0x%08x 0x%08x 0x%08x", + tag_buf[i], tag_buf[i + 1], + tag_buf[i + 2], tag_buf[i + 3]); + } + + if (i + 3 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1], tag_buf[i + 2]); + } else if (i + 2 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x 0x%08x ", + tag_buf[i], tag_buf[i + 1]); + } else if (i + 1 == word_len) { + len += HTT_DBG_OUT(buf + len, buf_len - len, "0x%08x ", + tag_buf[i]); + } + len += HTT_DBG_OUT(buf + len, buf_len - len, + "============================================"); + + if (len >= buf_len) + buf[buf_len - 1] = 0; + else + buf[len] = 0; + + stats_req->buf_len = len; +} + +static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *tag_buf, + void *user_data) +{ + struct debug_htt_stats_req *stats_req = user_data; + + switch (tag) { + case HTT_STATS_TX_PDEV_CMN_TAG: + htt_print_tx_pdev_stats_cmn_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_PDEV_UNDERRUN_TAG: + htt_print_tx_pdev_stats_urrn_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_TAG: + htt_print_tx_pdev_stats_sifs_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_FLUSH_TAG: + htt_print_tx_pdev_stats_flush_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_PHY_ERR_TAG: + htt_print_tx_pdev_stats_phy_err_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_PDEV_SIFS_HIST_TAG: + htt_print_tx_pdev_stats_sifs_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG: + htt_print_tx_pdev_stats_tx_ppdu_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, + stats_req); + break; + + case HTT_STATS_STRING_TAG: + htt_print_stats_string_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMN_TAG: + htt_print_tx_hwq_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG: + htt_print_tx_hwq_difs_latency_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_RESULT_TAG: + htt_print_tx_hwq_cmd_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_CMD_STALL_TAG: + htt_print_tx_hwq_cmd_stall_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_FES_STATUS_TAG: + htt_print_tx_hwq_fes_result_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG: + htt_print_tx_hwq_tried_mpdu_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG: + htt_print_tx_hwq_txop_used_cnt_hist_tlv_v(tag_buf, len, stats_req); + break; + case HTT_STATS_TX_TQM_GEN_MPDU_TAG: + htt_print_tx_tqm_gen_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_TAG: + htt_print_tx_tqm_list_mpdu_stats_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG: + htt_print_tx_tqm_list_mpdu_cnt_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_TQM_CMN_TAG: + htt_print_tx_tqm_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_PDEV_TAG: + htt_print_tx_tqm_pdev_stats_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_CMDQ_STATUS_TAG: + htt_print_tx_tqm_cmdq_status_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_EAPOL_PACKETS_TAG: + htt_print_tx_de_eapol_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG: + htt_print_tx_de_classify_failed_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATS_TAG: + htt_print_tx_de_classify_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG: + htt_print_tx_de_classify_status_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG: + htt_print_tx_de_enqueue_packets_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG: + htt_print_tx_de_enqueue_discard_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG: + htt_print_tx_de_fw2wbm_ring_full_hist_tlv(tag_buf, len, stats_req); + break; + + case HTT_STATS_TX_DE_CMN_TAG: + htt_print_tx_de_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RING_IF_TAG: + htt_print_ring_if_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG: + htt_print_tx_pdev_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CMN_TAG: + htt_print_sfm_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SRING_STATS_TAG: + htt_print_sring_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_STATS_TAG: + htt_print_rx_pdev_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG: + htt_print_rx_pdev_fw_ring_mpdu_err_tlv_v(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG: + htt_print_rx_pdev_fw_mpdu_drop_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_STATS_TAG: + htt_print_rx_soc_fw_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG: + htt_print_rx_soc_fw_refill_ring_empty_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG: + htt_print_rx_soc_fw_refill_ring_num_refill_tlv_v( + tag_buf, len, stats_req); + break; + case HTT_STATS_RX_REFILL_RXDMA_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REFILL_REO_ERR_TAG: + htt_print_rx_soc_fw_refill_ring_num_reo_err_tlv_v( + tag_buf, len, stats_req); + break; + + case HTT_STATS_RX_REO_RESOURCE_STATS_TAG: + htt_print_rx_reo_debug_stats_tlv_v( + tag_buf, stats_req); + break; + case HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG: + htt_print_rx_pdev_fw_stats_phy_err_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + htt_print_tx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + htt_print_rx_pdev_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG: + htt_print_tx_pdev_stats_sched_per_txq_tlv(tag_buf, stats_req); + break; + case HTT_STATS_TX_SCHED_CMN_TAG: + htt_print_stats_tx_sched_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_PDEV_MPDU_STATS_TAG: + htt_print_tx_pdev_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG: + htt_print_sched_txq_cmd_posted_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_RING_IF_CMN_TAG: + htt_print_ring_if_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_USER_TAG: + htt_print_sfm_client_user_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SFM_CLIENT_TAG: + htt_print_sfm_client_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TQM_ERROR_STATS_TAG: + htt_print_tx_tqm_error_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG: + htt_print_sched_txq_cmd_reaped_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SRING_CMN_TAG: + htt_print_sring_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SOUNDING_STATS_TAG: + htt_print_tx_sounding_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG: + htt_print_tx_selfgen_ac_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_CMN_STATS_TAG: + htt_print_tx_selfgen_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AC_STATS_TAG: + htt_print_tx_selfgen_ac_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_STATS_TAG: + htt_print_tx_selfgen_ax_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG: + htt_print_tx_selfgen_ax_err_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG: + htt_print_tx_hwq_mu_mimo_sch_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG: + htt_print_tx_hwq_mu_mimo_mpdu_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG: + htt_print_tx_hwq_mu_mimo_cmn_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_INTR_MISC_TAG: + htt_print_hw_stats_intr_misc_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_WD_TIMEOUT_TAG: + htt_print_hw_stats_wd_timeout_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_HW_PDEV_ERRS_TAG: + htt_print_hw_stats_pdev_errs_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_COUNTER_NAME_TAG: + htt_print_counter_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_TAG: + htt_print_tx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_TID_DETAILS_V1_TAG: + htt_print_tx_tid_stats_v1_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_RX_TID_DETAILS_TAG: + htt_print_rx_tid_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_STATS_CMN_TAG: + htt_print_peer_stats_cmn_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_DETAILS_TAG: + htt_print_peer_details_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_MSDU_FLOWQ_TAG: + htt_print_msdu_flow_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_TX_RATE_STATS_TAG: + htt_print_tx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PEER_RX_RATE_STATS_TAG: + htt_print_rx_peer_rate_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_TX_DE_COMPL_STATS_TAG: + htt_print_tx_de_compl_stats_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_1SEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG: + case HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG: + htt_print_pdev_cca_stats_hist_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_CCA_COUNTERS_TAG: + htt_print_pdev_stats_cca_counters_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_WHAL_TX_TAG: + htt_print_hw_stats_whal_tx_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSIONS_TAG: + htt_print_pdev_stats_twt_sessions_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_PDEV_TWT_SESSION_TAG: + htt_print_pdev_stats_twt_session_tlv(tag_buf, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG: + htt_print_sched_txq_sched_order_su_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG: + htt_print_sched_txq_sched_ineligibility_tlv_v(tag_buf, len, stats_req); + break; + + case HTT_STATS_PDEV_OBSS_PD_TAG: + htt_print_pdev_obss_pd_stats_tlv_v(tag_buf, stats_req); + break; + default: + break; + } + + return 0; +} + +void ath11k_dbg_htt_ext_stats_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_extd_stats_msg *msg; + struct debug_htt_stats_req *stats_req; + struct ath11k *ar; + u32 len; + u64 cookie; + int ret; + u8 pdev_id; + + msg = (struct ath11k_htt_extd_stats_msg *)skb->data; + cookie = msg->cookie; + + if (FIELD_GET(HTT_STATS_COOKIE_MSB, cookie) != HTT_STATS_MAGIC_VALUE) { + ath11k_warn(ab, "received invalid htt ext stats event\n"); + return; + } + + pdev_id = FIELD_GET(HTT_STATS_COOKIE_LSB, cookie); + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + rcu_read_unlock(); + if (!ar) { + ath11k_warn(ab, "failed to get ar for pdev_id %d\n", pdev_id); + return; + } + + stats_req = ar->debug.htt_stats.stats_req; + if (!stats_req) + return; + + spin_lock_bh(&ar->debug.htt_stats.lock); + if (stats_req->done) { + spin_unlock_bh(&ar->debug.htt_stats.lock); + return; + } + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + + len = FIELD_GET(HTT_T2H_EXT_STATS_INFO1_LENGTH, msg->info1); + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + + complete(&stats_req->cmpln); +} + +static ssize_t ath11k_read_htt_stats_type(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.type); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS) + return -E2BIG; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET || + type == ATH11K_DBG_HTT_EXT_STATS_PEER_INFO) + return -EPERM; + + ar->debug.htt_stats.type = type; + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_type = { + .read = ath11k_read_htt_stats_type, + .write = ath11k_write_htt_stats_type, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath11k_prep_htt_stats_cfg_params(struct ath11k *ar, u8 type, + const u8 *mac_addr, + struct htt_ext_stats_cfg_params *cfg_params) +{ + if (!cfg_params) + return -EINVAL; + + switch (type) { + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ: + case ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_HWQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_TXQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_CMDQS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PEER_INFO: + cfg_params->cfg0 = HTT_STAT_PEER_INFO_MAC_ADDR; + cfg_params->cfg0 |= FIELD_PREP(GENMASK(15, 1), + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM); + cfg_params->cfg1 = HTT_STAT_DEFAULT_PEER_REQ_TYPE; + cfg_params->cfg2 |= FIELD_PREP(GENMASK(7, 0), mac_addr[0]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(15, 8), mac_addr[1]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(23, 16), mac_addr[2]); + cfg_params->cfg2 |= FIELD_PREP(GENMASK(31, 24), mac_addr[3]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(7, 0), mac_addr[4]); + cfg_params->cfg3 |= FIELD_PREP(GENMASK(15, 8), mac_addr[5]); + break; + case ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO: + case ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ALL_RINGS; + break; + case ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS; + break; + case ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE; + break; + case ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO: + cfg_params->cfg0 = HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS; + break; + default: + break; + } + + return 0; +} + +int ath11k_dbg_htt_stats_req(struct ath11k *ar) +{ + struct debug_htt_stats_req *stats_req = ar->debug.htt_stats.stats_req; + u8 type = stats_req->type; + u64 cookie = 0; + int ret, pdev_id = ar->pdev->pdev_id; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + + init_completion(&stats_req->cmpln); + + stats_req->done = false; + stats_req->pdev_id = pdev_id; + + cookie = FIELD_PREP(HTT_STATS_COOKIE_MSB, HTT_STATS_MAGIC_VALUE) | + FIELD_PREP(HTT_STATS_COOKIE_LSB, pdev_id); + + ret = ath11k_prep_htt_stats_cfg_params(ar, type, stats_req->peer_addr, + &cfg_params); + if (ret) { + ath11k_warn(ar->ab, "failed to set htt stats cfg params: %d\n", ret); + return ret; + } + + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, type, &cfg_params, cookie); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + return ret; + } + + while (!wait_for_completion_timeout(&stats_req->cmpln, 3 * HZ)) { + spin_lock_bh(&ar->debug.htt_stats.lock); + if (!stats_req->done) { + stats_req->done = true; + spin_unlock_bh(&ar->debug.htt_stats.lock); + ath11k_warn(ar->ab, "stats request timed out\n"); + return -ETIMEDOUT; + } + spin_unlock_bh(&ar->debug.htt_stats.lock); + } + + return 0; +} + +static int ath11k_open_htt_stats(struct inode *inode, struct file *file) +{ + struct ath11k *ar = inode->i_private; + struct debug_htt_stats_req *stats_req; + u8 type = ar->debug.htt_stats.type; + int ret; + + if (type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -EPERM; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = type; + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int ath11k_release_htt_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_read_htt_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_dump_htt_stats = { + .open = ath11k_open_htt_stats, + .release = ath11k_release_htt_stats, + .read = ath11k_read_htt_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_read_htt_stats_reset(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "%u\n", ar->debug.htt_stats.reset); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath11k_write_htt_stats_reset(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + u8 type; + struct htt_ext_stats_cfg_params cfg_params = { 0 }; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= ATH11K_DBG_HTT_NUM_EXT_STATS || + type == ATH11K_DBG_HTT_EXT_STATS_RESET) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + cfg_params.cfg0 = HTT_STAT_DEFAULT_RESET_START_OFFSET; + cfg_params.cfg1 = 1 << (cfg_params.cfg0 + type); + ret = ath11k_dp_tx_htt_h2t_ext_stats_req(ar, + ATH11K_DBG_HTT_EXT_STATS_RESET, + &cfg_params, + 0ULL); + if (ret) { + ath11k_warn(ar->ab, "failed to send htt stats request: %d\n", ret); + mutex_unlock(&ar->conf_mutex); + return ret; + } + + ar->debug.htt_stats.reset = type; + mutex_unlock(&ar->conf_mutex); + + ret = count; + + return ret; +} + +static const struct file_operations fops_htt_stats_reset = { + .read = ath11k_read_htt_stats_reset, + .write = ath11k_write_htt_stats_reset, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar) +{ + spin_lock_init(&ar->debug.htt_stats.lock); + debugfs_create_file("htt_stats_type", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_type); + debugfs_create_file("htt_stats", 0400, ar->debug.debugfs_pdev, + ar, &fops_dump_htt_stats); + debugfs_create_file("htt_stats_reset", 0600, ar->debug.debugfs_pdev, + ar, &fops_htt_stats_reset); +} diff --git a/drivers/net/wireless/ath/ath11k/debug_htt_stats.h b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h new file mode 100644 index 000000000000..618f1946bf49 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debug_htt_stats.h @@ -0,0 +1,1620 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef DEBUG_HTT_STATS_H +#define DEBUG_HTT_STATS_H + +#define HTT_STATS_COOKIE_LSB GENMASK_ULL(31, 0) +#define HTT_STATS_COOKIE_MSB GENMASK_ULL(63, 32) +#define HTT_STATS_MAGIC_VALUE 0xF0F0F0F0 + +enum htt_tlv_tag_t { + HTT_STATS_TX_PDEV_CMN_TAG = 0, + HTT_STATS_TX_PDEV_UNDERRUN_TAG = 1, + HTT_STATS_TX_PDEV_SIFS_TAG = 2, + HTT_STATS_TX_PDEV_FLUSH_TAG = 3, + HTT_STATS_TX_PDEV_PHY_ERR_TAG = 4, + HTT_STATS_STRING_TAG = 5, + HTT_STATS_TX_HWQ_CMN_TAG = 6, + HTT_STATS_TX_HWQ_DIFS_LATENCY_TAG = 7, + HTT_STATS_TX_HWQ_CMD_RESULT_TAG = 8, + HTT_STATS_TX_HWQ_CMD_STALL_TAG = 9, + HTT_STATS_TX_HWQ_FES_STATUS_TAG = 10, + HTT_STATS_TX_TQM_GEN_MPDU_TAG = 11, + HTT_STATS_TX_TQM_LIST_MPDU_TAG = 12, + HTT_STATS_TX_TQM_LIST_MPDU_CNT_TAG = 13, + HTT_STATS_TX_TQM_CMN_TAG = 14, + HTT_STATS_TX_TQM_PDEV_TAG = 15, + HTT_STATS_TX_TQM_CMDQ_STATUS_TAG = 16, + HTT_STATS_TX_DE_EAPOL_PACKETS_TAG = 17, + HTT_STATS_TX_DE_CLASSIFY_FAILED_TAG = 18, + HTT_STATS_TX_DE_CLASSIFY_STATS_TAG = 19, + HTT_STATS_TX_DE_CLASSIFY_STATUS_TAG = 20, + HTT_STATS_TX_DE_ENQUEUE_PACKETS_TAG = 21, + HTT_STATS_TX_DE_ENQUEUE_DISCARD_TAG = 22, + HTT_STATS_TX_DE_CMN_TAG = 23, + HTT_STATS_RING_IF_TAG = 24, + HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, + HTT_STATS_SFM_CMN_TAG = 26, + HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_RX_PDEV_FW_STATS_TAG = 28, + HTT_STATS_RX_PDEV_FW_RING_MPDU_ERR_TAG = 29, + HTT_STATS_RX_PDEV_FW_MPDU_DROP_TAG = 30, + HTT_STATS_RX_SOC_FW_STATS_TAG = 31, + HTT_STATS_RX_SOC_FW_REFILL_RING_EMPTY_TAG = 32, + HTT_STATS_RX_SOC_FW_REFILL_RING_NUM_REFILL_TAG = 33, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, + HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, + HTT_STATS_TX_SCHED_CMN_TAG = 37, + HTT_STATS_TX_PDEV_MUMIMO_MPDU_STATS_TAG = 38, + HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, + HTT_STATS_RING_IF_CMN_TAG = 40, + HTT_STATS_SFM_CLIENT_USER_TAG = 41, + HTT_STATS_SFM_CLIENT_TAG = 42, + HTT_STATS_TX_TQM_ERROR_STATS_TAG = 43, + HTT_STATS_SCHED_TXQ_CMD_REAPED_TAG = 44, + HTT_STATS_SRING_CMN_TAG = 45, + HTT_STATS_TX_SELFGEN_AC_ERR_STATS_TAG = 46, + HTT_STATS_TX_SELFGEN_CMN_STATS_TAG = 47, + HTT_STATS_TX_SELFGEN_AC_STATS_TAG = 48, + HTT_STATS_TX_SELFGEN_AX_STATS_TAG = 49, + HTT_STATS_TX_SELFGEN_AX_ERR_STATS_TAG = 50, + HTT_STATS_TX_HWQ_MUMIMO_SCH_STATS_TAG = 51, + HTT_STATS_TX_HWQ_MUMIMO_MPDU_STATS_TAG = 52, + HTT_STATS_TX_HWQ_MUMIMO_CMN_STATS_TAG = 53, + HTT_STATS_HW_INTR_MISC_TAG = 54, + HTT_STATS_HW_WD_TIMEOUT_TAG = 55, + HTT_STATS_HW_PDEV_ERRS_TAG = 56, + HTT_STATS_COUNTER_NAME_TAG = 57, + HTT_STATS_TX_TID_DETAILS_TAG = 58, + HTT_STATS_RX_TID_DETAILS_TAG = 59, + HTT_STATS_PEER_STATS_CMN_TAG = 60, + HTT_STATS_PEER_DETAILS_TAG = 61, + HTT_STATS_PEER_TX_RATE_STATS_TAG = 62, + HTT_STATS_PEER_RX_RATE_STATS_TAG = 63, + HTT_STATS_PEER_MSDU_FLOWQ_TAG = 64, + HTT_STATS_TX_DE_COMPL_STATS_TAG = 65, + HTT_STATS_WHAL_TX_TAG = 66, + HTT_STATS_TX_PDEV_SIFS_HIST_TAG = 67, + HTT_STATS_RX_PDEV_FW_STATS_PHY_ERR_TAG = 68, + HTT_STATS_TX_TID_DETAILS_V1_TAG = 69, + HTT_STATS_PDEV_CCA_1SEC_HIST_TAG = 70, + HTT_STATS_PDEV_CCA_100MSEC_HIST_TAG = 71, + HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, + HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, + HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_PDEV_TWT_SESSIONS_TAG = 75, + HTT_STATS_PDEV_TWT_SESSION_TAG = 76, + HTT_STATS_RX_REFILL_RXDMA_ERR_TAG = 77, + HTT_STATS_RX_REFILL_REO_ERR_TAG = 78, + HTT_STATS_RX_REO_RESOURCE_STATS_TAG = 79, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, + HTT_STATS_TX_PDEV_TX_PPDU_STATS_TAG = 81, + HTT_STATS_TX_PDEV_TRIED_MPDU_CNT_HIST_TAG = 82, + HTT_STATS_TX_HWQ_TRIED_MPDU_CNT_HIST_TAG = 83, + HTT_STATS_TX_HWQ_TXOP_USED_CNT_HIST_TAG = 84, + HTT_STATS_TX_DE_FW2WBM_RING_FULL_HIST_TAG = 85, + HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, + HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, + HTT_STATS_PDEV_OBSS_PD_TAG = 88, + + HTT_STATS_MAX_TAG, +}; + +#define HTT_STATS_MAX_STRING_SZ32 4 +#define HTT_STATS_MACID_INVALID 0xff +#define HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS 10 +#define HTT_TX_HWQ_MAX_CMD_RESULT_STATS 13 +#define HTT_TX_HWQ_MAX_CMD_STALL_STATS 5 +#define HTT_TX_HWQ_MAX_FES_RESULT_STATS 10 + +enum htt_tx_pdev_underrun_enum { + HTT_STATS_TX_PDEV_NO_DATA_UNDERRUN = 0, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_BETWEEN_MPDU = 1, + HTT_STATS_TX_PDEV_DATA_UNDERRUN_WITHIN_MPDU = 2, + HTT_TX_PDEV_MAX_URRN_STATS = 3, +}; + +#define HTT_TX_PDEV_MAX_FLUSH_REASON_STATS 71 +#define HTT_TX_PDEV_MAX_SIFS_BURST_STATS 9 +#define HTT_TX_PDEV_MAX_SIFS_BURST_HIST_STATS 10 +#define HTT_TX_PDEV_MAX_PHY_ERR_STATS 18 +#define HTT_TX_PDEV_SCHED_TX_MODE_MAX 4 +#define HTT_TX_PDEV_NUM_SCHED_ORDER_LOG 20 + +#define HTT_RX_STATS_REFILL_MAX_RING 4 +#define HTT_RX_STATS_RXDMA_MAX_ERR 16 +#define HTT_RX_STATS_FW_DROP_REASON_MAX 16 + +/* Bytes stored in little endian order */ +/* Length should be multiple of DWORD */ +struct htt_stats_string_tlv { + u32 data[0]; /* Can be variable length */ +} __packed; + +/* == TX PDEV STATS == */ +struct htt_tx_pdev_stats_cmn_tlv { + u32 mac_id__word; + u32 hw_queued; + u32 hw_reaped; + u32 underrun; + u32 hw_paused; + u32 hw_flush; + u32 hw_filt; + u32 tx_abort; + u32 mpdu_requed; + u32 tx_xretry; + u32 data_rc; + u32 mpdu_dropped_xretry; + u32 illgl_rate_phy_err; + u32 cont_xretry; + u32 tx_timeout; + u32 pdev_resets; + u32 phy_underrun; + u32 txop_ovf; + u32 seq_posted; + u32 seq_failed_queueing; + u32 seq_completed; + u32 seq_restarted; + u32 mu_seq_posted; + u32 seq_switch_hw_paused; + u32 next_seq_posted_dsr; + u32 seq_posted_isr; + u32 seq_ctrl_cached; + u32 mpdu_count_tqm; + u32 msdu_count_tqm; + u32 mpdu_removed_tqm; + u32 msdu_removed_tqm; + u32 mpdus_sw_flush; + u32 mpdus_hw_filter; + u32 mpdus_truncated; + u32 mpdus_ack_failed; + u32 mpdus_expired; + u32 mpdus_seq_hw_retry; + u32 ack_tlv_proc; + u32 coex_abort_mpdu_cnt_valid; + u32 coex_abort_mpdu_cnt; + u32 num_total_ppdus_tried_ota; + u32 num_data_ppdus_tried_ota; + u32 local_ctrl_mgmt_enqued; + u32 local_ctrl_mgmt_freed; + u32 local_data_enqued; + u32 local_data_freed; + u32 mpdu_tried; + u32 isr_wait_seq_posted; + + u32 tx_active_dur_us_low; + u32 tx_active_dur_us_high; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_urrn_tlv_v { + u32 urrn_stats[0]; /* HTT_TX_PDEV_MAX_URRN_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_flush_tlv_v { + u32 flush_errs[0]; /* HTT_TX_PDEV_MAX_FLUSH_REASON_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_tlv_v { + u32 sifs_status[0]; /* HTT_TX_PDEV_MAX_SIFS_BURST_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_phy_err_tlv_v { + u32 phy_errs[0]; /* HTT_TX_PDEV_MAX_PHY_ERR_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_pdev_stats_sifs_hist_tlv_v { + u32 sifs_hist_status[0]; /* HTT_TX_PDEV_SIFS_BURST_HIST_STATS */ +}; + +struct htt_tx_pdev_stats_tx_ppdu_stats_tlv_v { + u32 num_data_ppdus_legacy_su; + u32 num_data_ppdus_ac_su; + u32 num_data_ppdus_ax_su; + u32 num_data_ppdus_ac_su_txbf; + u32 num_data_ppdus_ax_su_txbf; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size . + * + * Tried_mpdu_cnt_hist is the histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the + * HW had attempted to transmit on air, for the HWSCH Schedule + * command submitted by FW.It is not the retry attempts. + * The histogram bins are 0-29, 30-59, 60-89 and so on. The are + * 10 bins in this histogram. They are defined in FW using the + * following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_pdev_stats_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_PDEV_TRIED_MPDU_CNT_HIST */ +}; + +/* == SOC ERROR STATS == */ + +/* =============== PDEV ERROR STATS ============== */ +#define HTT_STATS_MAX_HW_INTR_NAME_LEN 8 +struct htt_hw_stats_intr_misc_tlv { + /* Stored as little endian */ + u8 hw_intr_name[HTT_STATS_MAX_HW_INTR_NAME_LEN]; + u32 mask; + u32 count; +}; + +#define HTT_STATS_MAX_HW_MODULE_NAME_LEN 8 +struct htt_hw_stats_wd_timeout_tlv { + /* Stored as little endian */ + u8 hw_module_name[HTT_STATS_MAX_HW_MODULE_NAME_LEN]; + u32 count; +}; + +struct htt_hw_stats_pdev_errs_tlv { + u32 mac_id__word; /* BIT [ 7 : 0] : mac_id */ + u32 tx_abort; + u32 tx_abort_fail_count; + u32 rx_abort; + u32 rx_abort_fail_count; + u32 warm_reset; + u32 cold_reset; + u32 tx_flush; + u32 tx_glb_reset; + u32 tx_txq_reset; + u32 rx_timeout_reset; +}; + +struct htt_hw_stats_whal_tx_tlv { + u32 mac_id__word; + u32 last_unpause_ppdu_id; + u32 hwsch_unpause_wait_tqm_write; + u32 hwsch_dummy_tlv_skipped; + u32 hwsch_misaligned_offset_received; + u32 hwsch_reset_count; + u32 hwsch_dev_reset_war; + u32 hwsch_delayed_pause; + u32 hwsch_long_delayed_pause; + u32 sch_rx_ppdu_no_response; + u32 sch_selfgen_response; + u32 sch_rx_sifs_resp_trigger; +}; + +/* ============ PEER STATS ============ */ +struct htt_msdu_flow_stats_tlv { + u32 last_update_timestamp; + u32 last_add_timestamp; + u32 last_remove_timestamp; + u32 total_processed_msdu_count; + u32 cur_msdu_count_in_flowq; + u32 sw_peer_id; + u32 tx_flow_no__tid_num__drop_rule; + u32 last_cycle_enqueue_count; + u32 last_cycle_dequeue_count; + u32 last_cycle_drop_count; + u32 current_drop_th; +}; + +#define MAX_HTT_TID_NAME 8 + +/* Tidq stats */ +struct htt_tx_tid_stats_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 hw_queued; + u32 hw_reaped; + u32 mpdus_hw_filter; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; +}; + +/* Tidq stats */ +struct htt_tx_tid_stats_v1_tlv { + /* Stored as little endian */ + u8 tid_name[MAX_HTT_TID_NAME]; + u32 sw_peer_id__tid_num; + u32 num_sched_pending__num_ppdu_in_hwq; + u32 tid_flags; + u32 max_qdepth_bytes; + u32 max_qdepth_n_msdus; + u32 rsvd; + + u32 qdepth_bytes; + u32 qdepth_num_msdu; + u32 qdepth_num_mpdu; + u32 last_scheduled_tsmp; + u32 pause_module_id; + u32 block_module_id; + u32 tid_tx_airtime; + u32 allow_n_flags; + u32 sendn_frms_allowed; +}; + +struct htt_rx_tid_stats_tlv { + u32 sw_peer_id__tid_num; + u8 tid_name[MAX_HTT_TID_NAME]; + u32 dup_in_reorder; + u32 dup_past_outside_window; + u32 dup_past_within_window; + u32 rxdesc_err_decrypt; + u32 tid_rx_airtime; +}; + +#define HTT_MAX_COUNTER_NAME 8 +struct htt_counter_tlv { + u8 counter_name[HTT_MAX_COUNTER_NAME]; + u32 count; +}; + +struct htt_peer_stats_cmn_tlv { + u32 ppdu_cnt; + u32 mpdu_cnt; + u32 msdu_cnt; + u32 pause_bitmap; + u32 block_bitmap; + u32 current_timestamp; + u32 peer_tx_airtime; + u32 peer_rx_airtime; + s32 rssi; + u32 peer_enqueued_count_low; + u32 peer_enqueued_count_high; + u32 peer_dequeued_count_low; + u32 peer_dequeued_count_high; + u32 peer_dropped_count_low; + u32 peer_dropped_count_high; + u32 ppdu_transmitted_bytes_low; + u32 ppdu_transmitted_bytes_high; + u32 peer_ttl_removed_count; + u32 inactive_time; +}; + +struct htt_peer_details_tlv { + u32 peer_type; + u32 sw_peer_id; + u32 vdev_pdev_ast_idx; + struct htt_mac_addr mac_addr; + u32 peer_flags; + u32 qpeer_flags; +}; + +enum htt_stats_param_type { + HTT_STATS_PREAM_OFDM, + HTT_STATS_PREAM_CCK, + HTT_STATS_PREAM_HT, + HTT_STATS_PREAM_VHT, + HTT_STATS_PREAM_HE, + HTT_STATS_PREAM_RSVD, + HTT_STATS_PREAM_RSVD1, + + HTT_STATS_PREAM_COUNT, +}; + +#define HTT_TX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_tx_peer_rate_stats_tlv { + u32 tx_ldpc; + u32 rts_cnt; + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_su_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PEER_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PEER_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PEER_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets in each GI + * (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PEER_STATS_NUM_GI_COUNTERS][HTT_TX_PEER_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PEER_STATS_NUM_DCM_COUNTERS]; + +}; + +#define HTT_RX_PEER_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PEER_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PEER_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_rx_peer_rate_stats_tlv { + u32 nsts; + + /* Number of rx ldpc packets */ + u32 rx_ldpc; + /* Number of rx rts packets */ + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PEER_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PEER_STATS_NUM_PREAMBLE_TYPES]; + /* units = dB above noise floor */ + u8 rssi_chain[HTT_RX_PEER_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PEER_STATS_NUM_BW_COUNTERS]; + + /* Counters to track number of rx packets in each GI in each mcs (0-11) */ + u32 rx_gi[HTT_RX_PEER_STATS_NUM_GI_COUNTERS] + [HTT_RX_PEER_STATS_NUM_MCS_COUNTERS]; +}; + +enum htt_peer_stats_req_mode { + HTT_PEER_STATS_REQ_MODE_NO_QUERY, + HTT_PEER_STATS_REQ_MODE_QUERY_TQM, + HTT_PEER_STATS_REQ_MODE_FLUSH_TQM, +}; + +enum htt_peer_stats_tlv_enum { + HTT_PEER_STATS_CMN_TLV = 0, + HTT_PEER_DETAILS_TLV = 1, + HTT_TX_PEER_RATE_STATS_TLV = 2, + HTT_RX_PEER_RATE_STATS_TLV = 3, + HTT_TX_TID_STATS_TLV = 4, + HTT_RX_TID_STATS_TLV = 5, + HTT_MSDU_FLOW_STATS_TLV = 6, + + HTT_PEER_STATS_MAX_TLV = 31, +}; + +/* =========== MUMIMO HWQ stats =========== */ +/* MU MIMO stats per hwQ */ +struct htt_tx_hwq_mu_mimo_sch_stats_tlv { + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + u32 mu_mimo_ppdu_posted; +}; + +struct htt_tx_hwq_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; +}; + +struct htt_tx_hwq_mu_mimo_cmn_stats_tlv { + u32 mac_id__hwq_id__word; +}; + +/* == TX HWQ STATS == */ +struct htt_tx_hwq_stats_cmn_tlv { + u32 mac_id__hwq_id__word; + + /* PPDU level stats */ + u32 xretry; + u32 underrun_cnt; + u32 flush_cnt; + u32 filt_cnt; + u32 null_mpdu_bmap; + u32 user_ack_failure; + u32 ack_tlv_proc; + u32 sched_id_proc; + u32 null_mpdu_tx_count; + u32 mpdu_bmap_not_recvd; + + /* Selfgen stats per hwQ */ + u32 num_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + + /* MPDU level stats */ + u32 mpdu_tried_cnt; + u32 mpdu_queued_cnt; + u32 mpdu_ack_fail_cnt; + u32 mpdu_filt_cnt; + u32 false_mpdu_ack_count; + + u32 txq_timeout; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_difs_latency_stats_tlv_v { + u32 hist_intvl; + /* histogram of ppdu post to hwsch - > cmd status received */ + u32 difs_latency_hist[0]; /* HTT_TX_HWQ_MAX_DIFS_LATENCY_BINS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_result_stats_tlv_v { + /* Histogram of sched cmd result */ + u32 cmd_result[0]; /* HTT_TX_HWQ_MAX_CMD_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_cmd_stall_stats_tlv_v { + /* Histogram of various pause conitions */ + u32 cmd_stall_status[0]; /* HTT_TX_HWQ_MAX_CMD_STALL_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_hwq_fes_result_stats_tlv_v { + /* Histogram of number of user fes result */ + u32 fes_result[0]; /* HTT_TX_HWQ_MAX_FES_RESULT_STATS */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The hwq_tried_mpdu_cnt_hist is a histogram of MPDUs tries per HWQ. + * The tries here is the count of the MPDUS within a PPDU that the HW + * had attempted to transmit on air, for the HWSCH Schedule command + * submitted by FW in this HWQ .It is not the retry attempts. The + * histogram bins are 0-29, 30-59, 60-89 and so on. The are 10 bins + * in this histogram. + * they are defined in FW using the following macros + * #define WAL_MAX_TRIED_MPDU_CNT_HISTOGRAM 9 + * #define WAL_TRIED_MPDU_CNT_HISTOGRAM_INTERVAL 30 + */ +struct htt_tx_hwq_tried_mpdu_cnt_hist_tlv_v { + u32 hist_bin_size; + /* Histogram of number of mpdus on tried mpdu */ + u32 tried_mpdu_cnt_hist[0]; /* HTT_TX_HWQ_TRIED_MPDU_CNT_HIST */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size + * + * The txop_used_cnt_hist is the histogram of txop per burst. After + * completing the burst, we identify the txop used in the burst and + * incr the corresponding bin. + * Each bin represents 1ms & we have 10 bins in this histogram. + * they are deined in FW using the following macros + * #define WAL_MAX_TXOP_USED_CNT_HISTOGRAM 10 + * #define WAL_TXOP_USED_HISTOGRAM_INTERVAL 1000 ( 1 ms ) + */ +struct htt_tx_hwq_txop_used_cnt_hist_tlv_v { + /* Histogram of txop used cnt */ + u32 txop_used_cnt_hist[0]; /* HTT_TX_HWQ_TXOP_USED_CNT_HIST */ +}; + +/* == TX SELFGEN STATS == */ +struct htt_tx_selfgen_cmn_stats_tlv { + u32 mac_id__word; + u32 su_bar; + u32 rts; + u32 cts2self; + u32 qos_null; + u32 delayed_bar_1; /* MU user 1 */ + u32 delayed_bar_2; /* MU user 2 */ + u32 delayed_bar_3; /* MU user 3 */ + u32 delayed_bar_4; /* MU user 4 */ + u32 delayed_bar_5; /* MU user 5 */ + u32 delayed_bar_6; /* MU user 6 */ + u32 delayed_bar_7; /* MU user 7 */ +}; + +struct htt_tx_selfgen_ac_stats_tlv { + /* 11AC */ + u32 ac_su_ndpa; + u32 ac_su_ndp; + u32 ac_mu_mimo_ndpa; + u32 ac_mu_mimo_ndp; + u32 ac_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ac_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ac_mu_mimo_brpoll_3; /* MU user 3 */ +}; + +struct htt_tx_selfgen_ax_stats_tlv { + /* 11AX */ + u32 ax_su_ndpa; + u32 ax_su_ndp; + u32 ax_mu_mimo_ndpa; + u32 ax_mu_mimo_ndp; + u32 ax_mu_mimo_brpoll_1; /* MU user 1 */ + u32 ax_mu_mimo_brpoll_2; /* MU user 2 */ + u32 ax_mu_mimo_brpoll_3; /* MU user 3 */ + u32 ax_mu_mimo_brpoll_4; /* MU user 4 */ + u32 ax_mu_mimo_brpoll_5; /* MU user 5 */ + u32 ax_mu_mimo_brpoll_6; /* MU user 6 */ + u32 ax_mu_mimo_brpoll_7; /* MU user 7 */ + u32 ax_basic_trigger; + u32 ax_bsr_trigger; + u32 ax_mu_bar_trigger; + u32 ax_mu_rts_trigger; +}; + +struct htt_tx_selfgen_ac_err_stats_tlv { + /* 11AC error stats */ + u32 ac_su_ndp_err; + u32 ac_su_ndpa_err; + u32 ac_mu_mimo_ndpa_err; + u32 ac_mu_mimo_ndp_err; + u32 ac_mu_mimo_brp1_err; + u32 ac_mu_mimo_brp2_err; + u32 ac_mu_mimo_brp3_err; +}; + +struct htt_tx_selfgen_ax_err_stats_tlv { + /* 11AX error stats */ + u32 ax_su_ndp_err; + u32 ax_su_ndpa_err; + u32 ax_mu_mimo_ndpa_err; + u32 ax_mu_mimo_ndp_err; + u32 ax_mu_mimo_brp1_err; + u32 ax_mu_mimo_brp2_err; + u32 ax_mu_mimo_brp3_err; + u32 ax_mu_mimo_brp4_err; + u32 ax_mu_mimo_brp5_err; + u32 ax_mu_mimo_brp6_err; + u32 ax_mu_mimo_brp7_err; + u32 ax_basic_trigger_err; + u32 ax_bsr_trigger_err; + u32 ax_mu_bar_trigger_err; + u32 ax_mu_rts_trigger_err; +}; + +/* == TX MU STATS == */ +#define HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS 74 + +struct htt_tx_pdev_mu_mimo_sch_stats_tlv { + /* mu-mimo sw sched cmd stats */ + u32 mu_mimo_sch_posted; + u32 mu_mimo_sch_failed; + /* MU PPDU stats per hwQ */ + u32 mu_mimo_ppdu_posted; + /* + * Counts the number of users in each transmission of + * the given TX mode. + * + * Index is the number of users - 1. + */ + u32 ac_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AC_MUMIMO_USER_STATS]; + u32 ax_mu_mimo_sch_nusers[HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS]; + u32 ax_ofdma_sch_nusers[HTT_TX_PDEV_STATS_NUM_OFDMA_USER_STATS]; +}; + +struct htt_tx_pdev_mu_mimo_mpdu_stats_tlv { + u32 mu_mimo_mpdus_queued_usr; + u32 mu_mimo_mpdus_tried_usr; + u32 mu_mimo_mpdus_failed_usr; + u32 mu_mimo_mpdus_requeued_usr; + u32 mu_mimo_err_no_ba_usr; + u32 mu_mimo_mpdu_underrun_usr; + u32 mu_mimo_ampdu_underrun_usr; + + u32 ax_mu_mimo_mpdus_queued_usr; + u32 ax_mu_mimo_mpdus_tried_usr; + u32 ax_mu_mimo_mpdus_failed_usr; + u32 ax_mu_mimo_mpdus_requeued_usr; + u32 ax_mu_mimo_err_no_ba_usr; + u32 ax_mu_mimo_mpdu_underrun_usr; + u32 ax_mu_mimo_ampdu_underrun_usr; + + u32 ax_ofdma_mpdus_queued_usr; + u32 ax_ofdma_mpdus_tried_usr; + u32 ax_ofdma_mpdus_failed_usr; + u32 ax_ofdma_mpdus_requeued_usr; + u32 ax_ofdma_err_no_ba_usr; + u32 ax_ofdma_mpdu_underrun_usr; + u32 ax_ofdma_ampdu_underrun_usr; +}; + +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AC 1 +#define HTT_STATS_TX_SCHED_MODE_MU_MIMO_AX 2 +#define HTT_STATS_TX_SCHED_MODE_MU_OFDMA_AX 3 + +struct htt_tx_pdev_mpdu_stats_tlv { + /* mpdu level stats */ + u32 mpdus_queued_usr; + u32 mpdus_tried_usr; + u32 mpdus_failed_usr; + u32 mpdus_requeued_usr; + u32 err_no_ba_usr; + u32 mpdu_underrun_usr; + u32 ampdu_underrun_usr; + u32 user_index; + u32 tx_sched_mode; /* HTT_STATS_TX_SCHED_MODE_xxx */ +}; + +/* == TX SCHED STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_posted_tlv_v { + u32 sched_cmd_posted[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_cmd_reaped_tlv_v { + u32 sched_cmd_reaped[0]; /* HTT_TX_PDEV_SCHED_TX_MODE_MAX */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_order_su_tlv_v { + u32 sched_order_su[0]; /* HTT_TX_PDEV_NUM_SCHED_ORDER_LOG */ +}; + +enum htt_sched_txq_sched_ineligibility_tlv_enum { + HTT_SCHED_TID_SKIP_SCHED_MASK_DISABLED = 0, + HTT_SCHED_TID_SKIP_NOTIFY_MPDU, + HTT_SCHED_TID_SKIP_MPDU_STATE_INVALID, + HTT_SCHED_TID_SKIP_SCHED_DISABLED, + HTT_SCHED_TID_SKIP_TQM_BYPASS_CMD_PENDING, + HTT_SCHED_TID_SKIP_SECOND_SU_SCHEDULE, + + HTT_SCHED_TID_SKIP_CMD_SLOT_NOT_AVAIL, + HTT_SCHED_TID_SKIP_NO_ENQ, + HTT_SCHED_TID_SKIP_LOW_ENQ, + HTT_SCHED_TID_SKIP_PAUSED, + HTT_SCHED_TID_SKIP_UL, + HTT_SCHED_TID_REMOVE_PAUSED, + HTT_SCHED_TID_REMOVE_NO_ENQ, + HTT_SCHED_TID_REMOVE_UL, + HTT_SCHED_TID_QUERY, + HTT_SCHED_TID_SU_ONLY, + HTT_SCHED_TID_ELIGIBLE, + HTT_SCHED_INELIGIBILITY_MAX, +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sched_txq_sched_ineligibility_tlv_v { + /* indexed by htt_sched_txq_sched_ineligibility_tlv_enum */ + u32 sched_ineligibility[0]; +}; + +struct htt_tx_pdev_stats_sched_per_txq_tlv { + u32 mac_id__txq_id__word; + u32 sched_policy; + u32 last_sched_cmd_posted_timestamp; + u32 last_sched_cmd_compl_timestamp; + u32 sched_2_tac_lwm_count; + u32 sched_2_tac_ring_full; + u32 sched_cmd_post_failure; + u32 num_active_tids; + u32 num_ps_schedules; + u32 sched_cmds_pending; + u32 num_tid_register; + u32 num_tid_unregister; + u32 num_qstats_queried; + u32 qstats_update_pending; + u32 last_qstats_query_timestamp; + u32 num_tqm_cmdq_full; + u32 num_de_sched_algo_trigger; + u32 num_rt_sched_algo_trigger; + u32 num_tqm_sched_algo_trigger; + u32 notify_sched; + u32 dur_based_sendn_term; +}; + +struct htt_stats_tx_sched_cmn_tlv { + /* BIT [ 7 : 0] :- mac_id + * BIT [31 : 8] :- reserved + */ + u32 mac_id__word; + /* Current timestamp */ + u32 current_timestamp; +}; + +/* == TQM STATS == */ +#define HTT_TX_TQM_MAX_GEN_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_END_REASON 16 +#define HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS 16 + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_gen_mpdu_stats_tlv_v { + u32 gen_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_GEN_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_stats_tlv_v { + u32 list_mpdu_end_reason[0]; /* HTT_TX_TQM_MAX_LIST_MPDU_END_REASON */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_tx_tqm_list_mpdu_cnt_tlv_v { + u32 list_mpdu_cnt_hist[0]; + /* HTT_TX_TQM_MAX_LIST_MPDU_CNT_HISTOGRAM_BINS */ +}; + +struct htt_tx_tqm_pdev_stats_tlv_v { + u32 msdu_count; + u32 mpdu_count; + u32 remove_msdu; + u32 remove_mpdu; + u32 remove_msdu_ttl; + u32 send_bar; + u32 bar_sync; + u32 notify_mpdu; + u32 sync_cmd; + u32 write_cmd; + u32 hwsch_trigger; + u32 ack_tlv_proc; + u32 gen_mpdu_cmd; + u32 gen_list_cmd; + u32 remove_mpdu_cmd; + u32 remove_mpdu_tried_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_msdu_cmd; + u32 remove_msdu_ttl_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 enqueue; + u32 enqueue_notify; + u32 notify_mpdu_at_head; + u32 notify_mpdu_state_valid; + /* + * On receiving TQM_FLOW_NOT_EMPTY_STATUS from TQM, (on MSDUs being enqueued + * the flow is non empty), if the number of MSDUs is greater than the threshold, + * notify is incremented. UDP_THRESH counters are for UDP MSDUs, and NONUDP are + * for non-UDP MSDUs. + * MSDUQ_SWNOTIFY_UDP_THRESH1 threshold - sched_udp_notify1 is incremented + * MSDUQ_SWNOTIFY_UDP_THRESH2 threshold - sched_udp_notify2 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH1 threshold - sched_nonudp_notify1 is incremented + * MSDUQ_SWNOTIFY_NONUDP_THRESH2 threshold - sched_nonudp_notify2 is incremented + * + * Notify signifies that we trigger the scheduler. + */ + u32 sched_udp_notify1; + u32 sched_udp_notify2; + u32 sched_nonudp_notify1; + u32 sched_nonudp_notify2; +}; + +struct htt_tx_tqm_cmn_stats_tlv { + u32 mac_id__word; + u32 max_cmdq_id; + u32 list_mpdu_cnt_hist_intvl; + + /* Global stats */ + u32 add_msdu; + u32 q_empty; + u32 q_not_empty; + u32 drop_notification; + u32 desc_threshold; +}; + +struct htt_tx_tqm_error_stats_tlv { + /* Error stats */ + u32 q_empty_failure; + u32 q_not_empty_failure; + u32 add_msdu_failure; +}; + +/* == TQM CMDQ stats == */ +struct htt_tx_tqm_cmdq_status_tlv { + u32 mac_id__cmdq_id__word; + u32 sync_cmd; + u32 write_cmd; + u32 gen_mpdu_cmd; + u32 mpdu_queue_stats_cmd; + u32 mpdu_head_info_cmd; + u32 msdu_flow_stats_cmd; + u32 remove_mpdu_cmd; + u32 remove_msdu_cmd; + u32 flush_cache_cmd; + u32 update_mpduq_cmd; + u32 update_msduq_cmd; +}; + +/* == TX-DE STATS == */ +/* Structures for tx de stats */ +struct htt_tx_de_eapol_packets_stats_tlv { + u32 m1_packets; + u32 m2_packets; + u32 m3_packets; + u32 m4_packets; + u32 g1_packets; + u32 g2_packets; +}; + +struct htt_tx_de_classify_failed_stats_tlv { + u32 ap_bss_peer_not_found; + u32 ap_bcast_mcast_no_peer; + u32 sta_delete_in_progress; + u32 ibss_no_bss_peer; + u32 invalid_vdev_type; + u32 invalid_ast_peer_entry; + u32 peer_entry_invalid; + u32 ethertype_not_ip; + u32 eapol_lookup_failed; + u32 qpeer_not_allow_data; + u32 fse_tid_override; + u32 ipv6_jumbogram_zero_length; + u32 qos_to_non_qos_in_prog; +}; + +struct htt_tx_de_classify_stats_tlv { + u32 arp_packets; + u32 igmp_packets; + u32 dhcp_packets; + u32 host_inspected; + u32 htt_included; + u32 htt_valid_mcs; + u32 htt_valid_nss; + u32 htt_valid_preamble_type; + u32 htt_valid_chainmask; + u32 htt_valid_guard_interval; + u32 htt_valid_retries; + u32 htt_valid_bw_info; + u32 htt_valid_power; + u32 htt_valid_key_flags; + u32 htt_valid_no_encryption; + u32 fse_entry_count; + u32 fse_priority_be; + u32 fse_priority_high; + u32 fse_priority_low; + u32 fse_traffic_ptrn_be; + u32 fse_traffic_ptrn_over_sub; + u32 fse_traffic_ptrn_bursty; + u32 fse_traffic_ptrn_interactive; + u32 fse_traffic_ptrn_periodic; + u32 fse_hwqueue_alloc; + u32 fse_hwqueue_created; + u32 fse_hwqueue_send_to_host; + u32 mcast_entry; + u32 bcast_entry; + u32 htt_update_peer_cache; + u32 htt_learning_frame; + u32 fse_invalid_peer; + /* + * mec_notify is HTT TX WBM multicast echo check notification + * from firmware to host. FW sends SA addresses to host for all + * multicast/broadcast packets received on STA side. + */ + u32 mec_notify; +}; + +struct htt_tx_de_classify_status_stats_tlv { + u32 eok; + u32 classify_done; + u32 lookup_failed; + u32 send_host_dhcp; + u32 send_host_mcast; + u32 send_host_unknown_dest; + u32 send_host; + u32 status_invalid; +}; + +struct htt_tx_de_enqueue_packets_stats_tlv { + u32 enqueued_pkts; + u32 to_tqm; + u32 to_tqm_bypass; +}; + +struct htt_tx_de_enqueue_discard_stats_tlv { + u32 discarded_pkts; + u32 local_frames; + u32 is_ext_msdu; +}; + +struct htt_tx_de_compl_stats_tlv { + u32 tcl_dummy_frame; + u32 tqm_dummy_frame; + u32 tqm_notify_frame; + u32 fw2wbm_enq; + u32 tqm_bypass_frame; +}; + +/* + * The htt_tx_de_fw2wbm_ring_full_hist_tlv is a histogram of time we waited + * for the fw2wbm ring buffer. we are requesting a buffer in FW2WBM release + * ring,which may fail, due to non availability of buffer. Hence we sleep for + * 200us & again request for it. This is a histogram of time we wait, with + * bin of 200ms & there are 10 bin (2 seconds max) + * They are defined by the following macros in FW + * #define ENTRIES_PER_BIN_COUNT 1000 // per bin 1000 * 200us = 200ms + * #define RING_FULL_BIN_ENTRIES (WAL_TX_DE_FW2WBM_ALLOC_TIMEOUT_COUNT / + * ENTRIES_PER_BIN_COUNT) + */ +struct htt_tx_de_fw2wbm_ring_full_hist_tlv { + u32 fw2wbm_ring_full_hist[0]; +}; + +struct htt_tx_de_cmn_stats_tlv { + u32 mac_id__word; + + /* Global Stats */ + u32 tcl2fw_entry_count; + u32 not_to_fw; + u32 invalid_pdev_vdev_peer; + u32 tcl_res_invalid_addrx; + u32 wbm2fw_entry_count; + u32 invalid_pdev; +}; + +/* == RING-IF STATS == */ +#define HTT_STATS_LOW_WM_BINS 5 +#define HTT_STATS_HIGH_WM_BINS 5 + +struct htt_ring_if_stats_tlv { + u32 base_addr; /* DWORD aligned base memory address of the ring */ + u32 elem_size; + u32 num_elems__prefetch_tail_idx; + u32 head_idx__tail_idx; + u32 shadow_head_idx__shadow_tail_idx; + u32 num_tail_incr; + u32 lwm_thresh__hwm_thresh; + u32 overrun_hit_count; + u32 underrun_hit_count; + u32 prod_blockwait_count; + u32 cons_blockwait_count; + u32 low_wm_hit_count[HTT_STATS_LOW_WM_BINS]; + u32 high_wm_hit_count[HTT_STATS_HIGH_WM_BINS]; +}; + +struct htt_ring_if_cmn_tlv { + u32 mac_id__word; + u32 num_records; +}; + +/* == SFM STATS == */ +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_sfm_client_user_tlv_v { + /* Number of DWORDS used per user and per client */ + u32 dwords_used_by_user_n[0]; +}; + +struct htt_sfm_client_tlv { + /* Client ID */ + u32 client_id; + /* Minimum number of buffers */ + u32 buf_min; + /* Maximum number of buffers */ + u32 buf_max; + /* Number of Busy buffers */ + u32 buf_busy; + /* Number of Allocated buffers */ + u32 buf_alloc; + /* Number of Available/Usable buffers */ + u32 buf_avail; + /* Number of users */ + u32 num_users; +}; + +struct htt_sfm_cmn_tlv { + u32 mac_id__word; + /* Indicates the total number of 128 byte buffers + * in the CMEM that are available for buffer sharing + */ + u32 buf_total; + /* Indicates for certain client or all the clients + * there is no dowrd saved in SFM, refer to SFM_R1_MEM_EMPTY + */ + u32 mem_empty; + /* DEALLOCATE_BUFFERS, refer to register SFM_R0_DEALLOCATE_BUFFERS */ + u32 deallocate_bufs; + /* Number of Records */ + u32 num_records; +}; + +/* == SRNG STATS == */ +struct htt_sring_stats_tlv { + u32 mac_id__ring_id__arena__ep; + u32 base_addr_lsb; /* DWORD aligned base memory address of the ring */ + u32 base_addr_msb; + u32 ring_size; + u32 elem_size; + + u32 num_avail_words__num_valid_words; + u32 head_ptr__tail_ptr; + u32 consumer_empty__producer_full; + u32 prefetch_count__internal_tail_ptr; +}; + +struct htt_sring_cmn_tlv { + u32 num_records; +}; + +/* == PDEV TX RATE CTRL STATS == */ +#define HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT +#define HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_TX_PDEV_STATS_NUM_LTF 4 + +#define HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + HTT_TX_PDEV_STATS_NUM_AX_MUMIMO_USER_STATS) + +struct htt_tx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 tx_ldpc; + u32 rts_cnt; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + + u32 tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 tx_su_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_mu_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 tx_stbc[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 tx_pream[HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + + /* Counters to track number of tx packets + * in each GI (400us, 800us, 1600us & 3200us) in each mcs (0-11) + */ + u32 tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS][HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + /* Counters to track packets in dcm mcs (MCS 0, 1, 3, 4) */ + u32 tx_dcm[HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + /* Number of CTS-acknowledged RTS packets */ + u32 rts_success; + + /* + * Counters for legacy 11a and 11b transmissions. + * + * The index corresponds to: + * + * CCK: 0: 1 Mbps, 1: 2 Mbps, 2: 5.5 Mbps, 3: 11 Mbps + * + * OFDM: 0: 6 Mbps, 1: 9 Mbps, 2: 12 Mbps, 3: 18 Mbps, + * 4: 24 Mbps, 5: 36 Mbps, 6: 48 Mbps, 7: 54 Mbps + */ + u32 tx_legacy_cck_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 tx_legacy_ofdm_rate[HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + + u32 ac_mu_mimo_tx_ldpc; + u32 ax_mu_mimo_tx_ldpc; + u32 ofdma_tx_ldpc; + + /* + * Counters for 11ax HE LTF selection during TX. + * + * The index corresponds to: + * + * 0: unused, 1: 1x LTF, 2: 2x LTF, 3: 4x LTF + */ + u32 tx_he_ltf[HTT_TX_PDEV_STATS_NUM_LTF]; + + u32 ac_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_mcs[HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + + u32 ac_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ax_mu_mimo_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 ofdma_tx_nss[HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + + u32 ac_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ax_mu_mimo_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 ofdma_tx_bw[HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + + u32 ac_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ax_mu_mimo_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + u32 ofdma_tx_gi[HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; +}; + +/* == PDEV RX RATE CTRL STATS == */ +#define HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES HTT_STATS_PREAM_COUNT + +struct htt_rx_pdev_rate_stats_tlv { + u32 mac_id__word; + u32 nsts; + + u32 rx_ldpc; + u32 rts_cnt; + + u32 rssi_mgmt; /* units = dB above noise floor */ + u32 rssi_data; /* units = dB above noise floor */ + u32 rssi_comb; /* units = dB above noise floor */ + u32 rx_mcs[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0,1, ...7 -> NSS 1,2, ...8 */ + u32 rx_nss[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + u32 rx_dcm[HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + u32 rx_stbc[HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + /* element 0: 20 MHz, 1: 40 MHz, 2: 80 MHz, 3: 160 and 80+80 MHz */ + u32 rx_bw[HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + u32 rx_pream[HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain[HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + /* units = dB above noise floor */ + + /* Counters to track number of rx packets + * in each GI in each mcs (0-11) + */ + u32 rx_gi[HTT_RX_PDEV_STATS_NUM_GI_COUNTERS][HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + s32 rssi_in_dbm; /* rx Signal Strength value in dBm unit */ + + u32 rx_11ax_su_ext; + u32 rx_11ac_mumimo; + u32 rx_11ax_mumimo; + u32 rx_11ax_ofdma; + u32 txbf; + u32 rx_legacy_cck_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + u32 rx_legacy_ofdm_rate[HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + u32 rx_active_dur_us_low; + u32 rx_active_dur_us_high; +}; + +/* == RX PDEV/SOC STATS == */ +struct htt_rx_soc_fw_stats_tlv { + u32 fw_reo_ring_data_msdu; + u32 fw_to_host_data_msdu_bcmc; + u32 fw_to_host_data_msdu_uc; + u32 ofld_remote_data_buf_recycle_cnt; + u32 ofld_remote_free_buf_indication_cnt; + + u32 ofld_buf_to_host_data_msdu_uc; + u32 reo_fw_ring_to_host_data_msdu_uc; + + u32 wbm_sw_ring_reap; + u32 wbm_forward_to_host_cnt; + u32 wbm_target_recycle_cnt; + + u32 target_refill_ring_recycle_cnt; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_empty_tlv_v { + u32 refill_ring_empty_cnt[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_refill_tlv_v { + u32 refill_ring_num_refill[0]; /* HTT_RX_STATS_REFILL_MAX_RING */ +}; + +/* RXDMA error code from WBM released packets */ +enum htt_rx_rxdma_error_code_enum { + HTT_RX_RXDMA_OVERFLOW_ERR = 0, + HTT_RX_RXDMA_MPDU_LENGTH_ERR = 1, + HTT_RX_RXDMA_FCS_ERR = 2, + HTT_RX_RXDMA_DECRYPT_ERR = 3, + HTT_RX_RXDMA_TKIP_MIC_ERR = 4, + HTT_RX_RXDMA_UNECRYPTED_ERR = 5, + HTT_RX_RXDMA_MSDU_LEN_ERR = 6, + HTT_RX_RXDMA_MSDU_LIMIT_ERR = 7, + HTT_RX_RXDMA_WIFI_PARSE_ERR = 8, + HTT_RX_RXDMA_AMSDU_PARSE_ERR = 9, + HTT_RX_RXDMA_SA_TIMEOUT_ERR = 10, + HTT_RX_RXDMA_DA_TIMEOUT_ERR = 11, + HTT_RX_RXDMA_FLOW_TIMEOUT_ERR = 12, + HTT_RX_RXDMA_FLUSH_REQUEST = 13, + HTT_RX_RXDMA_ERR_CODE_RVSD0 = 14, + HTT_RX_RXDMA_ERR_CODE_RVSD1 = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_RXDMA_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_rxdma_err_tlv_v { + u32 rxdma_err[0]; /* HTT_RX_RXDMA_MAX_ERR_CODE */ +}; + +/* REO error code from WBM released packets */ +enum htt_rx_reo_error_code_enum { + HTT_RX_REO_QUEUE_DESC_ADDR_ZERO = 0, + HTT_RX_REO_QUEUE_DESC_NOT_VALID = 1, + HTT_RX_AMPDU_IN_NON_BA = 2, + HTT_RX_NON_BA_DUPLICATE = 3, + HTT_RX_BA_DUPLICATE = 4, + HTT_RX_REGULAR_FRAME_2K_JUMP = 5, + HTT_RX_BAR_FRAME_2K_JUMP = 6, + HTT_RX_REGULAR_FRAME_OOR = 7, + HTT_RX_BAR_FRAME_OOR = 8, + HTT_RX_BAR_FRAME_NO_BA_SESSION = 9, + HTT_RX_BAR_FRAME_SN_EQUALS_SSN = 10, + HTT_RX_PN_CHECK_FAILED = 11, + HTT_RX_2K_ERROR_HANDLING_FLAG_SET = 12, + HTT_RX_PN_ERROR_HANDLING_FLAG_SET = 13, + HTT_RX_QUEUE_DESCRIPTOR_BLOCKED_SET = 14, + HTT_RX_REO_ERR_CODE_RVSD = 15, + + /* This MAX_ERR_CODE should not be used in any host/target messages, + * so that even though it is defined within a host/target interface + * definition header file, it isn't actually part of the host/target + * interface, and thus can be modified. + */ + HTT_RX_REO_MAX_ERR_CODE +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_soc_fw_refill_ring_num_reo_err_tlv_v { + u32 reo_err[0]; /* HTT_RX_REO_MAX_ERR_CODE */ +}; + +/* == RX PDEV STATS == */ +#define HTT_STATS_SUBTYPE_MAX 16 + +struct htt_rx_pdev_fw_stats_tlv { + u32 mac_id__word; + u32 ppdu_recvd; + u32 mpdu_cnt_fcs_ok; + u32 mpdu_cnt_fcs_err; + u32 tcp_msdu_cnt; + u32 tcp_ack_msdu_cnt; + u32 udp_msdu_cnt; + u32 other_msdu_cnt; + u32 fw_ring_mpdu_ind; + u32 fw_ring_mgmt_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_ctrl_subtype[HTT_STATS_SUBTYPE_MAX]; + u32 fw_ring_mcast_data_msdu; + u32 fw_ring_bcast_data_msdu; + u32 fw_ring_ucast_data_msdu; + u32 fw_ring_null_data_msdu; + u32 fw_ring_mpdu_drop; + u32 ofld_local_data_ind_cnt; + u32 ofld_local_data_buf_recycle_cnt; + u32 drx_local_data_ind_cnt; + u32 drx_local_data_buf_recycle_cnt; + u32 local_nondata_ind_cnt; + u32 local_nondata_buf_recycle_cnt; + + u32 fw_status_buf_ring_refill_cnt; + u32 fw_status_buf_ring_empty_cnt; + u32 fw_pkt_buf_ring_refill_cnt; + u32 fw_pkt_buf_ring_empty_cnt; + u32 fw_link_buf_ring_refill_cnt; + u32 fw_link_buf_ring_empty_cnt; + + u32 host_pkt_buf_ring_refill_cnt; + u32 host_pkt_buf_ring_empty_cnt; + u32 mon_pkt_buf_ring_refill_cnt; + u32 mon_pkt_buf_ring_empty_cnt; + u32 mon_status_buf_ring_refill_cnt; + u32 mon_status_buf_ring_empty_cnt; + u32 mon_desc_buf_ring_refill_cnt; + u32 mon_desc_buf_ring_empty_cnt; + u32 mon_dest_ring_update_cnt; + u32 mon_dest_ring_full_cnt; + + u32 rx_suspend_cnt; + u32 rx_suspend_fail_cnt; + u32 rx_resume_cnt; + u32 rx_resume_fail_cnt; + u32 rx_ring_switch_cnt; + u32 rx_ring_restore_cnt; + u32 rx_flush_cnt; + u32 rx_recovery_reset_cnt; +}; + +#define HTT_STATS_PHY_ERR_MAX 43 + +struct htt_rx_pdev_fw_stats_phy_err_tlv { + u32 mac_id__word; + u32 total_phy_err_cnt; + /* Counts of different types of phy errs + * The mapping of PHY error types to phy_err array elements is HW dependent. + * The only currently-supported mapping is shown below: + * + * 0 phyrx_err_phy_off Reception aborted due to receiving a PHY_OFF TLV + * 1 phyrx_err_synth_off + * 2 phyrx_err_ofdma_timing + * 3 phyrx_err_ofdma_signal_parity + * 4 phyrx_err_ofdma_rate_illegal + * 5 phyrx_err_ofdma_length_illegal + * 6 phyrx_err_ofdma_restart + * 7 phyrx_err_ofdma_service + * 8 phyrx_err_ppdu_ofdma_power_drop + * 9 phyrx_err_cck_blokker + * 10 phyrx_err_cck_timing + * 11 phyrx_err_cck_header_crc + * 12 phyrx_err_cck_rate_illegal + * 13 phyrx_err_cck_length_illegal + * 14 phyrx_err_cck_restart + * 15 phyrx_err_cck_service + * 16 phyrx_err_cck_power_drop + * 17 phyrx_err_ht_crc_err + * 18 phyrx_err_ht_length_illegal + * 19 phyrx_err_ht_rate_illegal + * 20 phyrx_err_ht_zlf + * 21 phyrx_err_false_radar_ext + * 22 phyrx_err_green_field + * 23 phyrx_err_bw_gt_dyn_bw + * 24 phyrx_err_leg_ht_mismatch + * 25 phyrx_err_vht_crc_error + * 26 phyrx_err_vht_siga_unsupported + * 27 phyrx_err_vht_lsig_len_invalid + * 28 phyrx_err_vht_ndp_or_zlf + * 29 phyrx_err_vht_nsym_lt_zero + * 30 phyrx_err_vht_rx_extra_symbol_mismatch + * 31 phyrx_err_vht_rx_skip_group_id0 + * 32 phyrx_err_vht_rx_skip_group_id1to62 + * 33 phyrx_err_vht_rx_skip_group_id63 + * 34 phyrx_err_ofdm_ldpc_decoder_disabled + * 35 phyrx_err_defer_nap + * 36 phyrx_err_fdomain_timeout + * 37 phyrx_err_lsig_rel_check + * 38 phyrx_err_bt_collision + * 39 phyrx_err_unsupported_mu_feedback + * 40 phyrx_err_ppdu_tx_interrupt_rx + * 41 phyrx_err_unsupported_cbf + * 42 phyrx_err_other + */ + u32 phy_err[HTT_STATS_PHY_ERR_MAX]; +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_ring_mpdu_err_tlv_v { + /* Num error MPDU for each RxDMA error type */ + u32 fw_ring_mpdu_err[0]; /* HTT_RX_STATS_RXDMA_MAX_ERR */ +}; + +/* NOTE: Variable length TLV, use length spec to infer array size */ +struct htt_rx_pdev_fw_mpdu_drop_tlv_v { + /* Num MPDU dropped */ + u32 fw_mpdu_drop[0]; /* HTT_RX_STATS_FW_DROP_REASON_MAX */ +}; + +#define HTT_PDEV_CCA_STATS_TX_FRAME_INFO_PRESENT (0x1) +#define HTT_PDEV_CCA_STATS_RX_FRAME_INFO_PRESENT (0x2) +#define HTT_PDEV_CCA_STATS_RX_CLEAR_INFO_PRESENT (0x4) +#define HTT_PDEV_CCA_STATS_MY_RX_FRAME_INFO_PRESENT (0x8) +#define HTT_PDEV_CCA_STATS_USEC_CNT_INFO_PRESENT (0x10) +#define HTT_PDEV_CCA_STATS_MED_RX_IDLE_INFO_PRESENT (0x20) +#define HTT_PDEV_CCA_STATS_MED_TX_IDLE_GLOBAL_INFO_PRESENT (0x40) +#define HTT_PDEV_CCA_STATS_CCA_OBBS_USEC_INFO_PRESENT (0x80) + +struct htt_pdev_stats_cca_counters_tlv { + /* Below values are obtained from the HW Cycles counter registers */ + u32 tx_frame_usec; + u32 rx_frame_usec; + u32 rx_clear_usec; + u32 my_rx_frame_usec; + u32 usec_cnt; + u32 med_rx_idle_usec; + u32 med_tx_idle_global_usec; + u32 cca_obss_usec; +}; + +struct htt_pdev_cca_stats_hist_v1_tlv { + u32 chan_num; + /* num of CCA records (Num of htt_pdev_stats_cca_counters_tlv)*/ + u32 num_records; + u32 valid_cca_counters_bitmap; + u32 collection_interval; + + /* This will be followed by an array which contains the CCA stats + * collected in the last N intervals, + * if the indication is for last N intervals CCA stats. + * Then the pdev_cca_stats[0] element contains the oldest CCA stats + * and pdev_cca_stats[N-1] will have the most recent CCA stats. + * htt_pdev_stats_cca_counters_tlv cca_hist_tlv[1]; + */ +}; + +struct htt_pdev_stats_twt_session_tlv { + u32 vdev_id; + struct htt_mac_addr peer_mac; + u32 flow_id_flags; + + /* TWT_DIALOG_ID_UNAVAILABLE is used + * when TWT session is not initiated by host + */ + u32 dialog_id; + u32 wake_dura_us; + u32 wake_intvl_us; + u32 sp_offset_us; +}; + +struct htt_pdev_stats_twt_sessions_tlv { + u32 pdev_id; + u32 num_sessions; + struct htt_pdev_stats_twt_session_tlv twt_session[0]; +}; + +enum htt_rx_reo_resource_sample_id_enum { + /* Global link descriptor queued in REO */ + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_0 = 0, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_1 = 1, + HTT_RX_REO_RESOURCE_GLOBAL_LINK_DESC_COUNT_2 = 2, + /*Number of queue descriptors of this aging group */ + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC0 = 3, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC1 = 4, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC2 = 5, + HTT_RX_REO_RESOURCE_BUFFERS_USED_AC3 = 6, + /* Total number of MSDUs buffered in AC */ + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC0 = 7, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC1 = 8, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC2 = 9, + HTT_RX_REO_RESOURCE_AGING_NUM_QUEUES_AC3 = 10, + + HTT_RX_REO_RESOURCE_STATS_MAX = 16 +}; + +struct htt_rx_reo_resource_stats_tlv_v { + /* Variable based on the Number of records. HTT_RX_REO_RESOURCE_STATS_MAX */ + u32 sample_id; + u32 total_max; + u32 total_avg; + u32 total_sample; + u32 non_zeros_avg; + u32 non_zeros_sample; + u32 last_non_zeros_max; + u32 last_non_zeros_min; + u32 last_non_zeros_avg; + u32 last_non_zeros_sample; +}; + +/* == TX SOUNDING STATS == */ + +enum htt_txbf_sound_steer_modes { + HTT_IMPLICIT_TXBF_STEER_STATS = 0, + HTT_EXPLICIT_TXBF_SU_SIFS_STEER_STATS = 1, + HTT_EXPLICIT_TXBF_SU_RBO_STEER_STATS = 2, + HTT_EXPLICIT_TXBF_MU_SIFS_STEER_STATS = 3, + HTT_EXPLICIT_TXBF_MU_RBO_STEER_STATS = 4, + HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum htt_stats_sounding_tx_mode { + HTT_TX_AC_SOUNDING_MODE = 0, + HTT_TX_AX_SOUNDING_MODE = 1, +}; + +struct htt_tx_sounding_stats_tlv { + u32 tx_sounding_mode; /* HTT_TX_XX_SOUNDING_MODE */ + /* Counts number of soundings for all steering modes in each bw */ + u32 cbf_20[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_40[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_80[HTT_TXBF_MAX_NUM_OF_MODES]; + u32 cbf_160[HTT_TXBF_MAX_NUM_OF_MODES]; + /* + * The sounding array is a 2-D array stored as an 1-D array of + * u32. The stats for a particular user/bw combination is + * referenced with the following: + * + * sounding[(user* max_bw) + bw] + * + * ... where max_bw == 4 for 160mhz + */ + u32 sounding[HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; +}; + +struct htt_pdev_obss_pd_stats_tlv { + u32 num_obss_tx_ppdu_success; + u32 num_obss_tx_ppdu_failure; +}; + +void ath11k_debug_htt_stats_init(struct ath11k *ar); +#endif diff --git a/drivers/net/wireless/ath/ath11k/debugfs_sta.c b/drivers/net/wireless/ath/ath11k/debugfs_sta.c new file mode 100644 index 000000000000..743760c9bcae --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/vmalloc.h> + +#include "core.h" +#include "peer.h" +#include "debug.h" + +void +ath11k_accumulate_per_peer_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) +{ + struct rate_info *txrate = &arsta->txrate; + struct ath11k_htt_tx_stats *tx_stats; + int gi, mcs, bw, nss; + + if (!arsta->tx_stats) + return; + + tx_stats = arsta->tx_stats; + gi = FIELD_GET(RATE_INFO_FLAGS_SHORT_GI, arsta->txrate.flags); + mcs = txrate->mcs; + bw = txrate->bw; + nss = txrate->nss - 1; + +#define STATS_OP_FMT(name) tx_stats->stats[ATH11K_STATS_TYPE_##name] + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(SUCC).he[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).he[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).he[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).he[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).he[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).he[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_VHT_MCS) { + STATS_OP_FMT(SUCC).vht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).vht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).vht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).vht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).vht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).vht[1][mcs] += peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(SUCC).ht[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).ht[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).ht[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).ht[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).ht[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).ht[1][mcs] += peer_stats->retry_pkts; + } else { + mcs = legacy_rate_idx; + + STATS_OP_FMT(SUCC).legacy[0][mcs] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).legacy[1][mcs] += peer_stats->succ_pkts; + STATS_OP_FMT(FAIL).legacy[0][mcs] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).legacy[1][mcs] += peer_stats->failed_pkts; + STATS_OP_FMT(RETRY).legacy[0][mcs] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts; + } + + if (peer_stats->is_ampdu) { + tx_stats->ba_fails += peer_stats->ba_fails; + + if (txrate->flags & RATE_INFO_FLAGS_HE_MCS) { + STATS_OP_FMT(AMPDU).he[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).he[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else if (txrate->flags & RATE_INFO_FLAGS_MCS) { + STATS_OP_FMT(AMPDU).ht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).ht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + STATS_OP_FMT(AMPDU).vht[0][mcs] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).vht[1][mcs] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } + STATS_OP_FMT(AMPDU).bw[0][bw] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).nss[0][nss] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).gi[0][gi] += + peer_stats->succ_bytes + peer_stats->retry_bytes; + STATS_OP_FMT(AMPDU).bw[1][bw] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).nss[1][nss] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + STATS_OP_FMT(AMPDU).gi[1][gi] += + peer_stats->succ_pkts + peer_stats->retry_pkts; + } else { + tx_stats->ack_fails += peer_stats->ba_fails; + } + + STATS_OP_FMT(SUCC).bw[0][bw] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).nss[0][nss] += peer_stats->succ_bytes; + STATS_OP_FMT(SUCC).gi[0][gi] += peer_stats->succ_bytes; + + STATS_OP_FMT(SUCC).bw[1][bw] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).nss[1][nss] += peer_stats->succ_pkts; + STATS_OP_FMT(SUCC).gi[1][gi] += peer_stats->succ_pkts; + + STATS_OP_FMT(FAIL).bw[0][bw] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).nss[0][nss] += peer_stats->failed_bytes; + STATS_OP_FMT(FAIL).gi[0][gi] += peer_stats->failed_bytes; + + STATS_OP_FMT(FAIL).bw[1][bw] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).nss[1][nss] += peer_stats->failed_pkts; + STATS_OP_FMT(FAIL).gi[1][gi] += peer_stats->failed_pkts; + + STATS_OP_FMT(RETRY).bw[0][bw] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).nss[0][nss] += peer_stats->retry_bytes; + STATS_OP_FMT(RETRY).gi[0][gi] += peer_stats->retry_bytes; + + STATS_OP_FMT(RETRY).bw[1][bw] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).nss[1][nss] += peer_stats->retry_pkts; + STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts; + + tx_stats->tx_duration += peer_stats->duration; +} + +void ath11k_update_per_peer_stats_from_txcompl(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_tx_status *ts) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum hal_tx_rate_stats_bw bw; + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; + u16 rate; + u8 rate_idx; + int ret; + u8 mcs; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_warn(ab, "failed to find the peer\n"); + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE, + ts->rate_stats); + mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS, + ts->rate_stats); + sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI, + ts->rate_stats); + bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats); + + if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A || + pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + pkt_type, + &rate_idx, + &rate); + if (ret < 0) + goto err_out; + arsta->txrate.legacy = rate; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) { + if (mcs > 7) { + ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1); + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) { + if (mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs); + goto err_out; + } + + arsta->txrate.mcs = mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + if (sgi) + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + /* TODO */ + } + + arsta->txrate.nss = arsta->last_txrate.nss; + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); + + ath11k_accumulate_per_peer_tx_stats(arsta, peer_stats, rate_idx); +err_out: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_htt_data_stats *stats; + static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ", "fail", + "retry", "ampdu"}; + static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes", "packets"}; + int len = 0, i, j, k, retval = 0; + const int size = 2 * 4096; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + for (k = 0; k < ATH11K_STATS_TYPE_MAX; k++) { + for (j = 0; j < ATH11K_COUNTER_TYPE_MAX; j++) { + stats = &arsta->tx_stats->stats[k]; + len += scnprintf(buf + len, size - len, "%s_%s\n", + str_name[k], + str[j]); + len += scnprintf(buf + len, size - len, + " HE MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HE_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->he[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " VHT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_VHT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", + stats->vht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, " HT MCS %s\n", + str[j]); + for (i = 0; i < ATH11K_HT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, + " %llu ", stats->ht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " BW %s (20,40,80,160 MHz)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->bw[j][0], stats->bw[j][1], + stats->bw[j][2], stats->bw[j][3]); + len += scnprintf(buf + len, size - len, + " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->nss[j][0], stats->nss[j][1], + stats->nss[j][2], stats->nss[j][3]); + len += scnprintf(buf + len, size - len, + " GI %s (0.4us,0.8us,1.6us,3.2us)\n", + str[j]); + len += scnprintf(buf + len, size - len, + " %llu %llu %llu %llu\n", + stats->gi[j][0], stats->gi[j][1], + stats->gi[j][2], stats->gi[j][3]); + len += scnprintf(buf + len, size - len, + " legacy rate %s (1,2 ... Mbps)\n ", + str[j]); + for (i = 0; i < ATH11K_LEGACY_NUM; i++) + len += scnprintf(buf + len, size - len, "%llu ", + stats->legacy[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + } + } + + len += scnprintf(buf + len, size - len, + "\nTX duration\n %llu usecs\n", + arsta->tx_stats->tx_duration); + len += scnprintf(buf + len, size - len, + "BA fails\n %llu\n", arsta->tx_stats->ba_fails); + len += scnprintf(buf + len, size - len, + "ack fails\n %llu\n", arsta->tx_stats->ack_fails); + spin_unlock_bh(&ar->data_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_tx_stats = { + .read = ath11k_dbg_sta_dump_tx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + int len = 0, i, retval = 0; + const int size = 4096; + char *buf; + + if (!rx_stats) + return -ENOENT; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->ab->base_lock); + + len += scnprintf(buf + len, size - len, "RX peer stats:\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + len += scnprintf(buf + len, size - len, + "GI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", + rx_stats->gi_count[0], rx_stats->gi_count[1], + rx_stats->gi_count[2], rx_stats->gi_count[3]); + len += scnprintf(buf + len, size - len, + "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", + rx_stats->bw_count[0], rx_stats->bw_count[1], + rx_stats->bw_count[2], rx_stats->bw_count[3]); + len += scnprintf(buf + len, size - len, "BCC %llu LDPC %llu\n", + rx_stats->coding_count[0], rx_stats->coding_count[1]); + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + len += scnprintf(buf + len, size - len, "\nMCS(0-11) Legacy MCS(12):"); + for (i = 0; i < HAL_RX_MAX_MCS + 1; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->mcs_count[i]); + len += scnprintf(buf + len, size - len, "\nNSS(1-8):"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->nss_count[i]); + len += scnprintf(buf + len, size - len, "\nRX Duration:%llu ", + rx_stats->rx_duration); + len += scnprintf(buf + len, size - len, "\n"); + + spin_unlock_bh(&ar->ab->base_lock); + + if (len > size) + len = size; + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + return retval; +} + +static const struct file_operations fops_rx_stats = { + .read = ath11k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +ath11k_dbg_sta_open_htt_peer_stats(struct inode *inode, struct file *file) +{ + struct ieee80211_sta *sta = inode->i_private; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + struct debug_htt_stats_req *stats_req; + int ret; + + stats_req = vzalloc(sizeof(*stats_req) + ATH11K_HTT_STATS_BUF_SIZE); + if (!stats_req) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + ar->debug.htt_stats.stats_req = stats_req; + stats_req->type = ATH11K_DBG_HTT_EXT_STATS_PEER_INFO; + memcpy(stats_req->peer_addr, sta->addr, ETH_ALEN); + ret = ath11k_dbg_htt_stats_req(ar); + mutex_unlock(&ar->conf_mutex); + if (ret < 0) + goto out; + + file->private_data = stats_req; + return 0; +out: + vfree(stats_req); + return ret; +} + +static int +ath11k_dbg_sta_release_htt_peer_stats(struct inode *inode, struct file *file) +{ + vfree(file->private_data); + return 0; +} + +static ssize_t ath11k_dbg_sta_read_htt_peer_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct debug_htt_stats_req *stats_req = file->private_data; + char *buf; + u32 length = 0; + + buf = stats_req->buf; + length = min_t(u32, stats_req->buf_len, ATH11K_HTT_STATS_BUF_SIZE); + return simple_read_from_buffer(user_buf, count, ppos, buf, length); +} + +static const struct file_operations fops_htt_peer_stats = { + .open = ath11k_dbg_sta_open_htt_peer_stats, + .release = ath11k_dbg_sta_release_htt_peer_stats, + .read = ath11k_dbg_sta_read_htt_peer_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath11k_dbg_sta_write_peer_pktlog(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + int ret, enable; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + ret = kstrtoint_from_user(buf, count, 0, &enable); + if (ret) + goto out; + + ar->debug.pktlog_peer_valid = enable; + memcpy(ar->debug.pktlog_peer_addr, sta->addr, ETH_ALEN); + + /* Send peer based pktlog enable/disable */ + ret = ath11k_wmi_pdev_peer_pktlog_filter(ar, sta->addr, enable); + if (ret) { + ath11k_warn(ar->ab, "failed to set peer pktlog filter %pM: %d\n", + sta->addr, ret); + goto out; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "peer pktlog filter set to %d\n", + enable); + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_dbg_sta_read_peer_pktlog(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; + struct ath11k *ar = arsta->arvif->ar; + char buf[32] = {0}; + int len; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf), "%08x %pM\n", + ar->debug.pktlog_peer_valid, + ar->debug.pktlog_peer_addr); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_peer_pktlog = { + .write = ath11k_dbg_sta_write_peer_pktlog, + .read = ath11k_dbg_sta_read_peer_pktlog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath11k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + struct ath11k *ar = hw->priv; + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + debugfs_create_file("tx_stats", 0400, dir, sta, + &fops_tx_stats); + if (ath11k_debug_is_extd_rx_stats_enabled(ar)) + debugfs_create_file("rx_stats", 0400, dir, sta, + &fops_rx_stats); + + debugfs_create_file("htt_peer_stats", 0400, dir, sta, + &fops_htt_peer_stats); + + debugfs_create_file("peer_pktlog", 0644, dir, sta, + &fops_peer_pktlog); +} diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c new file mode 100644 index 000000000000..b112825a52ed --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include "core.h" +#include "dp_tx.h" +#include "hal_tx.h" +#include "debug.h" +#include "dp_rx.h" +#include "peer.h" + +static void ath11k_dp_htt_htc_tx_complete(struct ath11k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); +} + +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + + /* TODO: Any other peer specific DP cleanup */ + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to lookup peer %pM on vdev %d\n", + addr, vdev_id); + spin_unlock_bh(&ab->base_lock); + return; + } + + ath11k_peer_rx_tid_cleanup(ar, peer); + spin_unlock_bh(&ab->base_lock); +} + +int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr) +{ + struct ath11k_base *ab = ar->ab; + u32 reo_dest; + int ret; + + /* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ + reo_dest = ar->dp.mac_id + 1; + ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id, + WMI_PEER_SET_DEFAULT_ROUTING, + DP_RX_HASH_ENABLE | (reo_dest << 1)); + + if (ret) { + ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n", + ret, addr, vdev_id); + return ret; + } + + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, + HAL_DESC_REO_NON_QOS_TID, 1, 0); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for non-qos tid %d\n", + ret); + return ret; + } + + ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, 0, 1, 0); + if (ret) { + ath11k_warn(ab, "failed to setup rxd tid queue for tid 0 %d\n", + ret); + return ret; + } + + /* TODO: Setup other peer specific resource used in data path */ + + return 0; +} + +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) +{ + if (!ring->vaddr_unaligned) + return; + + dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned); + + ring->vaddr_unaligned = NULL; +} + +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries) +{ + struct hal_srng_params params = { 0 }; + int entry_sz = ath11k_hal_srng_get_entrysize(type); + int max_entries = ath11k_hal_srng_get_max_entries(type); + int ret; + + if (max_entries < 0 || entry_sz < 0) + return -EINVAL; + + if (num_entries > max_entries) + num_entries = max_entries; + + ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1; + ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, + &ring->paddr_unaligned, + GFP_KERNEL); + if (!ring->vaddr_unaligned) + return -ENOMEM; + + ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN); + ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr - + (unsigned long)ring->vaddr_unaligned); + + params.ring_base_vaddr = ring->vaddr; + params.ring_base_paddr = ring->paddr; + params.num_entries = num_entries; + + switch (type) { + case HAL_REO_DST: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_RX; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_RXDMA_BUF: + case HAL_RXDMA_MONITOR_BUF: + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 0; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_TX; + params.intr_timer_thres_us = + HAL_SRNG_INT_TIMER_THRESHOLD_TX; + break; + } + /* follow through when ring_num >= 3 */ + /* fall through */ + case HAL_REO_EXCEPTION: + case HAL_REO_REINJECT: + case HAL_REO_CMD: + case HAL_REO_STATUS: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_TCL_STATUS: + case HAL_WBM_IDLE_LINK: + case HAL_SW2WBM_RELEASE: + case HAL_RXDMA_DST: + case HAL_RXDMA_MONITOR_DST: + case HAL_RXDMA_MONITOR_DESC: + case HAL_RXDMA_DIR_BUF: + params.intr_batch_cntr_thres_entries = + HAL_SRNG_INT_BATCH_THRESHOLD_OTHER; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER; + break; + default: + ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type); + return -EINVAL; + } + + ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms); + if (ret < 0) { + ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", + ret, ring_num); + return ret; + } + + ring->ring_id = ret; + + return 0; +} + +static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); + } + ath11k_dp_srng_cleanup(ab, &dp->reo_reinject_ring); + ath11k_dp_srng_cleanup(ab, &dp->rx_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_except_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->reo_status_ring); +} + +static int ath11k_dp_srng_common_setup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; + int i, ret; + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, + HAL_SW2WBM_RELEASE, 0, 0, + DP_WBM_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up wbm2sw_release ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_cmd_ring, HAL_TCL_CMD, 0, 0, + DP_TCL_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_cmd ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS, + 0, 0, DP_TCL_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_status ring :%d\n", ret); + goto err; + } + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring, + HAL_TCL_DATA, i, 0, + DP_TCL_DATA_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_data ring (%d) :%d\n", + i, ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_comp_ring, + HAL_WBM2SW_RELEASE, i, 0, + DP_TX_COMP_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up tcl_comp ring ring (%d) :%d\n", + i, ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; + ath11k_hal_tx_init_data_ring(ab, srng); + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT, + 0, 0, DP_REO_REINJECT_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_reinject ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE, + 3, 0, DP_RX_RELEASE_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up rx_rel ring :%d\n", ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_except_ring, HAL_REO_EXCEPTION, + 0, 0, DP_REO_EXCEPTION_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_exception ring :%d\n", + ret); + goto err; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_cmd_ring, HAL_REO_CMD, + 0, 0, DP_REO_CMD_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_cmd ring :%d\n", ret); + goto err; + } + + srng = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id]; + ath11k_hal_reo_init_cmd_ring(ab, srng); + + ret = ath11k_dp_srng_setup(ab, &dp->reo_status_ring, HAL_REO_STATUS, + 0, 0, DP_REO_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ab, "failed to set up reo_status ring :%d\n", ret); + goto err; + } + + ath11k_hal_reo_hw_setup(ab); + + return 0; + +err: + ath11k_dp_srng_common_cleanup(ab); + + return ret; +} + +static void ath11k_dp_scatter_idle_link_desc_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + int i; + + for (i = 0; i < DP_IDLE_SCATTER_BUFS_MAX; i++) { + if (!slist[i].vaddr) + continue; + + dma_free_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + slist[i].vaddr, slist[i].paddr); + slist[i].vaddr = NULL; + } +} + +static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, + int size, + u32 n_link_desc_bank, + u32 n_link_desc, + u32 last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_link_desc_bank *link_desc_banks = dp->link_desc_banks; + struct hal_wbm_idle_scatter_list *slist = dp->scatter_list; + u32 n_entries_per_buf; + int num_scatter_buf, scatter_idx; + struct hal_wbm_link_desc *scatter_buf; + int align_bytes, n_entries; + dma_addr_t paddr; + int rem_entries; + int i; + int ret = 0; + u32 end_offset; + + n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / + ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK); + num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); + + if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) + return -EINVAL; + + for (i = 0; i < num_scatter_buf; i++) { + slist[i].vaddr = dma_alloc_coherent(ab->dev, + HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, + &slist[i].paddr, GFP_KERNEL); + if (!slist[i].vaddr) { + ret = -ENOMEM; + goto err; + } + } + + scatter_idx = 0; + scatter_buf = slist[scatter_idx].vaddr; + rem_entries = n_entries_per_buf; + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (DP_LINK_DESC_ALLOC_SIZE_THRESH - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries) { + ath11k_hal_set_link_desc_addr(scatter_buf, i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + if (rem_entries) { + rem_entries--; + scatter_buf++; + continue; + } + + rem_entries = n_entries_per_buf; + scatter_idx++; + scatter_buf = slist[scatter_idx].vaddr; + } + } + + end_offset = (scatter_buf - slist[scatter_idx].vaddr) * + sizeof(struct hal_wbm_link_desc); + ath11k_hal_setup_link_idle_list(ab, slist, num_scatter_buf, + n_link_desc, end_offset); + + return 0; + +err: + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + + return ret; +} + +static void +ath11k_dp_link_desc_bank_free(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks) +{ + int i; + + for (i = 0; i < DP_LINK_DESC_BANKS_MAX; i++) { + if (link_desc_banks[i].vaddr_unaligned) { + dma_free_coherent(ab->dev, + link_desc_banks[i].size, + link_desc_banks[i].vaddr_unaligned, + link_desc_banks[i].paddr_unaligned); + link_desc_banks[i].vaddr_unaligned = NULL; + } + } +} + +static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + int n_link_desc_bank, + int last_bank_sz) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + int ret = 0; + int desc_sz = DP_LINK_DESC_ALLOC_SIZE_THRESH; + + for (i = 0; i < n_link_desc_bank; i++) { + if (i == (n_link_desc_bank - 1) && last_bank_sz) + desc_sz = last_bank_sz; + + desc_bank[i].vaddr_unaligned = + dma_alloc_coherent(ab->dev, desc_sz, + &desc_bank[i].paddr_unaligned, + GFP_KERNEL); + if (!desc_bank[i].vaddr_unaligned) { + ret = -ENOMEM; + goto err; + } + + desc_bank[i].vaddr = PTR_ALIGN(desc_bank[i].vaddr_unaligned, + HAL_LINK_DESC_ALIGN); + desc_bank[i].paddr = desc_bank[i].paddr_unaligned + + ((unsigned long)desc_bank[i].vaddr - + (unsigned long)desc_bank[i].vaddr_unaligned); + desc_bank[i].size = desc_sz; + } + + return 0; + +err: + ath11k_dp_link_desc_bank_free(ab, dp->link_desc_banks); + + return ret; +} + +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring) +{ + ath11k_dp_link_desc_bank_free(ab, desc_bank); + + if (ring_type != HAL_RXDMA_MONITOR_DESC) { + ath11k_dp_srng_cleanup(ab, ring); + ath11k_dp_scatter_idle_link_desc_cleanup(ab); + } +} + +static int ath11k_wbm_idle_ring_setup(struct ath11k_base *ab, u32 *n_link_desc) +{ + struct ath11k_dp *dp = &ab->dp; + u32 n_mpdu_link_desc, n_mpdu_queue_desc; + u32 n_tx_msdu_link_desc, n_rx_msdu_link_desc; + int ret = 0; + + n_mpdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX) / + HAL_NUM_MPDUS_PER_LINK_DESC; + + n_mpdu_queue_desc = n_mpdu_link_desc / + HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC; + + n_tx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_FLOWS_PER_TID * + DP_AVG_MSDUS_PER_FLOW) / + HAL_NUM_TX_MSDUS_PER_LINK_DESC; + + n_rx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX * + DP_AVG_MSDUS_PER_MPDU) / + HAL_NUM_RX_MSDUS_PER_LINK_DESC; + + *n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc + + n_tx_msdu_link_desc + n_rx_msdu_link_desc; + + if (*n_link_desc & (*n_link_desc - 1)) + *n_link_desc = 1 << fls(*n_link_desc); + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_idle_ring, + HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + return ret; +} + +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc) +{ + u32 tot_mem_sz; + u32 n_link_desc_bank, last_bank_sz; + u32 entry_sz, align_bytes, n_entries; + u32 paddr; + u32 *desc; + int i, ret; + + tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE; + tot_mem_sz += HAL_LINK_DESC_ALIGN; + + if (tot_mem_sz <= DP_LINK_DESC_ALLOC_SIZE_THRESH) { + n_link_desc_bank = 1; + last_bank_sz = tot_mem_sz; + } else { + n_link_desc_bank = tot_mem_sz / + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + last_bank_sz = tot_mem_sz % + (DP_LINK_DESC_ALLOC_SIZE_THRESH - + HAL_LINK_DESC_ALIGN); + + if (last_bank_sz) + n_link_desc_bank += 1; + } + + if (n_link_desc_bank > DP_LINK_DESC_BANKS_MAX) + return -EINVAL; + + ret = ath11k_dp_link_desc_bank_alloc(ab, link_desc_banks, + n_link_desc_bank, last_bank_sz); + if (ret) + return ret; + + /* Setup link desc idle list for HW internal usage */ + entry_sz = ath11k_hal_srng_get_entrysize(ring_type); + tot_mem_sz = entry_sz * n_link_desc; + + /* Setup scatter desc list when the total memory requirement is more */ + if (tot_mem_sz > DP_LINK_DESC_ALLOC_SIZE_THRESH && + ring_type != HAL_RXDMA_MONITOR_DESC) { + ret = ath11k_dp_scatter_idle_link_desc_setup(ab, tot_mem_sz, + n_link_desc_bank, + n_link_desc, + last_bank_sz); + if (ret) { + ath11k_warn(ab, "failed to setup scatting idle list descriptor :%d\n", + ret); + goto fail_desc_bank_free; + } + + return 0; + } + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + for (i = 0; i < n_link_desc_bank; i++) { + align_bytes = link_desc_banks[i].vaddr - + link_desc_banks[i].vaddr_unaligned; + n_entries = (link_desc_banks[i].size - align_bytes) / + HAL_LINK_DESC_SIZE; + paddr = link_desc_banks[i].paddr; + while (n_entries && + (desc = ath11k_hal_srng_src_get_next_entry(ab, srng))) { + ath11k_hal_set_link_desc_addr((struct hal_wbm_link_desc *)desc, + i, paddr); + n_entries--; + paddr += HAL_LINK_DESC_SIZE; + } + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return 0; + +fail_desc_bank_free: + ath11k_dp_link_desc_bank_free(ab, link_desc_banks); + + return ret; +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget) +{ + struct napi_struct *napi = &irq_grp->napi; + int grp_id = irq_grp->grp_id; + int work_done = 0; + int i = 0; + int tot_work_done = 0; + + while (ath11k_tx_ring_mask[grp_id] >> i) { + if (ath11k_tx_ring_mask[grp_id] & BIT(i)) + ath11k_dp_tx_completion_handler(ab, i); + i++; + } + + if (ath11k_rx_err_ring_mask[grp_id]) { + work_done = ath11k_dp_process_rx_err(ab, napi, budget); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } + + if (ath11k_rx_wbm_rel_ring_mask[grp_id]) { + work_done = ath11k_dp_rx_process_wbm_err(ab, + napi, + budget); + budget -= work_done; + tot_work_done += work_done; + + if (budget <= 0) + goto done; + } + + if (ath11k_rx_ring_mask[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + if (ath11k_rx_ring_mask[grp_id] & BIT(i)) { + work_done = ath11k_dp_process_rx(ab, i, napi, + &irq_grp->pending_q, + budget); + budget -= work_done; + tot_work_done += work_done; + } + if (budget <= 0) + goto done; + } + } + + if (rx_mon_status_ring_mask[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + if (rx_mon_status_ring_mask[grp_id] & BIT(i)) { + work_done = + ath11k_dp_rx_process_mon_rings(ab, + i, napi, + budget); + budget -= work_done; + tot_work_done += work_done; + } + if (budget <= 0) + goto done; + } + } + + if (ath11k_reo_status_ring_mask[grp_id]) + ath11k_dp_process_reo_status(ab); + + for (i = 0; i < ab->num_radios; i++) { + if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) { + work_done = ath11k_dp_process_rxdma_err(ab, i, budget); + budget -= work_done; + tot_work_done += work_done; + } + + if (budget <= 0) + goto done; + + if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) { + struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0, + HAL_RX_BUF_RBM_SW3_BM, + GFP_ATOMIC); + } + } + /* TODO: Implement handler for other interrupts */ + +done: + return tot_work_done; +} + +void ath11k_dp_pdev_free(struct ath11k_base *ab) +{ + struct ath11k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ath11k_dp_rx_pdev_free(ab, i); + ath11k_debug_unregister(ar); + ath11k_dp_rx_pdev_mon_detach(ar); + } +} + +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + dp->mac_id = i; + idr_init(&dp->rx_refill_buf_ring.bufs_idr); + spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); + atomic_set(&dp->num_tx_pending, 0); + init_waitqueue_head(&dp->tx_empty_waitq); + idr_init(&dp->rx_mon_status_refill_ring.bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock); + idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); + spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); + } +} + +int ath11k_dp_pdev_alloc(struct ath11k_base *ab) +{ + struct ath11k *ar; + int ret; + int i; + + /* TODO:Per-pdev rx ring unlike tx ring which is mapped to different AC's */ + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + ret = ath11k_dp_rx_pdev_alloc(ab, i); + if (ret) { + ath11k_warn(ab, "failed to allocate pdev rx for pdev_id :%d\n", + i); + goto err; + } + ret = ath11k_dp_rx_pdev_mon_attach(ar); + if (ret) { + ath11k_warn(ab, "failed to initialize mon pdev %d\n", + i); + goto err; + } + } + + return 0; + +err: + ath11k_dp_pdev_free(ab); + + return ret; +} + +int ath11k_dp_htt_connect(struct ath11k_dp *dp) +{ + struct ath11k_htc_svc_conn_req conn_req; + struct ath11k_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath11k_dp_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath11k_dp_htt_htc_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH11K_HTC_SVC_ID_HTT_DATA_MSG; + + status = ath11k_htc_connect_service(&dp->ab->htc, &conn_req, + &conn_resp); + + if (status) + return status; + + dp->eid = conn_resp.eid; + + return 0; +} + +static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) +{ + /* For STA mode, enable address search index, + * tcl uses ast_hash value in the descriptor. + */ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_STA: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + break; + case WMI_VDEV_TYPE_AP: + case WMI_VDEV_TYPE_IBSS: + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + break; + case WMI_VDEV_TYPE_MONITOR: + default: + return; + } +} + +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif) +{ + arvif->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 1) | + FIELD_PREP(HTT_TCL_META_DATA_VDEV_ID, + arvif->vdev_id) | + FIELD_PREP(HTT_TCL_META_DATA_PDEV_ID, + ar->pdev->pdev_id); + + /* set HTT extension valid bit to 0 by default */ + arvif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT; + + ath11k_dp_update_vdev_search(arvif); +} + +static int ath11k_dp_tx_pending_cleanup(int buf_id, void *skb, void *ctx) +{ + struct ath11k_base *ab = (struct ath11k_base *)ctx; + struct sk_buff *msdu = skb; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, + DMA_TO_DEVICE); + + dev_kfree_skb_any(msdu); + + return 0; +} + +void ath11k_dp_free(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + ath11k_dp_srng_common_cleanup(ab); + + ath11k_dp_reo_cmd_list_cleanup(ab); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath11k_dp_tx_pending_cleanup, ab); + idr_destroy(&dp->tx_ring[i].txbuf_idr); + spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); + kfree(dp->tx_ring[i].tx_status); + } + + /* Deinit any SOC level resource */ +} + +int ath11k_dp_alloc(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng = NULL; + size_t size = 0; + u32 n_link_desc = 0; + int ret; + int i; + + dp->ab = ab; + + INIT_LIST_HEAD(&dp->reo_cmd_list); + INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); + spin_lock_init(&dp->reo_cmd_lock); + + ret = ath11k_wbm_idle_ring_setup(ab, &n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret); + return ret; + } + + srng = &ab->hal.srng_list[dp->wbm_idle_ring.ring_id]; + + ret = ath11k_dp_link_desc_setup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, srng, n_link_desc); + if (ret) { + ath11k_warn(ab, "failed to setup link desc: %d\n", ret); + return ret; + } + + ret = ath11k_dp_srng_common_setup(ab); + if (ret) + goto fail_link_desc_cleanup; + + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); + dp->tx_ring[i].tcl_data_ring_id = i; + + dp->tx_ring[i].tx_status_head = 0; + dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE - 1; + dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL); + if (!dp->tx_ring[i].tx_status) + goto fail_cmn_srng_cleanup; + } + + for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++) + ath11k_hal_tx_set_dscp_tid_map(ab, i); + + /* Init any SOC level resource for DP */ + + return 0; + +fail_cmn_srng_cleanup: + ath11k_dp_srng_common_cleanup(ab); + +fail_link_desc_cleanup: + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h new file mode 100644 index 000000000000..2f0980f2c762 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -0,0 +1,1527 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#ifndef ATH11K_DP_H +#define ATH11K_DP_H + +#include "hal_rx.h" + +struct ath11k_base; +struct ath11k_peer; +struct ath11k_dp; +struct ath11k_vif; +struct hal_tcl_status_ring; +struct ath11k_ext_irq_grp; + +struct dp_rx_tid { + u8 tid; + u32 *vaddr; + dma_addr_t paddr; + u32 size; + u32 ba_win_sz; + bool active; +}; + +#define DP_REO_DESC_FREE_TIMEOUT_MS 1000 + +struct dp_reo_cache_flush_elem { + struct list_head list; + struct dp_rx_tid data; + unsigned long ts; +}; + +struct dp_reo_cmd { + struct list_head list; + struct dp_rx_tid data; + int cmd_num; + void (*handler)(struct ath11k_dp *, void *, + enum hal_reo_cmd_status status); +}; + +struct dp_srng { + u32 *vaddr_unaligned; + u32 *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + int size; + u32 ring_id; +}; + +struct dp_rxdma_ring { + struct dp_srng refill_buf_ring; + struct idr bufs_idr; + /* Protects bufs_idr */ + spinlock_t idr_lock; + int bufs_max; +}; + +#define ATH11K_TX_COMPL_NEXT(x) (((x) + 1) % DP_TX_COMP_RING_SIZE) + +struct dp_tx_ring { + u8 tcl_data_ring_id; + struct dp_srng tcl_data_ring; + struct dp_srng tcl_comp_ring; + struct idr txbuf_idr; + /* Protects txbuf_idr and num_pending */ + spinlock_t tx_idr_lock; + struct hal_wbm_release_ring *tx_status; + int tx_status_head; + int tx_status_tail; +}; + +struct ath11k_pdev_mon_stats { + u32 status_ppdu_state; + u32 status_ppdu_start; + u32 status_ppdu_end; + u32 status_ppdu_compl; + u32 status_ppdu_start_mis; + u32 status_ppdu_end_mis; + u32 status_ppdu_done; + u32 dest_ppdu_done; + u32 dest_mpdu_done; + u32 dest_mpdu_drop; + u32 dup_mon_linkdesc_cnt; + u32 dup_mon_buf_cnt; +}; + +struct dp_link_desc_bank { + void *vaddr_unaligned; + void *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + u32 size; +}; + +/* Size to enforce scatter idle list mode */ +#define DP_LINK_DESC_ALLOC_SIZE_THRESH 0x200000 +#define DP_LINK_DESC_BANKS_MAX 8 + +#define DP_RX_DESC_COOKIE_INDEX_MAX 0x3ffff +#define DP_RX_DESC_COOKIE_POOL_ID_MAX 0x1c0000 +#define DP_RX_DESC_COOKIE_MAX \ + (DP_RX_DESC_COOKIE_INDEX_MAX | DP_RX_DESC_COOKIE_POOL_ID_MAX) +#define DP_NOT_PPDU_ID_WRAP_AROUND 20000 + +enum ath11k_dp_ppdu_state { + DP_PPDU_STATUS_START, + DP_PPDU_STATUS_DONE, +}; + +struct ath11k_mon_data { + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct hal_rx_mon_ppdu_info mon_ppdu_info; + + u32 mon_ppdu_status; + u32 mon_last_buf_cookie; + u64 mon_last_linkdesc_paddr; + u16 chan_noise_floor; + + struct ath11k_pdev_mon_stats rx_mon_stats; + /* lock for monitor data */ + spinlock_t mon_lock; + struct sk_buff_head rx_status_q; +}; + +struct ath11k_pdev_dp { + u32 mac_id; + atomic_t num_tx_pending; + wait_queue_head_t tx_empty_waitq; + struct dp_srng reo_dst_ring; + struct dp_rxdma_ring rx_refill_buf_ring; + struct dp_srng rxdma_err_dst_ring; + struct dp_srng rxdma_mon_dst_ring; + struct dp_srng rxdma_mon_desc_ring; + + struct dp_rxdma_ring rxdma_mon_buf_ring; + struct dp_rxdma_ring rx_mon_status_refill_ring; + struct ieee80211_rx_status rx_status; + struct ath11k_mon_data mon_data; +}; + +#define DP_NUM_CLIENTS_MAX 64 +#define DP_AVG_TIDS_PER_CLIENT 2 +#define DP_NUM_TIDS_MAX (DP_NUM_CLIENTS_MAX * DP_AVG_TIDS_PER_CLIENT) +#define DP_AVG_MSDUS_PER_FLOW 128 +#define DP_AVG_FLOWS_PER_TID 2 +#define DP_AVG_MPDUS_PER_TID_MAX 128 +#define DP_AVG_MSDUS_PER_MPDU 4 + +#define DP_RX_HASH_ENABLE 0 /* Disable hash based Rx steering */ + +#define DP_BA_WIN_SZ_MAX 256 + +#define DP_TCL_NUM_RING_MAX 3 + +#define DP_IDLE_SCATTER_BUFS_MAX 16 + +#define DP_WBM_RELEASE_RING_SIZE 64 +#define DP_TCL_DATA_RING_SIZE 512 +#define DP_TX_COMP_RING_SIZE 8192 +#define DP_TX_IDR_SIZE (DP_TX_COMP_RING_SIZE << 1) +#define DP_TCL_CMD_RING_SIZE 32 +#define DP_TCL_STATUS_RING_SIZE 32 +#define DP_REO_DST_RING_MAX 4 +#define DP_REO_DST_RING_SIZE 2048 +#define DP_REO_REINJECT_RING_SIZE 32 +#define DP_RX_RELEASE_RING_SIZE 1024 +#define DP_REO_EXCEPTION_RING_SIZE 128 +#define DP_REO_CMD_RING_SIZE 128 +#define DP_REO_STATUS_RING_SIZE 256 +#define DP_RXDMA_BUF_RING_SIZE 4096 +#define DP_RXDMA_REFILL_RING_SIZE 2048 +#define DP_RXDMA_ERR_DST_RING_SIZE 1024 +#define DP_RXDMA_MON_STATUS_RING_SIZE 1024 +#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + +#define DP_RX_BUFFER_SIZE 2048 +#define DP_RX_BUFFER_ALIGN_SIZE 128 + +#define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) +#define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(20, 18) + +#define DP_HW2SW_MACID(mac_id) ((mac_id) ? ((mac_id) - 1) : 0) +#define DP_SW2HW_MACID(mac_id) ((mac_id) + 1) + +#define DP_TX_DESC_ID_MAC_ID GENMASK(1, 0) +#define DP_TX_DESC_ID_MSDU_ID GENMASK(18, 2) +#define DP_TX_DESC_ID_POOL_ID GENMASK(20, 19) + +struct ath11k_dp { + struct ath11k_base *ab; + enum ath11k_htc_ep_id eid; + struct completion htt_tgt_version_received; + u8 htt_tgt_ver_major; + u8 htt_tgt_ver_minor; + struct dp_link_desc_bank link_desc_banks[DP_LINK_DESC_BANKS_MAX]; + struct dp_srng wbm_idle_ring; + struct dp_srng wbm_desc_rel_ring; + struct dp_srng tcl_cmd_ring; + struct dp_srng tcl_status_ring; + struct dp_srng reo_reinject_ring; + struct dp_srng rx_rel_ring; + struct dp_srng reo_except_ring; + struct dp_srng reo_cmd_ring; + struct dp_srng reo_status_ring; + struct dp_tx_ring tx_ring[DP_TCL_NUM_RING_MAX]; + struct hal_wbm_idle_scatter_list scatter_list[DP_IDLE_SCATTER_BUFS_MAX]; + struct list_head reo_cmd_list; + struct list_head reo_cmd_cache_flush_list; + /* protects access to reo_cmd_list and reo_cmd_cache_flush_list */ + spinlock_t reo_cmd_lock; +}; + +/* HTT definitions */ + +#define HTT_TCL_META_DATA_TYPE BIT(0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(1) + +/* vdev meta data */ +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) +#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) + +/* peer meta data */ +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) + +#define HTT_TX_WBM_COMP_STATUS_OFFSET 8 + +/* HTT tx completion is overlayed in wbm_release_ring */ +#define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(12, 9) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) +#define HTT_TX_WBM_COMP_INFO0_REINJECT_REASON GENMASK(16, 13) + +#define HTT_TX_WBM_COMP_INFO1_ACK_RSSI GENMASK(31, 24) + +struct htt_tx_wbm_completion { + u32 info0; + u32 info1; + u32 info2; + u32 info3; +} __packed; + +enum htt_h2t_msg_type { + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb, + HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc, + HTT_H2T_MSG_TYPE_EXT_STATS_CFG = 0x10, + HTT_H2T_MSG_TYPE_PPDU_STATS_CFG = 0x11, +}; + +#define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) + +struct htt_ver_req_cmd { + u32 ver_reg_info; +} __packed; + +enum htt_srng_ring_type { + HTT_HW_TO_SW_RING, + HTT_SW_TO_HW_RING, + HTT_SW_TO_SW_RING, +}; + +enum htt_srng_ring_id { + HTT_RXDMA_HOST_BUF_RING, + HTT_RXDMA_MONITOR_STATUS_RING, + HTT_RXDMA_MONITOR_BUF_RING, + HTT_RXDMA_MONITOR_DESC_RING, + HTT_RXDMA_MONITOR_DEST_RING, + HTT_HOST1_TO_FW_RXBUF_RING, + HTT_HOST2_TO_FW_RXBUF_RING, + HTT_RXDMA_NON_MONITOR_DEST_RING, +}; + +/* host -> target HTT_SRING_SETUP message + * + * After target is booted up, Host can send SRING setup message for + * each host facing LMAC SRING. Target setups up HW registers based + * on setup message and confirms back to Host if response_required is set. + * Host should wait for confirmation message before sending new SRING + * setup message + * + * The message would appear as follows: + * + * |31 24|23 20|19|18 16|15|14 8|7 0| + * |--------------- +-----------------+----------------+------------------| + * | ring_type | ring_id | pdev_id | msg_type | + * |----------------------------------------------------------------------| + * | ring_base_addr_lo | + * |----------------------------------------------------------------------| + * | ring_base_addr_hi | + * |----------------------------------------------------------------------| + * |ring_misc_cfg_flag|ring_entry_size| ring_size | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_head_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_lo | + * |----------------------------------------------------------------------| + * | ring_tail_offset32_remote_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_addr_lo | + * |----------------------------------------------------------------------| + * | ring_msi_addr_hi | + * |----------------------------------------------------------------------| + * | ring_msi_data | + * |----------------------------------------------------------------------| + * | intr_timer_th |IM| intr_batch_counter_th | + * |----------------------------------------------------------------------| + * | reserved |RR|PTCF| intr_low_threshold | + * |----------------------------------------------------------------------| + * Where + * IM = sw_intr_mode + * RR = response_required + * PTCF = prefetch_timer_cfg + * + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_SRING_SETUP + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id: identify which ring is to setup, + * more details can be got from enum htt_srng_ring_id + * b'24:31 - ring_type: identify type of host rings, + * more details can be got from enum htt_srng_ring_type + * dword1 - b'0:31 - ring_base_addr_lo: Lower 32bits of ring base address + * dword2 - b'0:31 - ring_base_addr_hi: Upper 32bits of ring base address + * dword3 - b'0:15 - ring_size: size of the ring in unit of 4-bytes words + * b'16:23 - ring_entry_size: Size of each entry in 4-byte word units + * b'24:31 - ring_misc_cfg_flag: Valid only for HW_TO_SW_RING and + * SW_TO_HW_RING. + * Refer to HTT_SRING_SETUP_RING_MISC_CFG_RING defs. + * dword4 - b'0:31 - ring_head_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword5 - b'0:31 - ring_head_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the head + * element within the ring. + * (The head offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword6 - b'0:31 - ring_tail_off32_remote_addr_lo: + * Lower 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword7 - b'0:31 - ring_tail_off32_remote_addr_hi: + * Upper 32 bits of memory address of the remote variable + * storing the 4-byte word offset that identifies the tail + * element within the ring. + * (The tail offset variable has type u32.) + * Valid for HW_TO_SW and SW_TO_SW rings. + * dword8 - b'0:31 - ring_msi_addr_lo: Lower 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword9 - b'0:31 - ring_msi_addr_hi: Upper 32bits of MSI cfg address + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword10 - b'0:31 - ring_msi_data: MSI data + * Refer to HTT_SRING_SETUP_RING_MSC_CFG_xxx defs + * valid only for HW_TO_SW_RING and SW_TO_HW_RING + * dword11 - b'0:14 - intr_batch_counter_th: + * batch counter threshold is in units of 4-byte words. + * HW internally maintains and increments batch count. + * (see SRING spec for detail description). + * When batch count reaches threshold value, an interrupt + * is generated by HW. + * b'15 - sw_intr_mode: + * This configuration shall be static. + * Only programmed at power up. + * 0: generate pulse style sw interrupts + * 1: generate level style sw interrupts + * b'16:31 - intr_timer_th: + * The timer init value when timer is idle or is + * initialized to start downcounting. + * In 8us units (to cover a range of 0 to 524 ms) + * dword12 - b'0:15 - intr_low_threshold: + * Used only by Consumer ring to generate ring_sw_int_p. + * Ring entries low threshold water mark, that is used + * in combination with the interrupt timer as well as + * the the clearing of the level interrupt. + * b'16:18 - prefetch_timer_cfg: + * Used only by Consumer ring to set timer mode to + * support Application prefetch handling. + * The external tail offset/pointer will be updated + * at following intervals: + * 3'b000: (Prefetch feature disabled; used only for debug) + * 3'b001: 1 usec + * 3'b010: 4 usec + * 3'b011: 8 usec (default) + * 3'b100: 16 usec + * Others: Reserverd + * b'19 - response_required: + * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response + * b'20:31 - reserved: reserved for future use + */ + +#define HTT_SRNG_SETUP_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_SRNG_SETUP_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO0_RING_TYPE GENMASK(31, 24) + +#define HTT_SRNG_SETUP_CMD_INFO1_RING_SIZE GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_ENTRY_SIZE GENMASK(23, 16) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_LOOP_CNT_DIS BIT(25) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_MSI_SWAP BIT(27) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_HOST_FW_SWAP BIT(28) +#define HTT_SRNG_SETUP_CMD_INFO1_RING_FLAGS_TLV_SWAP BIT(29) + +#define HTT_SRNG_SETUP_CMD_INTR_INFO_BATCH_COUNTER_THRESH GENMASK(14, 0) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_SW_INTR_MODE BIT(15) +#define HTT_SRNG_SETUP_CMD_INTR_INFO_INTR_TIMER_THRESH GENMASK(31, 16) + +#define HTT_SRNG_SETUP_CMD_INFO2_INTR_LOW_THRESH GENMASK(15, 0) +#define HTT_SRNG_SETUP_CMD_INFO2_PRE_FETCH_TIMER_CFG BIT(16) +#define HTT_SRNG_SETUP_CMD_INFO2_RESPONSE_REQUIRED BIT(19) + +struct htt_srng_setup_cmd { + u32 info0; + u32 ring_base_addr_lo; + u32 ring_base_addr_hi; + u32 info1; + u32 ring_head_off32_remote_addr_lo; + u32 ring_head_off32_remote_addr_hi; + u32 ring_tail_off32_remote_addr_lo; + u32 ring_tail_off32_remote_addr_hi; + u32 ring_msi_addr_lo; + u32 ring_msi_addr_hi; + u32 msi_data; + u32 intr_info; + u32 info2; +} __packed; + +/* host -> target FW PPDU_STATS config message + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW for PPDU_STATS_CFG msg. + * The message allows the host to configure the PPDU_STATS_IND messages + * produced by the target. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | REQ bit mask | pdev_mask | msg type | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a req to configure ppdu_stats_ind from target + * Value: 0x11 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies which pdevs this PPDU stats configuration applies to + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - REQ_TLV_BIT_MASK + * Bits 16:31 + * Purpose: each set bit indicates the corresponding PPDU stats TLV type + * needs to be included in the target's PPDU_STATS_IND messages. + * Value: refer htt_ppdu_stats_tlv_tag_t <<<??? + * + */ + +struct htt_ppdu_stats_cfg_cmd { + u32 msg; +} __packed; + +#define HTT_PPDU_STATS_CFG_MSG_TYPE GENMASK(7, 0) +#define HTT_PPDU_STATS_CFG_PDEV_ID GENMASK(16, 9) +#define HTT_PPDU_STATS_CFG_TLV_TYPE_BITMASK GENMASK(31, 16) + +enum htt_ppdu_stats_tag_type { + HTT_PPDU_STATS_TAG_COMMON, + HTT_PPDU_STATS_TAG_USR_COMMON, + HTT_PPDU_STATS_TAG_USR_RATE, + HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_64, + HTT_PPDU_STATS_TAG_USR_MPDU_ENQ_BITMAP_256, + HTT_PPDU_STATS_TAG_SCH_CMD_STATUS, + HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON, + HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_64, + HTT_PPDU_STATS_TAG_USR_COMPLTN_BA_BITMAP_256, + HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS, + HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH, + HTT_PPDU_STATS_TAG_USR_COMMON_ARRAY, + HTT_PPDU_STATS_TAG_INFO, + HTT_PPDU_STATS_TAG_TX_MGMTCTRL_PAYLOAD, + + /* New TLV's are added above to this line */ + HTT_PPDU_STATS_TAG_MAX, +}; + +#define HTT_PPDU_STATS_TAG_DEFAULT (BIT(HTT_PPDU_STATS_TAG_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_RATE) \ + | BIT(HTT_PPDU_STATS_TAG_SCH_CMD_STATUS) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH) \ + | BIT(HTT_PPDU_STATS_TAG_USR_COMMON_ARRAY)) + +/* HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG Message + * + * details: + * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG message is sent by host to + * configure RXDMA rings. + * The configuration is per ring based and includes both packet subtypes + * and PPDU/MPDU TLVs. + * + * The message would appear as follows: + * + * |31 26|25|24|23 16|15 8|7 0| + * |-----------------+----------------+----------------+---------------| + * | rsvd1 |PS|SS| ring_id | pdev_id | msg_type | + * |-------------------------------------------------------------------| + * | rsvd2 | ring_buffer_size | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_0 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_1 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_2 | + * |-------------------------------------------------------------------| + * | packet_type_enable_flags_3 | + * |-------------------------------------------------------------------| + * | tlv_filter_in_flags | + * |-------------------------------------------------------------------| + * Where: + * PS = pkt_swap + * SS = status_swap + * The message is interpreted as follows: + * dword0 - b'0:7 - msg_type: This will be set to + * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG + * b'8:15 - pdev_id: + * 0 (for rings at SOC/UMAC level), + * 1/2/3 mac id (for rings at LMAC level) + * b'16:23 - ring_id : Identify the ring to configure. + * More details can be got from enum htt_srng_ring_id + * b'24 - status_swap: 1 is to swap status TLV + * b'25 - pkt_swap: 1 is to swap packet TLV + * b'26:31 - rsvd1: reserved for future use + * dword1 - b'0:16 - ring_buffer_size: size of bufferes referenced by rx ring, + * in byte units. + * Valid only for HW_TO_SW_RING and SW_TO_HW_RING + * - b'16:31 - rsvd2: Reserved for future use + * dword2 - b'0:31 - packet_type_enable_flags_0: + * Enable MGMT packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * FP: Filter_Pass + * MD: Monitor_Direct + * MO: Monitor_Other + * 10 mgmt subtypes * 3 bits -> 30 bits + * Refer to PKT_TYPE_ENABLE_FLAG0_xxx_MGMT_xxx defs + * dword3 - b'0:31 - packet_type_enable_flags_1: + * Enable MGMT packet from 0b1010 to 0b1111 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG1_xxx_MGMT_xxx defs + * dword4 - b'0:31 - packet_type_enable_flags_2: + * Enable CTRL packet from 0b0000 to 0b1001 + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG2_xxx_CTRL_xxx defs + * dword5 - b'0:31 - packet_type_enable_flags_3: + * Enable CTRL packet from 0b1010 to 0b1111, + * MCAST_DATA, UCAST_DATA, NULL_DATA + * bits from low to high: FP, MD, MO - 3 bits + * Refer to PKT_TYPE_ENABLE_FLAG3_xxx_CTRL_xxx defs + * dword6 - b'0:31 - tlv_filter_in_flags: + * Filter in Attention/MPDU/PPDU/Header/User tlvs + * Refer to CFG_TLV_FILTER_IN_FLAG defs + */ + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PDEV_ID GENMASK(15, 8) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) + +enum htt_rx_filter_tlv_flags { + HTT_RX_FILTER_TLV_FLAGS_MPDU_START = BIT(0), + HTT_RX_FILTER_TLV_FLAGS_MSDU_START = BIT(1), + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET = BIT(2), + HTT_RX_FILTER_TLV_FLAGS_MSDU_END = BIT(3), + HTT_RX_FILTER_TLV_FLAGS_MPDU_END = BIT(4), + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER = BIT(5), + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER = BIT(6), + HTT_RX_FILTER_TLV_FLAGS_ATTENTION = BIT(7), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START = BIT(8), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END = BIT(9), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags0 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP = BIT(17), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(18), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(19), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV = BIT(20), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(21), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(22), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7 = BIT(23), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(24), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(25), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON = BIT(26), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(27), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(28), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM = BIT(29), +}; + +enum htt_rx_mgmt_pkt_filter_tlv_flags1 { + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(0), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(1), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC = BIT(2), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(3), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(4), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH = BIT(5), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(6), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(7), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH = BIT(8), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(9), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(10), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION = BIT(11), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(12), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(13), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK = BIT(14), + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(15), + HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(16), + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15 = BIT(17), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags2 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP = BIT(17), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(18), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(19), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT = BIT(20), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(21), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(22), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER = BIT(23), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(24), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(25), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR = BIT(26), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(27), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(28), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA = BIT(29), +}; + +enum htt_rx_ctrl_pkt_filter_tlv_flags3 { + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(0), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(1), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL = BIT(2), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(3), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(4), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS = BIT(5), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(6), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(7), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS = BIT(8), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(9), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(10), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK = BIT(11), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(12), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(13), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND = BIT(14), + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(15), + HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(16), + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK = BIT(17), +}; + +enum htt_rx_data_pkt_filter_tlv_flasg3 { + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(18), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(19), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST = BIT(20), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(21), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(22), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST = BIT(23), + HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(24), + HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(25), + HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA = BIT(26), +}; + +#define HTT_RX_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_REASSOC_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_REQ \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_RESP \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_PROBE_TIMING_ADV \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_BEACON \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_ATIM) + +#define HTT_RX_FP_MGMT_FILTER_FLAGS1 (HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MD_MGMT_FILTER_FLAGS1 (HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MD_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_MO_MGMT_FILTER_FLAGS1 (HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DISASSOC \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_AUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_DEAUTH \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION \ + | HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_ACTION_NOACK) + +#define HTT_RX_FP_CTRL_FILTER_FLASG2 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MD_CTRL_FILTER_FLASG2 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_MO_CTRL_FILTER_FLASG2 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_WRAPPER \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BAR \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_BA) + +#define HTT_RX_FP_CTRL_FILTER_FLASG3 (HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MD_CTRL_FILTER_FLASG3 (HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MD_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_MO_CTRL_FILTER_FLASG3 (HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_PSPOLL \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_RTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CTS \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_ACK \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND \ + | HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS3_CFEND_ACK) + +#define HTT_RX_FP_DATA_FILTER_FLASG3 (HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MD_DATA_FILTER_FLASG3 (HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MD_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MO_DATA_FILTER_FLASG3 (HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_MCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_UCAST \ + | HTT_RX_MO_DATA_PKT_FILTER_TLV_FLASG3_NULL_DATA) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS0 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS0 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS0 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS0_RESERVED_7) + +#define HTT_RX_MON_FP_MGMT_FILTER_FLAGS1 \ + (HTT_RX_FP_MGMT_FILTER_FLAGS1 | \ + HTT_RX_FP_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_MO_MGMT_FILTER_FLAGS1 \ + (HTT_RX_MO_MGMT_FILTER_FLAGS1 | \ + HTT_RX_MO_MGMT_PKT_FILTER_TLV_FLAGS1_RESERVED_15) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG2 \ + (HTT_RX_FP_CTRL_FILTER_FLASG2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG2 \ + (HTT_RX_MO_CTRL_FILTER_FLASG2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_1 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_2 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_TRIGGER | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_RESERVED_4 | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_BF_REP_POLL | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_VHT_NDP | \ + HTT_RX_MO_CTRL_PKT_FILTER_TLV_FLAGS2_CTRL_FRAME_EXT) + +#define HTT_RX_MON_FP_CTRL_FILTER_FLASG3 HTT_RX_FP_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_MO_CTRL_FILTER_FLASG3 HTT_RX_MO_CTRL_FILTER_FLASG3 + +#define HTT_RX_MON_FP_DATA_FILTER_FLASG3 HTT_RX_FP_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_MO_DATA_FILTER_FLASG3 HTT_RX_MO_DATA_FILTER_FLASG3 + +#define HTT_RX_MON_FILTER_TLV_FLAGS \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE) + +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_ATTENTION) + +struct htt_rx_ring_selection_cfg_cmd { + u32 info0; + u32 info1; + u32 pkt_type_en_flags0; + u32 pkt_type_en_flags1; + u32 pkt_type_en_flags2; + u32 pkt_type_en_flags3; + u32 rx_filter_tlv; +} __packed; + +struct htt_rx_ring_tlv_filter { + u32 rx_filter; /* see htt_rx_filter_tlv_flags */ + u32 pkt_filter_flags0; /* MGMT */ + u32 pkt_filter_flags1; /* MGMT */ + u32 pkt_filter_flags2; /* CTRL */ + u32 pkt_filter_flags3; /* DATA */ +}; + +/* HTT message target->host */ + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x1e, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x1f, + HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, + HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, +}; + +#define HTT_TARGET_VERSION_MAJOR 3 + +#define HTT_T2H_MSG_TYPE GENMASK(7, 0) +#define HTT_T2H_VERSION_CONF_MINOR GENMASK(15, 8) +#define HTT_T2H_VERSION_CONF_MAJOR GENMASK(23, 16) + +struct htt_t2h_version_conf_msg { + u32 version; +} __packed; + +#define HTT_T2H_PEER_MAP_INFO_VDEV_ID GENMASK(15, 8) +#define HTT_T2H_PEER_MAP_INFO_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) +#define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) +#define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 + +struct htt_t2h_peer_map_event { + u32 info; + u32 mac_addr_l32; + u32 info1; + u32 info2; +} __packed; + +#define HTT_T2H_PEER_UNMAP_INFO_VDEV_ID HTT_T2H_PEER_MAP_INFO_VDEV_ID +#define HTT_T2H_PEER_UNMAP_INFO_PEER_ID HTT_T2H_PEER_MAP_INFO_PEER_ID +#define HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16 \ + HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M +#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S + +struct htt_t2h_peer_unmap_event { + u32 info; + u32 mac_addr_l32; + u32 info1; +} __packed; + +struct htt_resp_msg { + union { + struct htt_t2h_version_conf_msg version_msg; + struct htt_t2h_peer_map_event peer_map_ev; + struct htt_t2h_peer_unmap_event peer_unmap_ev; + }; +} __packed; + +/* ppdu stats + * + * @details + * The following field definitions describe the format of the HTT target + * to host ppdu stats indication message. + * + * + * |31 16|15 12|11 10|9 8|7 0 | + * |----------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id | msg type | + * |----------------------------------------------------------------------| + * | ppdu_id | + * |----------------------------------------------------------------------| + * | Timestamp in us | + * |----------------------------------------------------------------------| + * | reserved | + * |----------------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_ppdu_stats.h) | + * |----------------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a PPDU STATS indication + * message. + * Value: 0x1d + * - mac_id + * Bits 9:8 + * Purpose: mac_id of this ppdu_id + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id of this ppdu_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: total tlv size + * Value: payload_size in bytes + */ + +#define HTT_T2H_PPDU_STATS_INFO_PDEV_ID GENMASK(11, 10) +#define HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE GENMASK(31, 16) + +struct ath11k_htt_ppdu_stats_msg { + u32 info; + u32 ppdu_id; + u32 timestamp; + u32 rsvd; + u8 data[0]; +} __packed; + +struct htt_tlv { + u32 header; + u8 value[0]; +} __packed; + +#define HTT_TLV_TAG GENMASK(11, 0) +#define HTT_TLV_LEN GENMASK(23, 12) + +enum HTT_PPDU_STATS_BW { + HTT_PPDU_STATS_BANDWIDTH_5MHZ = 0, + HTT_PPDU_STATS_BANDWIDTH_10MHZ = 1, + HTT_PPDU_STATS_BANDWIDTH_20MHZ = 2, + HTT_PPDU_STATS_BANDWIDTH_40MHZ = 3, + HTT_PPDU_STATS_BANDWIDTH_80MHZ = 4, + HTT_PPDU_STATS_BANDWIDTH_160MHZ = 5, /* includes 80+80 */ + HTT_PPDU_STATS_BANDWIDTH_DYN = 6, +}; + +#define HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_CMN_FLAGS_QUEUE_TYPE_M GENMASK(15, 8) +/* bw - HTT_PPDU_STATS_BW */ +#define HTT_PPDU_STATS_CMN_FLAGS_BW_M GENMASK(19, 16) + +struct htt_ppdu_stats_common { + u32 ppdu_id; + u16 sched_cmdid; + u8 ring_id; + u8 num_users; + u32 flags; /* %HTT_PPDU_STATS_COMMON_FLAGS_*/ + u32 chain_mask; + u32 fes_duration_us; /* frame exchange sequence */ + u32 ppdu_sch_eval_start_tstmp_us; + u32 ppdu_sch_end_tstmp_us; + u32 ppdu_start_tstmp_us; + /* BIT [15 : 0] - phy mode (WLAN_PHY_MODE) with which ppdu was transmitted + * BIT [31 : 16] - bandwidth (in MHz) with which ppdu was transmitted + */ + u16 phy_mode; + u16 bw_mhz; +} __packed; + +#define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M GENMASK(11, 4) + +#define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M BIT(0) +#define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M GENMASK(5, 1) + +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_FLAGS_LDPC_M BIT(29) + +#define HTT_USR_RATE_PREAMBLE(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_PREAMBLE_M, _val) +#define HTT_USR_RATE_BW(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_BW_M, _val) +#define HTT_USR_RATE_NSS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_NSS_M, _val) +#define HTT_USR_RATE_MCS(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_MCS_M, _val) +#define HTT_USR_RATE_GI(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val) + +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_HE_RE_M BIT(3) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_TXBF_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_BW_M GENMASK(11, 8) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_NSS_M GENMASK(15, 12) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_MCS_M GENMASK(19, 16) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_PREAMBLE_M GENMASK(23, 20) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_GI_M GENMASK(27, 24) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_DCM_M BIT(28) +#define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LDPC_M BIT(29) + +struct htt_ppdu_stats_user_rate { + u8 tid_num; + u8 reserved0; + u16 sw_peer_id; + u32 info0; /* %HTT_PPDU_STATS_USER_RATE_INFO0_*/ + u16 ru_end; + u16 ru_start; + u16 resp_ru_end; + u16 resp_ru_start; + u32 info1; /* %HTT_PPDU_STATS_USER_RATE_INFO1_ */ + u32 rate_flags; /* %HTT_PPDU_STATS_USER_RATE_FLAGS_ */ + /* Note: resp_rate_info is only valid for if resp_type is UL */ + u32 resp_rate_flags; /* %HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_ */ +} __packed; + +#define HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M GENMASK(7, 0) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M GENMASK(10, 9) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_BW_M GENMASK(13, 11) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_SGI_M BIT(14) +#define HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M GENMASK(31, 16) + +#define HTT_TX_INFO_IS_AMSDU(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_IS_AMPDU_M, _flags) +#define HTT_TX_INFO_BA_ACK_FAILED(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_BA_ACK_FAILED_M, _flags) +#define HTT_TX_INFO_RATECODE(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_RATECODE_M, _flags) +#define HTT_TX_INFO_PEERID(_flags) \ + FIELD_GET(HTT_PPDU_STATS_TX_INFO_FLAGS_PEERID_M, _flags) + +struct htt_tx_ppdu_stats_info { + struct htt_tlv tlv_hdr; + u32 tx_success_bytes; + u32 tx_retry_bytes; + u32 tx_failed_bytes; + u32 flags; /* %HTT_PPDU_STATS_TX_INFO_FLAGS_ */ + u16 tx_success_msdus; + u16 tx_retry_msdus; + u16 tx_failed_msdus; + u16 tx_duration; /* united in us */ +} __packed; + +enum htt_ppdu_stats_usr_compln_status { + HTT_PPDU_STATS_USER_STATUS_OK, + HTT_PPDU_STATS_USER_STATUS_FILTERED, + HTT_PPDU_STATS_USER_STATUS_RESP_TIMEOUT, + HTT_PPDU_STATS_USER_STATUS_RESP_MISMATCH, + HTT_PPDU_STATS_USER_STATUS_ABORT, +}; + +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M GENMASK(3, 0) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M GENMASK(7, 4) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M BIT(8) +#define HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_RESP_TYPE_M GENMASK(12, 9) + +#define HTT_USR_CMPLTN_IS_AMPDU(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_IS_AMPDU_M, _val) +#define HTT_USR_CMPLTN_LONG_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRY_M, _val) +#define HTT_USR_CMPLTN_SHORT_RETRY(_val) \ + FIELD_GET(HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_SHORT_RETRY_M, _val) + +struct htt_ppdu_stats_usr_cmpltn_cmn { + u8 status; + u8 tid_num; + u16 sw_peer_id; + /* RSSI value of last ack packet (units = dB above noise floor) */ + u32 ack_rssi; + u16 mpdu_tried; + u16 mpdu_success; + u32 flags; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_FLAGS_LONG_RETRIES*/ +} __packed; + +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MPDU_M GENMASK(8, 0) +#define HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M GENMASK(24, 9) +#define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(31, 25) + +#define HTT_PPDU_STATS_NON_QOS_TID 16 + +struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { + u32 ppdu_id; + u16 sw_peer_id; + u16 reserved0; + u32 info; /* %HTT_PPDU_STATS_USR_CMPLTN_CMN_INFO_ */ + u16 current_seq; + u16 start_seq; + u32 success_bytes; +} __packed; + +struct htt_ppdu_stats_usr_cmn_array { + struct htt_tlv tlv_hdr; + u32 num_ppdu_stats; + /* tx_ppdu_stats_info is filled by multiple struct htt_tx_ppdu_stats_info + * elements. + * tx_ppdu_stats_info is variable length, with length = + * number_of_ppdu_stats * sizeof (struct htt_tx_ppdu_stats_info) + */ + struct htt_tx_ppdu_stats_info tx_ppdu_info[0]; +} __packed; + +struct htt_ppdu_user_stats { + u16 peer_id; + u32 tlv_flags; + bool is_valid_peer_id; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; +}; + +#define HTT_PPDU_STATS_MAX_USERS 8 +#define HTT_PPDU_DESC_MAX_DEPTH 16 + +struct htt_ppdu_stats { + struct htt_ppdu_stats_common common; + struct htt_ppdu_user_stats user_stats[HTT_PPDU_STATS_MAX_USERS]; +}; + +struct htt_ppdu_stats_info { + u32 ppdu_id; + struct htt_ppdu_stats ppdu_stats; + struct list_head list; +}; + +/** + * @brief target -> host packet log message + * + * @details + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 16|15 12|11 10|9 8|7 0| + * |------------------------------------------------------------------| + * | payload_size | rsvd |pdev_id|mac_id| msg type | + * |------------------------------------------------------------------| + * | payload | + * |------------------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a pktlog message + * Value: HTT_T2H_MSG_TYPE_PKTLOG + * - mac_id + * Bits 9:8 + * Purpose: identifies which MAC/PHY instance generated this pktlog info + * Value: 0-3 + * - pdev_id + * Bits 11:10 + * Purpose: pdev_id + * Value: 0-3 + * 0 (for rings at SOC level), + * 1/2/3 PDEV -> 0/1/2 + * - payload_size + * Bits 31:16 + * Purpose: explicitly specify the payload size + * Value: payload size in bytes (payload size is a multiple of 4 bytes) + */ +struct htt_pktlog_msg { + u32 hdr; + u8 payload[0]; +}; + +/** + * @brief host -> target FW extended statistics retrieve + * + * @details + * The following field definitions describe the format of the HTT host + * to target FW extended stats retrieve message. + * The message specifies the type of stats the host wants to retrieve. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | reserved | stats type | pdev_mask | msg type | + * |-----------------------------------------------------------| + * | config param [0] | + * |-----------------------------------------------------------| + * | config param [1] | + * |-----------------------------------------------------------| + * | config param [2] | + * |-----------------------------------------------------------| + * | config param [3] | + * |-----------------------------------------------------------| + * | reserved | + * |-----------------------------------------------------------| + * | cookie LSBs | + * |-----------------------------------------------------------| + * | cookie MSBs | + * |-----------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a extended stats upload request message + * Value: 0x10 + * - PDEV_MASK + * Bits 8:15 + * Purpose: identifies the mask of PDEVs to retrieve stats from + * Value: This is a overloaded field, refer to usage and interpretation of + * PDEV in interface document. + * Bit 8 : Reserved for SOC stats + * Bit 9 - 15 : Indicates PDEV_MASK in DBDC + * Indicates MACID_MASK in DBS + * - STATS_TYPE + * Bits 23:16 + * Purpose: identifies which FW statistics to upload + * Value: Defined by htt_dbg_ext_stats_type (see htt_stats.h) + * - Reserved + * Bits 31:24 + * - CONFIG_PARAM [0] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [1] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [2] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - CONFIG_PARAM [3] + * Bits 31:0 + * Purpose: give an opaque configuration value to the specified stats type + * Value: stats-type specific configuration value + * Refer to htt_stats.h for interpretation for each stats sub_type + * - Reserved [31:0] for future use. + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + */ + +struct htt_ext_stats_cfg_hdr { + u8 msg_type; + u8 pdev_mask; + u8 stats_type; + u8 reserved; +} __packed; + +struct htt_ext_stats_cfg_cmd { + struct htt_ext_stats_cfg_hdr hdr; + u32 cfg_param0; + u32 cfg_param1; + u32 cfg_param2; + u32 cfg_param3; + u32 reserved; + u32 cookie_lsb; + u32 cookie_msb; +} __packed; + +/* htt stats config default params */ +#define HTT_STAT_DEFAULT_RESET_START_OFFSET 0 +#define HTT_STAT_DEFAULT_CFG0_ALL_HWQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_TXQS 0xffffffff +#define HTT_STAT_DEFAULT_CFG0_ALL_CMDQS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ALL_RINGS 0xffff +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_PEERS 0xff +#define HTT_STAT_DEFAULT_CFG0_CCA_CUMULATIVE 0x00 +#define HTT_STAT_DEFAULT_CFG0_ACTIVE_VDEVS 0x00 + +/* HTT_DBG_EXT_STATS_PEER_INFO + * PARAMS: + * @config_param0: + * [Bit0] - [0] for sw_peer_id, [1] for mac_addr based request + * [Bit15 : Bit 1] htt_peer_stats_req_mode_t + * [Bit31 : Bit16] sw_peer_id + * @config_param1: + * peer_stats_req_type_mask:32 (enum htt_peer_stats_tlv_enum) + * 0 bit htt_peer_stats_cmn_tlv + * 1 bit htt_peer_details_tlv + * 2 bit htt_tx_peer_rate_stats_tlv + * 3 bit htt_rx_peer_rate_stats_tlv + * 4 bit htt_tx_tid_stats_tlv/htt_tx_tid_stats_v1_tlv + * 5 bit htt_rx_tid_stats_tlv + * 6 bit htt_msdu_flow_stats_tlv + * @config_param2: [Bit31 : Bit0] mac_addr31to0 + * @config_param3: [Bit15 : Bit0] mac_addr47to32 + * [Bit31 : Bit16] reserved + */ +#define HTT_STAT_PEER_INFO_MAC_ADDR BIT(0) +#define HTT_STAT_DEFAULT_PEER_REQ_TYPE 0x7f + +/* Used to set different configs to the specified stats type.*/ +struct htt_ext_stats_cfg_params { + u32 cfg0; + u32 cfg1; + u32 cfg2; + u32 cfg3; +}; + +/** + * @brief target -> host extended statistics upload + * + * @details + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a single stats can span over multiple HTT stats indication + * due to the HTT message size limitation so every HTT ext stats indication + * will have tag-length-value stats information elements. + * The tag-length header for each HTT stats IND message also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A Done bit 1's indicate the end of the of stats info elements. + * + * + * |31 16|15 12|11|10 8|7 5|4 0| + * |--------------------------------------------------------------| + * | reserved | msg type | + * |--------------------------------------------------------------| + * | cookie LSBs | + * |--------------------------------------------------------------| + * | cookie MSBs | + * |--------------------------------------------------------------| + * | stats entry length | rsvd | D| S | stat type | + * |--------------------------------------------------------------| + * | type-specific stats info | + * | (see htt_stats.h) | + * |--------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: Identifies this is a extended statistics upload confirmation + * message. + * Value: 0x1c + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 7:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_ext_stats_type + * - STATUS + * Bits 10:8 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_ext_stats_status + * - DONE + * Bits 11 + * Purpose: + * Indicates the completion of the stats entry, this will be the last + * stats conf HTT segment for the requested stats type. + * Value: + * 0 -> the stats retrieval is ongoing + * 1 -> the stats retrieval is complete + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. + */ + +#define HTT_T2H_EXT_STATS_INFO1_LENGTH GENMASK(31, 16) + +struct ath11k_htt_extd_stats_msg { + u32 info0; + u64 cookie; + u32 info1; + u8 data[0]; +} __packed; + +struct htt_mac_addr { + u32 mac_addr_l32; + u32 mac_addr_h16; +}; + +static inline void ath11k_dp_get_mac_addr(u32 addr_l32, u16 addr_h16, u8 *addr) +{ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + addr_l32 = swab32(addr_l32); + addr_h16 = swab16(addr_h16); + } + + memcpy(addr, &addr_l32, 4); + memcpy(addr + 4, &addr_h16, ETH_ALEN - 4); +} + +int ath11k_dp_service_srng(struct ath11k_base *ab, + struct ath11k_ext_irq_grp *irq_grp, + int budget); +int ath11k_dp_htt_connect(struct ath11k_dp *dp); +void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif); +void ath11k_dp_free(struct ath11k_base *ab); +int ath11k_dp_alloc(struct ath11k_base *ab); +int ath11k_dp_pdev_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab); +void ath11k_dp_pdev_free(struct ath11k_base *ab); +int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, + int mac_id, enum hal_ring_type ring_type); +int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr); +void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring); +int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, + enum hal_ring_type type, int ring_num, + int mac_id, int num_entries); +void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab, + struct dp_link_desc_bank *desc_bank, + u32 ring_type, struct dp_srng *ring); +int ath11k_dp_link_desc_setup(struct ath11k_base *ab, + struct dp_link_desc_bank *link_desc_banks, + u32 ring_type, struct hal_srng *srng, + u32 n_link_desc); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c new file mode 100644 index 000000000000..3a3dc7680622 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -0,0 +1,4194 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + */ + +#include <linux/ieee80211.h> +#include "core.h" +#include "debug.h" +#include "hal_desc.h" +#include "hw.h" +#include "dp_rx.h" +#include "hal_rx.h" +#include "dp_tx.h" +#include "peer.h" + +static u8 *ath11k_dp_rx_h_80211_hdr(struct hal_rx_desc *desc) +{ + return desc->hdr_status; +} + +static enum hal_encrypt_type ath11k_dp_rx_h_mpdu_start_enctype(struct hal_rx_desc *desc) +{ + if (!(__le32_to_cpu(desc->mpdu_start.info1) & + RX_MPDU_START_INFO1_ENCRYPT_INFO_VALID)) + return HAL_ENCRYPT_TYPE_OPEN; + + return FIELD_GET(RX_MPDU_START_INFO2_ENC_TYPE, + __le32_to_cpu(desc->mpdu_start.info2)); +} + +static u8 ath11k_dp_rx_h_mpdu_start_decap_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MPDU_START_INFO5_DECAP_TYPE, + __le32_to_cpu(desc->mpdu_start.info5)); +} + +static bool ath11k_dp_rx_h_attn_msdu_done(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO2_MSDU_DONE, + __le32_to_cpu(desc->attention.info2)); +} + +static bool ath11k_dp_rx_h_attn_first_mpdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_FIRST_MPDU, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_l4_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_TCP_UDP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_ip_cksum_fail(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_ATTENTION_INFO1_IP_CKSUM_FAIL, + __le32_to_cpu(desc->attention.info1)); +} + +static bool ath11k_dp_rx_h_attn_is_decrypted(struct hal_rx_desc *desc) +{ + return (FIELD_GET(RX_ATTENTION_INFO2_DCRYPT_STATUS_CODE, + __le32_to_cpu(desc->attention.info2)) == + RX_DESC_DECRYPT_STATUS_CODE_OK); +} + +static u32 ath11k_dp_rx_h_attn_mpdu_err(struct hal_rx_desc *desc) +{ + u32 info = __le32_to_cpu(desc->attention.info1); + u32 errmap = 0; + + if (info & RX_ATTENTION_INFO1_FCS_ERR) + errmap |= DP_RX_MPDU_ERR_FCS; + + if (info & RX_ATTENTION_INFO1_DECRYPT_ERR) + errmap |= DP_RX_MPDU_ERR_DECRYPT; + + if (info & RX_ATTENTION_INFO1_TKIP_MIC_ERR) + errmap |= DP_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_ATTENTION_INFO1_A_MSDU_ERROR) + errmap |= DP_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_ATTENTION_INFO1_OVERFLOW_ERR) + errmap |= DP_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_ATTENTION_INFO1_MSDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_ATTENTION_INFO1_MPDU_LEN_ERR) + errmap |= DP_RX_MPDU_ERR_MPDU_LEN; + + return errmap; +} + +static u16 ath11k_dp_rx_h_msdu_start_msdu_len(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO1_MSDU_LENGTH, + __le32_to_cpu(desc->msdu_start.info1)); +} + +static u8 ath11k_dp_rx_h_msdu_start_sgi(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_SGI, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rate_mcs(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RATE_MCS, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_rx_bw(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_RECV_BW, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u32 ath11k_dp_rx_h_msdu_start_freq(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->msdu_start.phy_meta_data); +} + +static u8 ath11k_dp_rx_h_msdu_start_pkt_type(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_START_INFO3_PKT_TYPE, + __le32_to_cpu(desc->msdu_start.info3)); +} + +static u8 ath11k_dp_rx_h_msdu_start_nss(struct hal_rx_desc *desc) +{ + u8 mimo_ss_bitmap = FIELD_GET(RX_MSDU_START_INFO3_MIMO_SS_BITMAP, + __le32_to_cpu(desc->msdu_start.info3)); + + return hweight8(mimo_ss_bitmap); +} + +static u8 ath11k_dp_rx_h_msdu_end_l3pad(struct hal_rx_desc *desc) +{ + return FIELD_GET(RX_MSDU_END_INFO2_L3_HDR_PADDING, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_first_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static bool ath11k_dp_rx_h_msdu_end_last_msdu(struct hal_rx_desc *desc) +{ + return !!FIELD_GET(RX_MSDU_END_INFO2_LAST_MSDU, + __le32_to_cpu(desc->msdu_end.info2)); +} + +static void ath11k_dp_rx_desc_end_tlv_copy(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy((u8 *)&fdesc->msdu_end, (u8 *)&ldesc->msdu_end, + sizeof(struct rx_msdu_end)); + memcpy((u8 *)&fdesc->attention, (u8 *)&ldesc->attention, + sizeof(struct rx_attention)); + memcpy((u8 *)&fdesc->mpdu_end, (u8 *)&ldesc->mpdu_end, + sizeof(struct rx_mpdu_end)); +} + +static u32 ath11k_dp_rxdesc_get_mpdulen_err(struct hal_rx_desc *rx_desc) +{ + struct rx_attention *rx_attn; + + rx_attn = &rx_desc->attention; + + return FIELD_GET(RX_ATTENTION_INFO1_MPDU_LEN_ERR, + __le32_to_cpu(rx_attn->info1)); +} + +static u32 ath11k_dp_rxdesc_get_decap_format(struct hal_rx_desc *rx_desc) +{ + struct rx_msdu_start *rx_msdu_start; + + rx_msdu_start = &rx_desc->msdu_start; + + return FIELD_GET(RX_MSDU_START_INFO2_DECAP_FORMAT, + __le32_to_cpu(rx_msdu_start->info2)); +} + +static u8 *ath11k_dp_rxdesc_get_80211hdr(struct hal_rx_desc *rx_desc) +{ + u8 *rx_pkt_hdr; + + rx_pkt_hdr = &rx_desc->msdu_payload[0]; + + return rx_pkt_hdr; +} + +static bool ath11k_dp_rxdesc_mpdu_valid(struct hal_rx_desc *rx_desc) +{ + u32 tlv_tag; + + tlv_tag = FIELD_GET(HAL_TLV_HDR_TAG, + __le32_to_cpu(rx_desc->mpdu_start_tag)); + + return tlv_tag == HAL_RX_MPDU_START ? true : false; +} + +static u32 ath11k_dp_rxdesc_get_ppduid(struct hal_rx_desc *rx_desc) +{ + return __le16_to_cpu(rx_desc->mpdu_start.phy_ppdu_id); +} + +/* Returns number of Rx buffers replenished */ +int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, + enum hal_rx_buf_return_buf_manager mgr, + gfp_t gfp) +{ + struct hal_srng *srng; + u32 *desc; + struct sk_buff *skb; + int num_free; + int num_remain; + int buf_id; + u32 cookie; + dma_addr_t paddr; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath11k_hal_srng_access_begin(ab, srng); + + num_free = ath11k_hal_srng_src_num_free(ab, srng, true); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = dev_alloc_skb(DP_RX_BUFFER_SIZE + + DP_RX_BUFFER_ALIGN_SIZE); + if (!skb) + break; + + if (!IS_ALIGNED((unsigned long)skb->data, + DP_RX_BUFFER_ALIGN_SIZE)) { + skb_pull(skb, + PTR_ALIGN(skb->data, DP_RX_BUFFER_ALIGN_SIZE) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max * 3, gfp); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id < 0) + goto fail_dma_unmap; + + desc = ath11k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_idr_remove; + + ATH11K_SKB_RXCB(skb)->paddr = paddr; + + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, mac_id) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + + num_remain--; + + ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_idr_remove: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); + + ath11k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + +static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct sk_buff *skb; + int buf_id; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* TODO: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + + rx_ring = &dp->rx_mon_status_refill_ring; + + spin_lock_bh(&rx_ring->idr_lock); + idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { + idr_remove(&rx_ring->bufs_idr, buf_id); + /* XXX: Understand where internal driver does this dma_unmap of + * of rxdma_buffer. + */ + dma_unmap_single(ar->ab->dev, ATH11K_SKB_RXCB(skb)->paddr, + skb->len + skb_tailroom(skb), DMA_BIDIRECTIONAL); + dev_kfree_skb_any(skb); + } + + idr_destroy(&rx_ring->bufs_idr); + spin_unlock_bh(&rx_ring->idr_lock); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + return 0; +} + +static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar, + struct dp_rxdma_ring *rx_ring, + u32 ringtype) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + int num_entries; + + num_entries = rx_ring->refill_buf_ring.size / + ath11k_hal_srng_get_entrysize(ringtype); + + rx_ring->bufs_max = num_entries; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, + HAL_RX_BUF_RBM_SW3_BM, GFP_KERNEL); + return 0; +} + +static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); + + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); + + rx_ring = &dp->rx_mon_status_refill_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); + + return 0; +} + +static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_refill_buf_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_err_dst_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rx_mon_status_refill_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); +} + +void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab) +{ + struct ath11k_pdev_dp *dp; + struct ath11k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + ath11k_dp_srng_cleanup(ab, &dp->reo_dst_ring); + } +} + +int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab) +{ + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + int ret; + int i; + + for (i = 0; i < ab->num_radios; i++) { + ar = ab->pdevs[i].ar; + dp = &ar->dp; + ret = ath11k_dp_srng_setup(ab, &dp->reo_dst_ring, HAL_REO_DST, + dp->mac_id, dp->mac_id, + DP_REO_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup reo_dst_ring\n"); + goto err_reo_cleanup; + } + } + + return 0; + +err_reo_cleanup: + ath11k_dp_pdev_reo_cleanup(ab); + + return ret; +} + +static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) +{ + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_srng *srng = NULL; + int ret; + + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rx_refill_buf_ring.refill_buf_ring, + HAL_RXDMA_BUF, 0, + dp->mac_id, DP_RXDMA_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx_refill_buf_ring\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring, + HAL_RXDMA_DST, 0, dp->mac_id, + DP_RXDMA_ERR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring\n"); + return ret; + } + + srng = &dp->rx_mon_status_refill_ring.refill_buf_ring; + ret = ath11k_dp_srng_setup(ar->ab, + srng, + HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup rx_mon_status_refill_ring\n"); + return ret; + } + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rxdma_mon_buf_ring.refill_buf_ring, + HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id, + DP_RXDMA_MONITOR_BUF_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_BUF\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_dst_ring, + HAL_RXDMA_MONITOR_DST, 0, dp->mac_id, + DP_RXDMA_MONITOR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DST\n"); + return ret; + } + + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_desc_ring, + HAL_RXDMA_MONITOR_DESC, 0, dp->mac_id, + DP_RXDMA_MONITOR_DESC_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup HAL_RXDMA_MONITOR_DESC\n"); + return ret; + } + + return 0; +} + +void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) +{ + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *cmd, *tmp; + struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; + + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + list_del(&cmd->list); + dma_unmap_single(ab->dev, cmd->data.paddr, + cmd->data.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.vaddr); + kfree(cmd); + } + + list_for_each_entry_safe(cmd_cache, tmp_cache, + &dp->reo_cmd_cache_flush_list, list) { + list_del(&cmd_cache->list); + dma_unmap_single(ab->dev, cmd_cache->data.paddr, + cmd_cache->data.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.vaddr); + kfree(cmd_cache); + } + spin_unlock_bh(&dp->reo_cmd_lock); +} + +static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct dp_rx_tid *rx_tid = ctx; + + if (status != HAL_REO_CMD_SUCCESS) + ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", + rx_tid->tid, status); + + dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, + struct dp_rx_tid *rx_tid) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + unsigned long tot_desc_sz, desc_sz; + int ret; + + tot_desc_sz = rx_tid->size; + desc_sz = ath11k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); + + while (tot_desc_sz > desc_sz) { + tot_desc_sz -= desc_sz; + cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, &cmd, + NULL); + if (ret) + ath11k_warn(ab, + "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n", + rx_tid->tid, ret); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag |= HAL_REO_CMD_FLG_NEED_STATUS; + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, + &cmd, ath11k_dp_reo_cmd_free); + if (ret) { + ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", + rx_tid->tid, ret); + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } +} + +static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) +{ + struct ath11k_base *ab = dp->ab; + struct dp_rx_tid *rx_tid = ctx; + struct dp_reo_cache_flush_elem *elem, *tmp; + + if (status == HAL_REO_CMD_DRAIN) { + goto free_desc; + } else if (status != HAL_REO_CMD_SUCCESS) { + /* Shouldn't happen! Cleanup in case of other failure? */ + ath11k_warn(ab, "failed to delete rx tid %d hw descriptor %d\n", + rx_tid->tid, status); + return; + } + + elem = kzalloc(sizeof(*elem), GFP_ATOMIC); + if (!elem) + goto free_desc; + + elem->ts = jiffies; + memcpy(&elem->data, rx_tid, sizeof(*rx_tid)); + + spin_lock_bh(&dp->reo_cmd_lock); + list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list); + spin_unlock_bh(&dp->reo_cmd_lock); + + /* Flush and invalidate aged REO desc from HW cache */ + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list, + list) { + if (time_after(jiffies, elem->ts + + msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) { + list_del(&elem->list); + spin_unlock_bh(&dp->reo_cmd_lock); + + ath11k_dp_reo_cache_flush(ab, &elem->data); + kfree(elem); + spin_lock_bh(&dp->reo_cmd_lock); + } + } + spin_unlock_bh(&dp->reo_cmd_lock); + + return; +free_desc: + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); +} + +static void ath11k_peer_rx_tid_delete(struct ath11k *ar, + struct ath11k_peer *peer, u8 tid) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + struct dp_rx_tid *rx_tid = &peer->rx_tid[tid]; + int ret; + + if (!rx_tid->active) + return; + + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + ath11k_dp_rx_tid_del_func); + if (ret) { + ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", + tid, ret); + dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + } + + rx_tid->active = false; +} + +void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) +{ + int i; + + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + ath11k_peer_rx_tid_delete(ar, peer, i); +} + +static int ath11k_peer_rx_tid_reo_update(struct ath11k *ar, + struct ath11k_peer *peer, + struct dp_rx_tid *rx_tid, + u32 ba_win_sz, u16 ssn, + bool update_ssn) +{ + struct ath11k_hal_reo_cmd cmd = {0}; + int ret; + + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; + cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; + cmd.ba_window_size = ba_win_sz; + + if (update_ssn) { + cmd.upd0 |= HAL_REO_CMD_UPD0_SSN; + cmd.upd2 = FIELD_PREP(HAL_REO_CMD_UPD2_SSN, ssn); + } + + ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, + HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, + NULL); + if (ret) { + ath11k_warn(ar->ab, "failed to update rx tid queue, tid %d (%d)\n", + rx_tid->tid, ret); + return ret; + } + + rx_tid->ba_win_sz = ba_win_sz; + + return 0; +} + +static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, + const u8 *peer_mac, int vdev_id, u8 tid) +{ + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to free up rx tid mem\n"); + goto unlock_exit; + } + + rx_tid = &peer->rx_tid[tid]; + if (!rx_tid->active) + goto unlock_exit; + + dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + + rx_tid->active = false; + +unlock_exit: + spin_unlock_bh(&ab->base_lock); +} + +int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + u32 hw_desc_sz; + u32 *addr_aligned; + void *vaddr; + dma_addr_t paddr; + int ret; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to set up rx tid\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + rx_tid = &peer->rx_tid[tid]; + /* Update the tid queue if it is already setup */ + if (rx_tid->active) { + paddr = rx_tid->paddr; + ret = ath11k_peer_rx_tid_reo_update(ar, peer, rx_tid, + ba_win_sz, ssn, true); + spin_unlock_bh(&ab->base_lock); + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d\n", tid); + return ret; + } + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + peer_mac, paddr, + tid, 1, ba_win_sz); + if (ret) + ath11k_warn(ab, "failed to send wmi command to update rx reorder queue, tid :%d (%d)\n", + tid, ret); + return ret; + } + + rx_tid->tid = tid; + + rx_tid->ba_win_sz = ba_win_sz; + + /* TODO: Optimize the memory allocation for qos tid based on the + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath11k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_KERNEL); + if (!vaddr) { + spin_unlock_bh(&ab->base_lock); + return -ENOMEM; + } + + addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn); + + paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + + ret = dma_mapping_error(ab->dev, paddr); + if (ret) { + spin_unlock_bh(&ab->base_lock); + goto err_mem_free; + } + + rx_tid->vaddr = vaddr; + rx_tid->paddr = paddr; + rx_tid->size = hw_desc_sz; + rx_tid->active = true; + + spin_unlock_bh(&ab->base_lock); + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, + paddr, tid, 1, ba_win_sz); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx reorder queue, tid :%d (%d)\n", + tid, ret); + ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid); + } + + return ret; + +err_mem_free: + kfree(vaddr); + + return ret; +} + +int ath11k_dp_rx_ampdu_start(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + int ret; + + ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, + params->tid, params->buf_size, + params->ssn); + if (ret) + ath11k_warn(ab, "failed to setup rx tid %d\n", ret); + + return ret; +} + +int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, + struct ieee80211_ampdu_params *params) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct ath11k_sta *arsta = (void *)params->sta->drv_priv; + int vdev_id = arsta->arvif->vdev_id; + dma_addr_t paddr; + bool active; + int ret; + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find(ab, vdev_id, params->sta->addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to stop rx aggregation\n"); + spin_unlock_bh(&ab->base_lock); + return -ENOENT; + } + + paddr = peer->rx_tid[params->tid].paddr; + active = peer->rx_tid[params->tid].active; + + if (!active) { + spin_unlock_bh(&ab->base_lock); + return 0; + } + + ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false); + spin_unlock_bh(&ab->base_lock); + if (ret) { + ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n", + params->tid, ret); + return ret; + } + + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, + params->sta->addr, paddr, + params->tid, 1, 1); + if (ret) + ath11k_warn(ab, "failed to send wmi to delete rx tid %d\n", + ret); + + return ret; +} + +static int ath11k_get_ppdu_user_index(struct htt_ppdu_stats *ppdu_stats, + u16 peer_id) +{ + int i; + + for (i = 0; i < HTT_PPDU_STATS_MAX_USERS - 1; i++) { + if (ppdu_stats->user_stats[i].is_valid_peer_id) { + if (peer_id == ppdu_stats->user_stats[i].peer_id) + return i; + } else { + return i; + } + } + + return -EINVAL; +} + +static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, const void *ptr, + void *data) +{ + struct htt_ppdu_stats_info *ppdu_info; + struct htt_ppdu_user_stats *user_stats; + int cur_user; + u16 peer_id; + + ppdu_info = (struct htt_ppdu_stats_info *)data; + + switch (tag) { + case HTT_PPDU_STATS_TAG_COMMON: + if (len < sizeof(struct htt_ppdu_stats_common)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + memcpy((void *)&ppdu_info->ppdu_stats.common, ptr, + sizeof(struct htt_ppdu_stats_common)); + break; + case HTT_PPDU_STATS_TAG_USR_RATE: + if (len < sizeof(struct htt_ppdu_stats_user_rate)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_user_rate *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->rate, ptr, + sizeof(struct htt_ppdu_stats_user_rate)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON: + if (len < sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = ((struct htt_ppdu_stats_usr_cmpltn_cmn *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->cmpltn_cmn, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_cmn)); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS: + if (len < + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)) { + ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", + len, tag); + return -EINVAL; + } + + peer_id = + ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, + peer_id); + if (cur_user < 0) + return -EINVAL; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->ack_ba, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); + user_stats->tlv_flags |= BIT(tag); + break; + } + return 0; +} + +int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const struct htt_tlv *tlv; + const void *begin = ptr; + u16 tlv_tag, tlv_len; + int ret = -EINVAL; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + tlv = (struct htt_tlv *)ptr; + tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header); + tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + ret = iter(ab, tlv_tag, tlv_len, ptr, data); + if (ret == -ENOMEM) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + return 0; +} + +static u32 ath11k_bw_to_mac80211_bwflags(u8 bw) +{ + u32 bwflags = 0; + + switch (bw) { + case ATH11K_BW_40: + bwflags = IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case ATH11K_BW_80: + bwflags = IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case ATH11K_BW_160: + bwflags = IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + } + + return bwflags; +} + +static void +ath11k_update_per_peer_tx_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats, u8 user) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + struct htt_ppdu_stats_user_rate *user_rate; + struct ieee80211_chanctx_conf *conf = NULL; + struct ath11k_per_peer_tx_stats *peer_stats = &ar->peer_tx_stats; + struct htt_ppdu_user_stats *usr_stats = &ppdu_stats->user_stats[user]; + struct htt_ppdu_stats_common *common = &ppdu_stats->common; + int ret; + u8 flags, mcs, nss, bw, sgi, rate_idx = 0; + u32 succ_bytes = 0; + u16 rate = 0, succ_pkts = 0; + u32 tx_duration = 0; + u8 tid = HTT_PPDU_STATS_NON_QOS_TID; + bool is_ampdu = false; + + if (!usr_stats) + return; + + if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) + return; + + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) + is_ampdu = + HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); + + if (usr_stats->tlv_flags & + BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_ACK_BA_STATUS)) { + succ_bytes = usr_stats->ack_ba.success_bytes; + succ_pkts = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_NUM_MSDU_M, + usr_stats->ack_ba.info); + tid = FIELD_GET(HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM, + usr_stats->ack_ba.info); + } + + if (common->fes_duration_us) + tx_duration = common->fes_duration_us; + + user_rate = &usr_stats->rate; + flags = HTT_USR_RATE_PREAMBLE(user_rate->rate_flags); + bw = HTT_USR_RATE_BW(user_rate->rate_flags) - 2; + nss = HTT_USR_RATE_NSS(user_rate->rate_flags) + 1; + mcs = HTT_USR_RATE_MCS(user_rate->rate_flags); + sgi = HTT_USR_RATE_GI(user_rate->rate_flags); + + /* Note: If host configured fixed rates and in some other special + * cases, the broadcast/management frames are sent in different rates. + * Firmware rate's control to be skipped for this? + */ + + if (flags == WMI_RATE_PREAMBLE_VHT && mcs > 9) { + ath11k_warn(ab, "Invalid VHT mcs %hhd peer stats", mcs); + return; + } + + if (flags == WMI_RATE_PREAMBLE_HT && (mcs > 7 || nss < 1)) { + ath11k_warn(ab, "Invalid HT mcs %hhd nss %hhd peer stats", + mcs, nss); + return; + } + + if (flags == WMI_RATE_PREAMBLE_CCK || flags == WMI_RATE_PREAMBLE_OFDM) { + ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs, + flags, + &rate_idx, + &rate); + if (ret < 0) + return; + } + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id); + + if (!peer || !peer->sta) { + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + return; + } + + sta = peer->sta; + arsta = (struct ath11k_sta *)sta->drv_priv; + + memset(&arsta->txrate, 0, sizeof(arsta->txrate)); + memset(&arsta->tx_info.status, 0, sizeof(arsta->tx_info.status)); + + switch (flags) { + case WMI_RATE_PREAMBLE_OFDM: + arsta->txrate.legacy = rate; + if (arsta->arvif && arsta->arvif->vif) + conf = rcu_dereference(arsta->arvif->vif->chanctx_conf); + if (conf && conf->def.chan->band == NL80211_BAND_5GHZ) + arsta->tx_info.status.rates[0].idx = rate_idx - 4; + break; + case WMI_RATE_PREAMBLE_CCK: + arsta->txrate.legacy = rate; + arsta->tx_info.status.rates[0].idx = rate_idx; + if (mcs > ATH11K_HW_RATE_CCK_LP_1M && + mcs <= ATH11K_HW_RATE_CCK_SP_2M) + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + break; + case WMI_RATE_PREAMBLE_HT: + arsta->txrate.mcs = mcs + 8 * (nss - 1); + arsta->tx_info.status.rates[0].idx = arsta->txrate.mcs; + arsta->txrate.flags = RATE_INFO_FLAGS_MCS; + arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_MCS; + if (sgi) { + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + } + break; + case WMI_RATE_PREAMBLE_VHT: + arsta->txrate.mcs = mcs; + ieee80211_rate_set_vht(&arsta->tx_info.status.rates[0], mcs, nss); + arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + arsta->tx_info.status.rates[0].flags |= IEEE80211_TX_RC_VHT_MCS; + if (sgi) { + arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + arsta->tx_info.status.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + } + break; + } + + arsta->txrate.nss = nss; + arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw); + arsta->tx_info.status.rates[0].flags |= ath11k_bw_to_mac80211_bwflags(bw); + arsta->tx_duration += tx_duration; + memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info)); + + if (succ_pkts) { + arsta->tx_info.flags = IEEE80211_TX_STAT_ACK; + arsta->tx_info.status.rates[0].count = 1; + ieee80211_tx_rate_update(ar->hw, sta, &arsta->tx_info); + } + + /* PPDU stats reported for mgmt packet doesn't have valid tx bytes. + * So skip peer stats update for mgmt packets. + */ + if (tid < HTT_PPDU_STATS_NON_QOS_TID) { + memset(peer_stats, 0, sizeof(*peer_stats)); + peer_stats->succ_pkts = succ_pkts; + peer_stats->succ_bytes = succ_bytes; + peer_stats->is_ampdu = is_ampdu; + peer_stats->duration = tx_duration; + peer_stats->ba_fails = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); + + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) + ath11k_accumulate_per_peer_tx_stats(arsta, + peer_stats, rate_idx); + } + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +} + +static void ath11k_htt_update_ppdu_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats) +{ + u8 user; + + for (user = 0; user < HTT_PPDU_STATS_MAX_USERS - 1; user++) + ath11k_update_per_peer_tx_stats(ar, ppdu_stats, user); +} + +static +struct htt_ppdu_stats_info *ath11k_dp_htt_get_ppdu_desc(struct ath11k *ar, + u32 ppdu_id) +{ + struct htt_ppdu_stats_info *ppdu_info; + + spin_lock_bh(&ar->data_lock); + if (!list_empty(&ar->ppdu_stats_info)) { + list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) { + if (ppdu_info->ppdu_id == ppdu_id) { + spin_unlock_bh(&ar->data_lock); + return ppdu_info; + } + } + + if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) { + ppdu_info = list_first_entry(&ar->ppdu_stats_info, + typeof(*ppdu_info), list); + list_del(&ppdu_info->list); + ar->ppdu_stat_list_depth--; + ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats); + kfree(ppdu_info); + } + } + spin_unlock_bh(&ar->data_lock); + + ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_KERNEL); + if (!ppdu_info) + return NULL; + + spin_lock_bh(&ar->data_lock); + list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info); + ar->ppdu_stat_list_depth++; + spin_unlock_bh(&ar->data_lock); + + return ppdu_info; +} + +static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_htt_ppdu_stats_msg *msg; + struct htt_ppdu_stats_info *ppdu_info; + struct ath11k *ar; + int ret; + u8 pdev_id; + u32 ppdu_id, len; + + msg = (struct ath11k_htt_ppdu_stats_msg *)skb->data; + len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, msg->info); + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info); + ppdu_id = msg->ppdu_id; + + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ret = -EINVAL; + goto exit; + } + + if (ath11k_debug_is_pktlog_lite_mode_enabled(ar)) + trace_ath11k_htt_ppdu_stats(ar, skb->data, len); + + ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id); + if (!ppdu_info) { + ret = -EINVAL; + goto exit; + } + + ppdu_info->ppdu_id = ppdu_id; + ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, + ath11k_htt_tlv_ppdu_stats_parse, + (void *)ppdu_info); + if (ret) { + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); + goto exit; + } + +exit: + rcu_read_unlock(); + + return ret; +} + +static void ath11k_htt_pktlog(struct ath11k_base *ab, struct sk_buff *skb) +{ + struct htt_pktlog_msg *data = (struct htt_pktlog_msg *)skb->data; + struct ath11k *ar; + u32 len; + u8 pdev_id; + + len = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PAYLOAD_SIZE, data->hdr); + if (len > ATH11K_HTT_PKTLOG_MAX_SIZE) { + ath11k_warn(ab, "htt pktlog buffer size %d, expected < %d\n", + len, + ATH11K_HTT_PKTLOG_MAX_SIZE); + return; + } + + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, data->hdr); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ath11k_warn(ab, "invalid pdev id %d on htt pktlog\n", pdev_id); + return; + } + + trace_ath11k_htt_pktlog(ar, data->payload, len); +} + +void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, + struct sk_buff *skb) +{ + struct ath11k_dp *dp = &ab->dp; + struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; + enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, *(u32 *)resp); + u16 peer_id; + u8 vdev_id; + u8 mac_addr[ETH_ALEN]; + u16 peer_mac_h16; + u16 ast_hash; + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type); + + switch (type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: + dp->htt_tgt_ver_major = FIELD_GET(HTT_T2H_VERSION_CONF_MAJOR, + resp->version_msg.version); + dp->htt_tgt_ver_minor = FIELD_GET(HTT_T2H_VERSION_CONF_MINOR, + resp->version_msg.version); + complete(&dp->htt_tgt_version_received); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); + ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, + resp->peer_map_ev.info2); + ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); + ath11k_peer_unmap_event(ab, peer_id); + break; + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath11k_htt_pull_ppdu_stats(ab, skb); + break; + case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: + ath11k_dbg_htt_ext_stats_handler(ab, skb); + break; + case HTT_T2H_MSG_TYPE_PKTLOG: + ath11k_htt_pktlog(ab, skb); + break; + default: + ath11k_warn(ab, "htt event %d not handled\n", type); + break; + } + + dev_kfree_skb_any(skb); +} + +static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar, + struct sk_buff_head *msdu_list, + struct sk_buff *first, struct sk_buff *last, + u8 l3pad_bytes, int msdu_len) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + int buf_first_hdr_len, buf_first_len; + struct hal_rx_desc *ldesc; + int space_extra; + int rem_len; + int buf_len; + + /* As the msdu is spread across multiple rx buffers, + * find the offset to the start of msdu for computing + * the length of the msdu in the first buffer. + */ + buf_first_hdr_len = HAL_RX_DESC_SIZE + l3pad_bytes; + buf_first_len = DP_RX_BUFFER_SIZE - buf_first_hdr_len; + + if (WARN_ON_ONCE(msdu_len <= buf_first_len)) { + skb_put(first, buf_first_hdr_len + msdu_len); + skb_pull(first, buf_first_hdr_len); + return 0; + } + + ldesc = (struct hal_rx_desc *)last->data; + rxcb->is_first_msdu = ath11k_dp_rx_h_msdu_end_first_msdu(ldesc); + rxcb->is_last_msdu = ath11k_dp_rx_h_msdu_end_last_msdu(ldesc); + + /* MSDU spans over multiple buffers because the length of the MSDU + * exceeds DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. So assume the data + * in the first buf is of length DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE. + */ + skb_put(first, DP_RX_BUFFER_SIZE); + skb_pull(first, buf_first_hdr_len); + + /* When an MSDU spread over multiple buffers attention, MSDU_END and + * MPDU_END tlvs are valid only in the last buffer. Copy those tlvs. + */ + ath11k_dp_rx_desc_end_tlv_copy(rxcb->rx_desc, ldesc); + + space_extra = msdu_len - (buf_first_len + skb_tailroom(first)); + if (space_extra > 0 && + (pskb_expand_head(first, 0, space_extra, GFP_ATOMIC) < 0)) { + /* Free up all buffers of the MSDU */ + while ((skb = __skb_dequeue(msdu_list)) != NULL) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) { + dev_kfree_skb_any(skb); + break; + } + dev_kfree_skb_any(skb); + } + return -ENOMEM; + } + + rem_len = msdu_len - buf_first_len; + while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { + rxcb = ATH11K_SKB_RXCB(skb); + if (rxcb->is_continuation) + buf_len = DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE; + else + buf_len = rem_len; + + if (buf_len > (DP_RX_BUFFER_SIZE - HAL_RX_DESC_SIZE)) { + WARN_ON_ONCE(1); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + skb_put(skb, buf_len + HAL_RX_DESC_SIZE); + skb_pull(skb, HAL_RX_DESC_SIZE); + skb_copy_from_linear_data(skb, skb_put(first, buf_len), + buf_len); + dev_kfree_skb_any(skb); + + rem_len -= buf_len; + if (!rxcb->is_continuation) + break; + } + + return 0; +} + +static struct sk_buff *ath11k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_list, + struct sk_buff *first) +{ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(first); + + if (!rxcb->is_continuation) + return first; + + skb_queue_walk(msdu_list, skb) { + rxcb = ATH11K_SKB_RXCB(skb); + if (!rxcb->is_continuation) + return skb; + } + + return NULL; +} + +static int ath11k_dp_rx_retrieve_amsdu(struct ath11k *ar, + struct sk_buff_head *msdu_list, + struct sk_buff_head *amsdu_list) +{ + struct sk_buff *msdu = skb_peek(msdu_list); + struct sk_buff *last_buf; + struct ath11k_skb_rxcb *rxcb; + struct ieee80211_hdr *hdr; + struct hal_rx_desc *rx_desc, *lrx_desc; + u16 msdu_len; + u8 l3_pad_bytes; + u8 *hdr_status; + int ret; + + if (!msdu) + return -ENOENT; + + rx_desc = (struct hal_rx_desc *)msdu->data; + hdr_status = ath11k_dp_rx_h_80211_hdr(rx_desc); + hdr = (struct ieee80211_hdr *)hdr_status; + /* Process only data frames */ + if (!ieee80211_is_data(hdr->frame_control)) { + __skb_unlink(msdu, msdu_list); + dev_kfree_skb_any(msdu); + return -EINVAL; + } + + do { + __skb_unlink(msdu, msdu_list); + last_buf = ath11k_dp_rx_get_msdu_last_buf(msdu_list, msdu); + if (!last_buf) { + ath11k_warn(ar->ab, + "No valid Rx buffer to access Atten/MSDU_END/MPDU_END tlvs\n"); + ret = -EIO; + goto free_out; + } + + rx_desc = (struct hal_rx_desc *)msdu->data; + lrx_desc = (struct hal_rx_desc *)last_buf->data; + + if (!ath11k_dp_rx_h_attn_msdu_done(lrx_desc)) { + ath11k_warn(ar->ab, "msdu_done bit in attention is not set\n"); + ret = -EIO; + goto free_out; + } + + rxcb = ATH11K_SKB_RXCB(msdu); + rxcb->rx_desc = rx_desc; + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(rx_desc); + l3_pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(lrx_desc); + + if (!rxcb->is_continuation) { + skb_put(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes + msdu_len); + skb_pull(msdu, HAL_RX_DESC_SIZE + l3_pad_bytes); + } else { + ret = ath11k_dp_rx_msdu_coalesce(ar, msdu_list, + msdu, last_buf, + l3_pad_bytes, msdu_len); + if (ret) { + ath11k_warn(ar->ab, + "failed to coalesce msdu rx buffer%d\n", ret); + goto free_out; + } + } + __skb_queue_tail(amsdu_list, msdu); + + /* Should we also consider msdu_cnt from mpdu_meta while + * preparing amsdu list? + */ + if (rxcb->is_last_msdu) + break; + } while ((msdu = skb_peek(msdu_list)) != NULL); + + return 0; + +free_out: + dev_kfree_skb_any(msdu); + __skb_queue_purge(amsdu_list); + + return ret; +} + +static void ath11k_dp_rx_h_csum_offload(struct sk_buff *msdu) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + bool ip_csum_fail, l4_csum_fail; + + ip_csum_fail = ath11k_dp_rx_h_attn_ip_cksum_fail(rxcb->rx_desc); + l4_csum_fail = ath11k_dp_rx_h_attn_l4_cksum_fail(rxcb->rx_desc); + + msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ? + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; +} + +static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return 0; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_MIC_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_MIC_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d for mic len\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_IV_LEN; + case HAL_ENCRYPT_TYPE_CCMP_128: + return IEEE80211_CCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_CCMP_256: + return IEEE80211_CCMP_256_HDR_LEN; + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return IEEE80211_GCMP_HDR_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, + enum hal_encrypt_type enctype) +{ + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + case HAL_ENCRYPT_TYPE_CCMP_128: + case HAL_ENCRYPT_TYPE_CCMP_256: + case HAL_ENCRYPT_TYPE_GCMP_128: + case HAL_ENCRYPT_TYPE_AES_GCMP_256: + return 0; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + return IEEE80211_TKIP_ICV_LEN; + case HAL_ENCRYPT_TYPE_WEP_40: + case HAL_ENCRYPT_TYPE_WEP_104: + case HAL_ENCRYPT_TYPE_WEP_128: + case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: + case HAL_ENCRYPT_TYPE_WAPI: + break; + } + + ath11k_warn(ar->ab, "unsupported encryption type %d\n", enctype); + return 0; +} + +static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + + /* pull decapped header and copy SA & DA */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(da, ieee80211_get_DA(hdr)); + ether_addr_copy(sa, ieee80211_get_SA(hdr)); + skb_pull(msdu, ieee80211_hdrlen(hdr->frame_control)); + + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap_raw(struct ath11k *ar, struct sk_buff *msdu, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len; + size_t crypto_len; + + if (!rxcb->is_first_msdu || + !(rxcb->is_first_msdu && rxcb->is_last_msdu)) { + WARN_ON_ONCE(1); + return; + } + + skb_trim(msdu, msdu->len - FCS_LEN); + + if (!decrypted) + return; + + hdr = (void *)msdu->data; + + /* Tail */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } else { + /* MIC */ + if (status->flag & RX_FLAG_MIC_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_mic_len(ar, enctype)); + + /* ICV */ + if (status->flag & RX_FLAG_ICV_STRIPPED) + skb_trim(msdu, msdu->len - + ath11k_dp_rx_crypto_icv_len(ar, enctype)); + } + + /* MMIC */ + if ((status->flag & RX_FLAG_MMIC_STRIPPED) && + !ieee80211_has_morefrags(hdr->frame_control) && + enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) + skb_trim(msdu, msdu->len - IEEE80211_CCMP_MIC_LEN); + + /* Head */ + if (status->flag & RX_FLAG_IV_STRIPPED) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + memmove((void *)msdu->data + crypto_len, + (void *)msdu->data, hdr_len); + skb_pull(msdu, crypto_len); + } +} + +static void *ath11k_dp_rx_h_find_rfc1042(struct ath11k *ar, + struct sk_buff *msdu, + enum hal_encrypt_type enctype) +{ + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ieee80211_hdr *hdr; + size_t hdr_len, crypto_len; + void *rfc1042; + bool is_amsdu; + + is_amsdu = !(rxcb->is_first_msdu && rxcb->is_last_msdu); + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rxcb->rx_desc); + rfc1042 = hdr; + + if (rxcb->is_first_msdu) { + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); + + rfc1042 += hdr_len + crypto_len; + } + + if (is_amsdu) + rfc1042 += sizeof(struct ath11k_dp_amsdu_subframe_hdr); + + return rfc1042; +} + +static void ath11k_dp_rx_h_undecap_eth(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status) +{ + struct ieee80211_hdr *hdr; + struct ethhdr *eth; + size_t hdr_len; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + void *rfc1042; + + rfc1042 = ath11k_dp_rx_h_find_rfc1042(ar, msdu, enctype); + if (WARN_ON_ONCE(!rfc1042)) + return; + + /* pull decapped header and copy SA & DA */ + eth = (struct ethhdr *)msdu->data; + ether_addr_copy(da, eth->h_dest); + ether_addr_copy(sa, eth->h_source); + skb_pull(msdu, sizeof(struct ethhdr)); + + /* push rfc1042/llc/snap */ + memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042, + sizeof(struct ath11k_dp_rfc1042_hdr)); + + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + memcpy(skb_push(msdu, + ath11k_dp_rx_crypto_param_len(ar, enctype)), + (void *)hdr + hdr_len, + ath11k_dp_rx_crypto_param_len(ar, enctype)); + } + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ + hdr = (struct ieee80211_hdr *)msdu->data; + ether_addr_copy(ieee80211_get_DA(hdr), da); + ether_addr_copy(ieee80211_get_SA(hdr), sa); +} + +static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + enum hal_encrypt_type enctype, + struct ieee80211_rx_status *status, + bool decrypted) +{ + u8 *first_hdr; + u8 decap; + + first_hdr = ath11k_dp_rx_h_80211_hdr(rx_desc); + decap = ath11k_dp_rx_h_mpdu_start_decap_type(rx_desc); + + switch (decap) { + case DP_RX_DECAP_TYPE_NATIVE_WIFI: + ath11k_dp_rx_h_undecap_nwifi(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_RAW: + ath11k_dp_rx_h_undecap_raw(ar, msdu, enctype, status, + decrypted); + break; + case DP_RX_DECAP_TYPE_ETHERNET2_DIX: + ath11k_dp_rx_h_undecap_eth(ar, msdu, first_hdr, + enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: + /* TODO: Handle undecap for these formats */ + break; + } +} + +static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_hdr *hdr; + enum hal_encrypt_type enctype; + struct sk_buff *last_msdu; + struct sk_buff *msdu; + struct ath11k_skb_rxcb *last_rxcb; + bool is_decrypted; + u32 err_bitmap; + u8 *qos; + + if (skb_queue_empty(amsdu_list)) + return; + + hdr = (struct ieee80211_hdr *)ath11k_dp_rx_h_80211_hdr(rx_desc); + + /* Each A-MSDU subframe will use the original header as the base and be + * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl. + */ + if (ieee80211_is_data_qos(hdr->frame_control)) { + qos = ieee80211_get_qos_ctl(hdr); + qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + + is_decrypted = ath11k_dp_rx_h_attn_is_decrypted(rx_desc); + enctype = ath11k_dp_rx_h_mpdu_start_enctype(rx_desc); + + /* Some attention flags are valid only in the last MSDU. */ + last_msdu = skb_peek_tail(amsdu_list); + last_rxcb = ATH11K_SKB_RXCB(last_msdu); + + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(last_rxcb->rx_desc); + + /* Clear per-MPDU flags while leaving per-PPDU flags intact. */ + rx_status->flag &= ~(RX_FLAG_FAILED_FCS_CRC | + RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); + + if (err_bitmap & DP_RX_MPDU_ERR_FCS) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (err_bitmap & DP_RX_MPDU_ERR_TKIP_MIC) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + if (is_decrypted) + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED | + RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED; + + skb_queue_walk(amsdu_list, msdu) { + ath11k_dp_rx_h_csum_offload(msdu); + ath11k_dp_rx_h_undecap(ar, msdu, rx_desc, + enctype, rx_status, is_decrypted); + } +} + +static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_supported_band *sband; + enum rx_msdu_start_pkt_type pkt_type; + u8 bw; + u8 rate_mcs, nss; + u8 sgi; + bool is_cck; + + pkt_type = ath11k_dp_rx_h_msdu_start_pkt_type(rx_desc); + bw = ath11k_dp_rx_h_msdu_start_rx_bw(rx_desc); + rate_mcs = ath11k_dp_rx_h_msdu_start_rate_mcs(rx_desc); + nss = ath11k_dp_rx_h_msdu_start_nss(rx_desc); + sgi = ath11k_dp_rx_h_msdu_start_sgi(rx_desc); + + switch (pkt_type) { + case RX_MSDU_START_PKT_TYPE_11A: + case RX_MSDU_START_PKT_TYPE_11B: + is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); + sband = &ar->mac.sbands[rx_status->band]; + rx_status->rate_idx = ath11k_mac_hw_rate_to_idx(sband, rate_mcs, + is_cck); + break; + case RX_MSDU_START_PKT_TYPE_11N: + rx_status->encoding = RX_ENC_HT; + if (rate_mcs > ATH11K_HT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HT mode %d\n", + rate_mcs); + break; + } + rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AC: + rx_status->encoding = RX_ENC_VHT; + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_VHT_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in VHT mode %d\n", + rate_mcs); + break; + } + rx_status->nss = nss; + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH11K_HE_MCS_MAX) { + ath11k_warn(ar->ab, + "Received with invalid mcs in HE mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_HE; + rx_status->nss = nss; + rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw); + break; + } +} + +static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, + struct ieee80211_rx_status *rx_status) +{ + u8 channel_num; + + rx_status->freq = 0; + rx_status->rate_idx = 0; + rx_status->nss = 0; + rx_status->encoding = RX_ENC_LEGACY; + rx_status->bw = RATE_INFO_BW_20; + + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc); + + if (channel_num >= 1 && channel_num <= 14) { + rx_status->band = NL80211_BAND_2GHZ; + } else if (channel_num >= 36 && channel_num <= 173) { + rx_status->band = NL80211_BAND_5GHZ; + } else { + ath11k_warn(ar->ab, "Unsupported Channel info received %d\n", + channel_num); + return; + } + + rx_status->freq = ieee80211_channel_to_frequency(channel_num, + rx_status->band); + + ath11k_dp_rx_h_rate(ar, rx_desc, rx_status); +} + +static void ath11k_dp_rx_process_amsdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct ieee80211_rx_status *rx_status) +{ + struct sk_buff *first; + struct ath11k_skb_rxcb *rxcb; + struct hal_rx_desc *rx_desc; + bool first_mpdu; + + if (skb_queue_empty(amsdu_list)) + return; + + first = skb_peek(amsdu_list); + rxcb = ATH11K_SKB_RXCB(first); + rx_desc = rxcb->rx_desc; + + first_mpdu = ath11k_dp_rx_h_attn_first_mpdu(rx_desc); + if (first_mpdu) + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); + + ath11k_dp_rx_h_mpdu(ar, amsdu_list, rx_desc, rx_status); +} + +static char *ath11k_print_get_tid(struct ieee80211_hdr *hdr, char *out, + size_t size) +{ + u8 *qc; + int tid; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return ""; + + qc = ieee80211_get_qos_ctl(hdr); + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + snprintf(out, size, "tid %d", tid); + + return out; +} + +static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *napi, + struct sk_buff *msdu) +{ + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN), + }; + struct ieee80211_rx_status *status; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_radiotap_he *he = NULL; + char tid[32]; + + status = IEEE80211_SKB_RXCB(msdu); + if (status->encoding == RX_ENC_HE) { + he = skb_push(msdu, sizeof(known)); + memcpy(he, &known, sizeof(known)); + status->flag |= RX_FLAG_RADIOTAP_HE; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + msdu, + msdu->len, + ieee80211_get_SA(hdr), + ath11k_print_get_tid(hdr, tid, sizeof(tid)), + is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? + "mcast" : "ucast", + (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, + (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", + (status->encoding == RX_ENC_HT) ? "ht" : "", + (status->encoding == RX_ENC_VHT) ? "vht" : "", + (status->encoding == RX_ENC_HE) ? "he" : "", + (status->bw == RATE_INFO_BW_40) ? "40" : "", + (status->bw == RATE_INFO_BW_80) ? "80" : "", + (status->bw == RATE_INFO_BW_160) ? "160" : "", + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->nss, + status->freq, + status->band, status->flag, + !!(status->flag & RX_FLAG_FAILED_FCS_CRC), + !!(status->flag & RX_FLAG_MMIC_ERROR), + !!(status->flag & RX_FLAG_AMSDU_MORE)); + + /* TODO: trace rx packet */ + + ieee80211_rx_napi(ar->hw, NULL, msdu, napi); +} + +static void ath11k_dp_rx_pre_deliver_amsdu(struct ath11k *ar, + struct sk_buff_head *amsdu_list, + struct ieee80211_rx_status *rxs) +{ + struct sk_buff *msdu; + struct sk_buff *first_subframe; + struct ieee80211_rx_status *status; + + first_subframe = skb_peek(amsdu_list); + + skb_queue_walk(amsdu_list, msdu) { + /* Setup per-MSDU flags */ + if (skb_queue_empty(amsdu_list)) + rxs->flag &= ~RX_FLAG_AMSDU_MORE; + else + rxs->flag |= RX_FLAG_AMSDU_MORE; + + if (msdu == first_subframe) { + first_subframe = NULL; + rxs->flag &= ~RX_FLAG_ALLOW_SAME_PN; + } else { + rxs->flag |= RX_FLAG_ALLOW_SAME_PN; + } + rxs->flag |= RX_FLAG_SKIP_MONITOR; + + status = IEEE80211_SKB_RXCB(msdu); + *status = *rxs; + } +} + +static void ath11k_dp_rx_process_pending_packets(struct ath11k_base *ab, + struct napi_struct *napi, + struct sk_buff_head *pending_q, + int *quota, u8 mac_id) +{ + struct ath11k *ar; + struct sk_buff *msdu; + struct ath11k_pdev *pdev; + + if (skb_queue_empty(pending_q)) + return; + + ar = ab->pdevs[mac_id].ar; + + rcu_read_lock(); + pdev = rcu_dereference(ab->pdevs_active[mac_id]); + + while (*quota && (msdu = __skb_dequeue(pending_q))) { + if (!pdev) { + dev_kfree_skb_any(msdu); + continue; + } + + ath11k_dp_rx_deliver_msdu(ar, napi, msdu); + (*quota)--; + } + rcu_read_unlock(); +} + +int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, struct sk_buff_head *pending_q, + int budget) +{ + struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k_pdev_dp *dp = &ar->dp; + struct ieee80211_rx_status *rx_status = &dp->rx_status; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + struct hal_srng *srng; + struct sk_buff *msdu; + struct sk_buff_head msdu_list; + struct sk_buff_head amsdu_list; + struct ath11k_skb_rxcb *rxcb; + u32 *rx_desc; |