From 74e7ef739f0613a28f6f77ed26739fdf232d0222 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Fri, 14 Nov 2025 12:10:22 +0800 Subject: [PATCH 01/20] FROMLIST: wifi: ath11k: add usecase firmware handling based on device compatible For M.2 WLAN chips, there is no suitable DTS node to specify the firmware-name property. In addition, assigning firmware for the M.2 PCIe interface causes chips that do not use usecase specific firmware to fail. Therefore, abandoning the approach of specifying firmware in DTS. As an alternative, propose a static lookup table mapping device compatible to firmware names. Currently, only WCN6855 HW2.1 requires this. For details on usecase specific firmware, see: https://lore.kernel.org/all/20250522013444.1301330-3-miaoqing.pan@oss.qualcomm.com/. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-04685-QCAHSPSWPL_V1_V2_SILICONZ_IOE-1 Fixes: edbbc647c4f3 ("wifi: ath11k: support usercase-specific firmware overrides") Signed-off-by: Miaoqing Pan Link: https://lore.kernel.org/linux-wireless/20251204071100.970518-2-miaoqing.pan@oss.qualcomm.com/ --- drivers/net/wireless/ath/ath11k/core.c | 37 +++++++++++++++++++++++++- drivers/net/wireless/ath/ath11k/core.h | 7 +++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 812686173ac8a..a4a3a65c77526 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -997,6 +996,42 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = { {} }; +static const struct __ath11k_core_usecase_firmware_table { + u32 hw_rev; + const char *compatible; + const char *firmware_name; +} ath11k_core_usecase_firmware_table[] = { + { ATH11K_HW_WCN6855_HW21, "qcom,lemans-evk", "nfa765"}, + { ATH11K_HW_WCN6855_HW21, "qcom,monaco-evk", "nfa765"}, + { ATH11K_HW_WCN6855_HW21, "qcom,hamoa-iot-evk", "nfa765"}, + { /* Sentinel */ } +}; + +const char *ath11k_core_get_usecase_firmware(struct ath11k_base *ab) +{ + struct device_node *root __free(device_node) = of_find_node_by_path("/"); + const struct __ath11k_core_usecase_firmware_table *entry = NULL; + int i, count = of_property_count_strings(root, "compatible"); + const char *compatible = NULL; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(root, "compatible", i, + &compatible) < 0) + continue; + + entry = ath11k_core_usecase_firmware_table; + while (entry->compatible) { + if (ab->hw_rev == entry->hw_rev && + !strcmp(entry->compatible, compatible)) + return entry->firmware_name; + entry++; + } + } + + return NULL; +} +EXPORT_SYMBOL(ath11k_core_get_usecase_firmware); + void ath11k_fw_stats_pdevs_free(struct list_head *head) { struct ath11k_fw_stats_pdev *i, *tmp; diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index e8780b05ce11e..f8fcd897ebd26 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #ifndef ATH11K_CORE_H @@ -1275,6 +1275,7 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab); const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, const char *filename); +const char *ath11k_core_get_usecase_firmware(struct ath11k_base *ab); static inline const char *ath11k_scan_state_str(enum ath11k_scan_state state) { @@ -1325,9 +1326,7 @@ static inline void ath11k_core_create_firmware_path(struct ath11k_base *ab, const char *filename, void *buf, size_t buf_len) { - const char *fw_name = NULL; - - of_property_read_string(ab->dev->of_node, "firmware-name", &fw_name); + const char *fw_name = ath11k_core_get_usecase_firmware(ab); if (fw_name && strncmp(filename, "board", 5)) snprintf(buf, buf_len, "%s/%s/%s/%s", ATH11K_FW_DIR, From 5de2966b51adf9ce26615f3bfc5fe3ea220ce876 Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 1 Dec 2025 18:33:15 -0800 Subject: [PATCH 02/20] FROMLIST: mhi: host: Add standard elf image download functionality Currently, the FBC image is a non-standard ELF file that contains a single ELF header, followed by segments for SBL, and WLAN FW. However, TME-L (Trust Management Engine Lite) supported devices (eg. QCC2072) requires separate ELF headers for SBL and WLAN FW segments due to TME-L image authentication requirement. Current image format contains two sections in a single binary: - First 512KB: ELF header + SBL segments - Remaining: WLAN FW segments The TME-L supported image format contains two sections with two elf headers in a single binary: - First 512KB: First ELF header + SBL segments - Remaining: Second ELF header + WLAN FW segments Download behavior: - Legacy: 1. First 512KB via BHI (ELF header + SBL) 2. Full image via BHIe - TME-L: 1. First 512KB via BHI (First ELF header + SBL) 2. Remaining via BHIe (Second ELF header + WLAN FW segments) Add standard_elf_image flag to mhi_controller_config to indicate TME-L supported image format. When set, MHI skips the first 512KB during WLAN FW download over BHIe as it is loaded in BHI phase. Reviewed-by: Baochen Qiang Signed-off-by: Mayank Rana Co-developed-by: Qiang Yu Signed-off-by: Qiang Yu Link: https://lore.kernel.org/mhi/20251201-wlan_image_load_skip_512k-v3-1-bedb5498a58a@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/bus/mhi/host/boot.c | 7 +++++++ include/linux/mhi.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index 205d83ac069f1..64fb7a257d352 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -584,6 +584,13 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) * device transitioning into MHI READY state */ if (fw_load_type == MHI_FW_LOAD_FBC) { + dev_dbg(dev, "standard_elf_image:%s\n", + (mhi_cntrl->standard_elf_image ? "True" : "False")); + if (mhi_cntrl->standard_elf_image) { + fw_data += mhi_cntrl->sbl_size; + fw_sz -= mhi_cntrl->sbl_size; + } + ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz); if (ret) { release_firmware(firmware); diff --git a/include/linux/mhi.h b/include/linux/mhi.h index dd372b0123a6d..a13106bb234d2 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -360,6 +360,9 @@ struct mhi_controller_config { * @bounce_buf: Use of bounce buffer * @fbc_download: MHI host needs to do complete image transfer (optional) * @wake_set: Device wakeup set flag + * @standard_elf_image: Flag to determine whether the first 512 KB of the FBC + * image need to be skipped when loading WLAN FW over + * BHIe interface (optional) * @irq_flags: irq flags passed to request_irq (optional) * @mru: the default MRU for the MHI device * @@ -445,6 +448,7 @@ struct mhi_controller { bool bounce_buf; bool fbc_download; bool wake_set; + bool standard_elf_image; unsigned long irq_flags; u32 mru; }; From 1ffaf5837ced9c794e1100380e2553db7a521c9c Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 10:58:09 +0800 Subject: [PATCH 03/20] BACKPORT: FROMLIST: wifi: ath12k: refactor PCI window register access Currently offset of PCI window register address is defined as 0x310c which is same across existing chips. However QCC2072 has a different offset 0x3278. In order to make the window selection logic work for QCC2072 as well, change to initialize this parameter per device at the probe time. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-1-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/pci.c | 16 +++++++++++++--- drivers/net/wireless/ath/ath12k/pci.h | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index a12c8379cb466..f401d0d890f72 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -175,8 +175,8 @@ static void ath12k_pci_select_window(struct ath12k_pci *ab_pci, u32 offset) if (window != ab_pci->register_window) { iowrite32(WINDOW_ENABLE_BIT | window, - ab->mem + WINDOW_REG_ADDRESS); - ioread32(ab->mem + WINDOW_REG_ADDRESS); + ab->mem + ab_pci->window_reg_addr); + ioread32(ab->mem + ab_pci->window_reg_addr); ab_pci->register_window = window; } } @@ -193,7 +193,7 @@ static void ath12k_pci_select_static_window(struct ath12k_pci *ab_pci) ab_pci->register_window = window; spin_unlock_bh(&ab_pci->window_lock); - iowrite32(WINDOW_ENABLE_BIT | window, ab_pci->ab->mem + WINDOW_REG_ADDRESS); + iowrite32(WINDOW_ENABLE_BIT | window, ab_pci->ab->mem + ab_pci->window_reg_addr); } static u32 ath12k_pci_get_window_start(struct ath12k_base *ab, @@ -1611,6 +1611,11 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab->static_window_map = true; ab_pci->pci_ops = &ath12k_pci_ops_qcn9274; ab->hal_rx_ops = &hal_rx_qcn9274_ops; + /* + * init window reg addr before reading hardware version + * as it will be used there + */ + ab_pci->window_reg_addr = WINDOW_REG_ADDRESS; ath12k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); ab->target_mem_mode = ath12k_core_get_memory_mode(ab); @@ -1635,6 +1640,11 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab->static_window_map = false; ab_pci->pci_ops = &ath12k_pci_ops_wcn7850; ab->hal_rx_ops = &hal_rx_wcn7850_ops; + /* + * init window reg addr before reading hardware version + * as it will be used there + */ + ab_pci->window_reg_addr = WINDOW_REG_ADDRESS; ath12k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); ab->target_mem_mode = ATH12K_QMI_MEMORY_MODE_DEFAULT; diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h index d1ec8aad7f6c3..541fba4373348 100644 --- a/drivers/net/wireless/ath/ath12k/pci.h +++ b/drivers/net/wireless/ath/ath12k/pci.h @@ -119,6 +119,8 @@ struct ath12k_pci { const struct ath12k_pci_ops *pci_ops; u32 qmi_instance; u64 dma_mask; + + u32 window_reg_addr; }; static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab) From 1913bd9acd84f411b53556a29f387baec2557517 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 11:04:30 +0800 Subject: [PATCH 04/20] BACKPORT: FROMLIST: wifi: ath12k: refactor REO CMD ring handling The entry of REO CMD ring of existing chips has a 64 bit TLV header, hence below functions take a 64 bit TLV assumption by default reo_init_cmd_ring reo_cmd_encode_hdr However this is not the case for QCC2072 of which the TLV is 32 bit, meaning above functions don't work for it. Rename/refactor above functions to prepare for QCC2072 support: Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-2-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/hal.c | 44 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/hal.h | 3 ++ drivers/net/wireless/ath/ath12k/hal_rx.c | 44 +++++++++++------------- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 6406fcf5d69fd..9a76e7d1ade50 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -1030,6 +1030,26 @@ static u32 ath12k_hw_qcn9274_compact_dp_rx_h_mpdu_err(struct hal_rx_desc *desc) return errmap; } +static void ath12k_hal_qcn9274_reo_init_cmd_ring(void *entry, int cmd_num) +{ + struct hal_reo_get_queue_stats *desc; + struct hal_tlv_64_hdr *tlv = entry; + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = le32_encode_bits(cmd_num, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); +} + +static void *ath12k_hal_qcn9274_reo_cmd_encode_hdr(void *reo_desc, + u64 tag, u64 len) +{ + struct hal_tlv_64_hdr *tlv = reo_desc; + + tlv->tl = le64_encode_bits(tag, HAL_TLV_HDR_TAG) | + le64_encode_bits(len, HAL_TLV_HDR_LEN); + + return tlv->value; +} + static u32 ath12k_hw_qcn9274_compact_get_rx_desc_size(void) { return sizeof(struct hal_rx_desc_qcn9274_compact); @@ -1089,6 +1109,8 @@ const struct hal_rx_ops hal_rx_qcn9274_compact_ops = { const struct hal_ops hal_qcn9274_ops = { .create_srng_config = ath12k_hal_srng_create_config_qcn9274, .tcl_to_wbm_rbm_map = ath12k_hal_qcn9274_tcl_to_wbm_rbm_map, + .reo_init_cmd_ring = ath12k_hal_qcn9274_reo_init_cmd_ring, + .reo_cmd_encode_hdr = ath12k_hal_qcn9274_reo_cmd_encode_hdr, .rxdma_ring_wmask_rx_mpdu_start = ath12k_hal_qcn9274_rx_mpdu_start_wmask_get, .rxdma_ring_wmask_rx_msdu_end = ath12k_hal_qcn9274_rx_msdu_end_wmask_get, .get_hal_rx_compact_ops = ath12k_hal_qcn9274_get_hal_rx_compact_ops, @@ -1512,6 +1534,26 @@ static u8 ath12k_hw_wcn7850_rx_desc_get_msdu_src_link(struct hal_rx_desc *desc) return 0; } +static void ath12k_hal_wcn7850_reo_init_cmd_ring(void *entry, int cmd_num) +{ + struct hal_reo_get_queue_stats *desc; + struct hal_tlv_64_hdr *tlv = entry; + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = le32_encode_bits(cmd_num, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); +} + +static void *ath12k_hal_wcn7850_reo_cmd_encode_hdr(void *reo_desc, + u64 tag, u64 len) +{ + struct hal_tlv_64_hdr *tlv = reo_desc; + + tlv->tl = le64_encode_bits(tag, HAL_TLV_HDR_TAG) | + le64_encode_bits(len, HAL_TLV_HDR_LEN); + + return tlv->value; +} + const struct hal_rx_ops hal_rx_wcn7850_ops = { .rx_desc_get_first_msdu = ath12k_hw_wcn7850_rx_desc_get_first_msdu, .rx_desc_get_last_msdu = ath12k_hw_wcn7850_rx_desc_get_last_msdu, @@ -1556,6 +1598,8 @@ const struct hal_rx_ops hal_rx_wcn7850_ops = { const struct hal_ops hal_wcn7850_ops = { .create_srng_config = ath12k_hal_srng_create_config_wcn7850, .tcl_to_wbm_rbm_map = ath12k_hal_wcn7850_tcl_to_wbm_rbm_map, + .reo_init_cmd_ring = ath12k_hal_wcn7850_reo_init_cmd_ring, + .reo_cmd_encode_hdr = ath12k_hal_wcn7850_reo_cmd_encode_hdr, .rxdma_ring_wmask_rx_mpdu_start = NULL, .rxdma_ring_wmask_rx_msdu_end = NULL, .get_hal_rx_compact_ops = NULL, diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index efe00e1679986..de423004a9a5a 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1106,6 +1106,9 @@ struct hal_ops { u16 (*rxdma_ring_wmask_rx_mpdu_start)(void); u32 (*rxdma_ring_wmask_rx_msdu_end)(void); const struct hal_rx_ops *(*get_hal_rx_compact_ops)(void); + void (*reo_init_cmd_ring)(void *entry, int cmd_num); + void *(*reo_cmd_encode_hdr)(void *reo_desc, + u64 tag, u64 len); const struct ath12k_hal_tcl_to_wbm_rbm_map *tcl_to_wbm_rbm_map; }; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c index c4443ca05cd65..7fc00c81576cf 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/hal_rx.c @@ -21,15 +21,15 @@ static void ath12k_hal_reo_set_desc_hdr(struct hal_desc_header *hdr, hdr->info0 |= le32_encode_bits(magic, HAL_DESC_HDR_INFO0_DBG_RESERVED); } -static int ath12k_hal_reo_cmd_queue_stats(struct hal_tlv_64_hdr *tlv, +static int ath12k_hal_reo_cmd_queue_stats(struct ath12k_base *ab, + struct hal_tlv_64_hdr *tlv, struct ath12k_hal_reo_cmd *cmd) { struct hal_reo_get_queue_stats *desc; - tlv->tl = le64_encode_bits(HAL_REO_GET_QUEUE_STATS, HAL_TLV_HDR_TAG) | - le64_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); - - desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc = ab->hw_params->hal_ops->reo_cmd_encode_hdr(tlv, + HAL_REO_GET_QUEUE_STATS, + sizeof(*desc)); memset_startat(desc, 0, queue_addr_lo); desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED); @@ -45,10 +45,11 @@ static int ath12k_hal_reo_cmd_queue_stats(struct hal_tlv_64_hdr *tlv, return le32_get_bits(desc->cmd.info0, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); } -static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_hal *hal, +static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv, struct ath12k_hal_reo_cmd *cmd) { + struct ath12k_hal *hal = &ab->hal; struct hal_reo_flush_cache *desc; u8 avail_slot = ffz(hal->avail_blk_resource); @@ -59,10 +60,9 @@ static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_hal *hal, hal->current_blk_index = avail_slot; } - tlv->tl = le64_encode_bits(HAL_REO_FLUSH_CACHE, HAL_TLV_HDR_TAG) | - le64_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); - - desc = (struct hal_reo_flush_cache *)tlv->value; + desc = ab->hw_params->hal_ops->reo_cmd_encode_hdr(tlv, + HAL_REO_FLUSH_CACHE, + sizeof(*desc)); memset_startat(desc, 0, cache_addr_lo); desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED); @@ -95,15 +95,15 @@ static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_hal *hal, return le32_get_bits(desc->cmd.info0, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); } -static int ath12k_hal_reo_cmd_update_rx_queue(struct hal_tlv_64_hdr *tlv, +static int ath12k_hal_reo_cmd_update_rx_queue(struct ath12k_base *ab, + struct hal_tlv_64_hdr *tlv, struct ath12k_hal_reo_cmd *cmd) { struct hal_reo_update_rx_queue *desc; - tlv->tl = le64_encode_bits(HAL_REO_UPDATE_RX_REO_QUEUE, HAL_TLV_HDR_TAG) | - le64_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN); - - desc = (struct hal_reo_update_rx_queue *)tlv->value; + desc = ab->hw_params->hal_ops->reo_cmd_encode_hdr(tlv, + HAL_REO_UPDATE_RX_REO_QUEUE, + sizeof(*desc)); memset_startat(desc, 0, queue_addr_lo); desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED); @@ -238,13 +238,13 @@ int ath12k_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng, switch (type) { case HAL_REO_CMD_GET_QUEUE_STATS: - ret = ath12k_hal_reo_cmd_queue_stats(reo_desc, cmd); + ret = ath12k_hal_reo_cmd_queue_stats(ab, reo_desc, cmd); break; case HAL_REO_CMD_FLUSH_CACHE: - ret = ath12k_hal_reo_cmd_flush_cache(&ab->hal, reo_desc, cmd); + ret = ath12k_hal_reo_cmd_flush_cache(ab, reo_desc, cmd); break; case HAL_REO_CMD_UPDATE_RX_QUEUE: - ret = ath12k_hal_reo_cmd_update_rx_queue(reo_desc, cmd); + ret = ath12k_hal_reo_cmd_update_rx_queue(ab, reo_desc, cmd); break; case HAL_REO_CMD_FLUSH_QUEUE: case HAL_REO_CMD_UNBLOCK_CACHE: @@ -872,8 +872,6 @@ void ath12k_hal_reo_init_cmd_ring(struct ath12k_base *ab, struct hal_srng *srng) { struct hal_srng_params params; - struct hal_tlv_64_hdr *tlv; - struct hal_reo_get_queue_stats *desc; int i, cmd_num = 1; int entry_size; u8 *entry; @@ -885,11 +883,9 @@ void ath12k_hal_reo_init_cmd_ring(struct ath12k_base *ab, entry = (u8 *)params.ring_base_vaddr; for (i = 0; i < params.num_entries; i++) { - tlv = (struct hal_tlv_64_hdr *)entry; - desc = (struct hal_reo_get_queue_stats *)tlv->value; - desc->cmd.info0 = le32_encode_bits(cmd_num++, - HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); + ab->hw_params->hal_ops->reo_init_cmd_ring(entry, cmd_num); entry += entry_size; + cmd_num++; } } From 9394b7a6d51fd56a98e1d6e33b9611402d6f75d1 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Fri, 5 Dec 2025 14:07:03 +0800 Subject: [PATCH 05/20] BACKPORT: FROMLIST: wifi: ath12k: refactor REO status ring handling The entry of REO status ring of existing chips has a 64 bit TLV header, however this is not the case for QCC2072 of which the TLV is 32 bit. Refactor above functions to prepare for QCC2072 support, this is done by removing TLV length assumption and offloading TLV decoding work to a newly added callback _reo_status_dec_tlv_hdr. This way each chip can register its own handler hence can do the work accordingly. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-3-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 ++- drivers/net/wireless/ath/ath12k/hal.c | 24 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/hal.h | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index d28d8ffec0f83..0ae4b7c36716a 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4421,6 +4421,7 @@ void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab) bool found = false; u16 tag; struct hal_reo_status reo_status; + void *desc; srng = &ab->hal.srng_list[dp->reo_status_ring.ring_id]; @@ -4431,7 +4432,7 @@ void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab) ath12k_hal_srng_access_begin(ab, srng); while ((hdr = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { - tag = le64_get_bits(hdr->tl, HAL_SRNG_TLV_HDR_TAG); + tag = ab->hw_params->hal_ops->reo_status_decode_hdr(hdr, &desc); switch (tag) { case HAL_REO_GET_QUEUE_STATS_STATUS: diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 9a76e7d1ade50..b3f446b570c4f 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -1050,6 +1050,17 @@ static void *ath12k_hal_qcn9274_reo_cmd_encode_hdr(void *reo_desc, return tlv->value; } +static u16 ath12k_hal_qcn9274_reo_status_decode_hdr(void *reo_desc, void **desc) +{ + struct hal_tlv_64_hdr *tlv = reo_desc; + u16 tag; + + tag = le64_get_bits(tlv->tl, HAL_SRNG_TLV_HDR_TAG); + *desc = tlv->value; + + return tag; +} + static u32 ath12k_hw_qcn9274_compact_get_rx_desc_size(void) { return sizeof(struct hal_rx_desc_qcn9274_compact); @@ -1111,6 +1122,7 @@ const struct hal_ops hal_qcn9274_ops = { .tcl_to_wbm_rbm_map = ath12k_hal_qcn9274_tcl_to_wbm_rbm_map, .reo_init_cmd_ring = ath12k_hal_qcn9274_reo_init_cmd_ring, .reo_cmd_encode_hdr = ath12k_hal_qcn9274_reo_cmd_encode_hdr, + .reo_status_decode_hdr = ath12k_hal_qcn9274_reo_status_decode_hdr, .rxdma_ring_wmask_rx_mpdu_start = ath12k_hal_qcn9274_rx_mpdu_start_wmask_get, .rxdma_ring_wmask_rx_msdu_end = ath12k_hal_qcn9274_rx_msdu_end_wmask_get, .get_hal_rx_compact_ops = ath12k_hal_qcn9274_get_hal_rx_compact_ops, @@ -1554,6 +1566,17 @@ static void *ath12k_hal_wcn7850_reo_cmd_encode_hdr(void *reo_desc, return tlv->value; } +static u16 ath12k_hal_wcn7850_reo_status_decode_hdr(void *reo_desc, void **desc) +{ + struct hal_tlv_64_hdr *tlv = reo_desc; + u16 tag; + + tag = le64_get_bits(tlv->tl, HAL_SRNG_TLV_HDR_TAG); + *desc = tlv->value; + + return tag; +} + const struct hal_rx_ops hal_rx_wcn7850_ops = { .rx_desc_get_first_msdu = ath12k_hw_wcn7850_rx_desc_get_first_msdu, .rx_desc_get_last_msdu = ath12k_hw_wcn7850_rx_desc_get_last_msdu, @@ -1600,6 +1623,7 @@ const struct hal_ops hal_wcn7850_ops = { .tcl_to_wbm_rbm_map = ath12k_hal_wcn7850_tcl_to_wbm_rbm_map, .reo_init_cmd_ring = ath12k_hal_wcn7850_reo_init_cmd_ring, .reo_cmd_encode_hdr = ath12k_hal_wcn7850_reo_cmd_encode_hdr, + .reo_status_decode_hdr = ath12k_hal_wcn7850_reo_status_decode_hdr, .rxdma_ring_wmask_rx_mpdu_start = NULL, .rxdma_ring_wmask_rx_msdu_end = NULL, .get_hal_rx_compact_ops = NULL, diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index de423004a9a5a..d84f71280d0ed 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1109,6 +1109,7 @@ struct hal_ops { void (*reo_init_cmd_ring)(void *entry, int cmd_num); void *(*reo_cmd_encode_hdr)(void *reo_desc, u64 tag, u64 len); + u16 (*reo_status_decode_hdr)(void *reo_desc, void **desc); const struct ath12k_hal_tcl_to_wbm_rbm_map *tcl_to_wbm_rbm_map; }; From 8e827ea47971da6baf2097e4f7de8405b9b9be0d Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 12 Nov 2025 09:28:21 +0800 Subject: [PATCH 06/20] BACKPORT: FROMLIST: wifi: ath12k: fix preferred hardware mode calculation For single pdev device like WCN7850/QCC2072, preferred_hw_mode is initialized to WMI_HOST_HW_MODE_SINGLE. Later when firmware sends supported modes to host, each mode is compared with the initial one and if the priority of the new mode is higher, update the parameter and store mode capability. For WCN7850, this does not result in issue, as one of the supported mode indeed has a higher priority. However the only available mode of QCC2072 at this stage is WMI_HOST_HW_MODE_SINGLE, which fails the comparison, hence mode capability is not stored. Subsequently driver initialization fails. Fix it by accepting a mode with the same priority. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-4-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index be8b2943094f8..8f3f79c19b5e2 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -4545,7 +4545,7 @@ static int ath12k_wmi_hw_mode_caps(struct ath12k_base *soc, pref = soc->wmi_ab.preferred_hw_mode; - if (ath12k_hw_mode_pri_map[mode] < ath12k_hw_mode_pri_map[pref]) { + if (ath12k_hw_mode_pri_map[mode] <= ath12k_hw_mode_pri_map[pref]) { svc_rdy_ext->pref_hw_mode_caps = *hw_mode_caps; soc->wmi_ab.preferred_hw_mode = mode; } From 40924a83b605549e79f92729f35cca2353209e9f Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 12 Nov 2025 09:41:22 +0800 Subject: [PATCH 07/20] BACKPORT: FROMLIST: wifi: ath12k: refactor 320 MHz bandwidth support parsing For single pdev device, 320 MHz bandwidth support is reported only in capability of WMI_HOST_HW_MODE_SINGLE mode, hence commit d4e244c85e45 ("wifi: ath12k: enable 320 MHz bandwidth for 6 GHz band in EHT PHY capability for WCN7850") relaxed the condition check in ath12k_wmi_tlv_mac_phy_caps_ext() to allow SINGLE mode getting parsed in ath12k_wmi_tlv_mac_phy_caps_ext_parse(). Since SINGLE mode is not assumed to be preferred, the function returns unconditionally after parsing 320 MHz support. This works for WCN7850 because it prefers another mode indeed, while it breaks QCC2072 since it prefers SINGLE mode. Due to the unconditional return, the subsequent EHT parsing is skipped. Consequently EHT related features are disabled. Refactor it by moving 320 MHz parsing to ath12k_wmi_tlv_mac_phy_caps_ext(), before the mode checking. This makes the code more straightforward, and work for both WCN7850 and QCC2072. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-5-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/wmi.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 8f3f79c19b5e2..1451f1cba573f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -5014,19 +5014,10 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, const struct ath12k_wmi_caps_ext_params *caps, struct ath12k_pdev *pdev) { - struct ath12k_band_cap *cap_band; - u32 bands, support_320mhz; + u32 bands; int i; if (ab->hw_params->single_pdev_only) { - if (caps->hw_mode_id == WMI_HOST_HW_MODE_SINGLE) { - support_320mhz = le32_to_cpu(caps->eht_cap_phy_info_5ghz[0]) & - IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; - cap_band = &pdev->cap.band[NL80211_BAND_6GHZ]; - cap_band->eht_cap_phy_info[0] |= support_320mhz; - return 0; - } - for (i = 0; i < ab->fw_pdev_count; i++) { struct ath12k_fw_pdev *fw_pdev = &ab->fw_pdev[i]; @@ -5079,14 +5070,22 @@ static int ath12k_wmi_tlv_mac_phy_caps_ext(struct ath12k_base *ab, u16 tag, void *data) { const struct ath12k_wmi_caps_ext_params *caps = ptr; + struct ath12k_band_cap *cap_band; + u32 support_320mhz; int i = 0, ret; if (tag != WMI_TAG_MAC_PHY_CAPABILITIES_EXT) return -EPROTO; if (ab->hw_params->single_pdev_only) { - if (ab->wmi_ab.preferred_hw_mode != le32_to_cpu(caps->hw_mode_id) && - caps->hw_mode_id != WMI_HOST_HW_MODE_SINGLE) + if (caps->hw_mode_id == WMI_HOST_HW_MODE_SINGLE) { + support_320mhz = le32_to_cpu(caps->eht_cap_phy_info_5ghz[0]) & + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + cap_band = &ab->pdevs[0].cap.band[NL80211_BAND_6GHZ]; + cap_band->eht_cap_phy_info[0] |= support_320mhz; + } + + if (ab->wmi_ab.preferred_hw_mode != le32_to_cpu(caps->hw_mode_id)) return 0; } else { for (i = 0; i < ab->num_radios; i++) { From e8ac32fc9f0a24730019e465f5c1ed310b0d2562 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:32:39 +0800 Subject: [PATCH 08/20] BACKPORT: FROMLIST: wifi: ath12k: fix mac phy capability parsing Currently ath12k_pull_mac_phy_cap_svc_ready_ext() assumes only one band supported in each phy, hence it skips 5 GHz band if 2 GHz band support is detected. This does not work for device which gets only one phy but has both bands supported, such as QCC2072. Change to check each band individually to fix this issue. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-6-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/wmi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 1451f1cba573f..f8dcec7492a18 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -496,6 +496,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, struct ath12k_band_cap *cap_band; struct ath12k_pdev_cap *pdev_cap = &pdev->cap; struct ath12k_fw_pdev *fw_pdev; + u32 supported_bands; u32 phy_map; u32 hw_idx, phy_idx = 0; int i; @@ -519,14 +520,19 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, return -EINVAL; mac_caps = wmi_mac_phy_caps + phy_idx; + supported_bands = le32_to_cpu(mac_caps->supported_bands); + + if (!(supported_bands & WMI_HOST_WLAN_2GHZ_CAP) && + !(supported_bands & WMI_HOST_WLAN_5GHZ_CAP)) + return -EINVAL; pdev->pdev_id = ath12k_wmi_mac_phy_get_pdev_id(mac_caps); pdev->hw_link_id = ath12k_wmi_mac_phy_get_hw_link_id(mac_caps); - pdev_cap->supported_bands |= le32_to_cpu(mac_caps->supported_bands); + pdev_cap->supported_bands |= supported_bands; pdev_cap->ampdu_density = le32_to_cpu(mac_caps->ampdu_density); fw_pdev = &ab->fw_pdev[ab->fw_pdev_count]; - fw_pdev->supported_bands = le32_to_cpu(mac_caps->supported_bands); + fw_pdev->supported_bands = supported_bands; fw_pdev->pdev_id = ath12k_wmi_mac_phy_get_pdev_id(mac_caps); fw_pdev->phy_id = le32_to_cpu(mac_caps->phy_id); ab->fw_pdev_count++; @@ -535,10 +541,12 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, * band to band for a single radio, need to see how this should be * handled. */ - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) { + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { pdev_cap->tx_chain_mask = le32_to_cpu(mac_caps->tx_chain_mask_2g); pdev_cap->rx_chain_mask = le32_to_cpu(mac_caps->rx_chain_mask_2g); - } else if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) { + } + + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { pdev_cap->vht_cap = le32_to_cpu(mac_caps->vht_cap_info_5g); pdev_cap->vht_mcs = le32_to_cpu(mac_caps->vht_supp_mcs_5g); pdev_cap->he_mcs = le32_to_cpu(mac_caps->he_supp_mcs_5g); @@ -548,8 +556,6 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, WMI_NSS_RATIO_EN_DIS_GET(mac_caps->nss_ratio); pdev_cap->nss_ratio_info = WMI_NSS_RATIO_INFO_GET(mac_caps->nss_ratio); - } else { - return -EINVAL; } /* tx/rx chainmask reported from fw depends on the actual hw chains used, @@ -565,7 +571,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, pdev_cap->rx_chain_mask_shift = find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32); - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) { + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; cap_band->phy_id = le32_to_cpu(mac_caps->phy_id); cap_band->max_bw_supported = le32_to_cpu(mac_caps->max_bw_supported_2g); @@ -585,7 +591,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, le32_to_cpu(mac_caps->he_ppet2g.ppet16_ppet8_ru3_ru0[i]); } - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) { + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; cap_band->phy_id = le32_to_cpu(mac_caps->phy_id); cap_band->max_bw_supported = From e65f5bd89581ee950884c192b22cb22b7bb3eb40 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:34:39 +0800 Subject: [PATCH 09/20] BACKPORT: FROMLIST: wifi: ath12k: add hardware registers for QCC2072 Add hardware registers and populate hw_regs field in ath12k_wifi7_hw_ver_map for QCC2072. Note for some registers not defined and not used by QCC2072, a magic value is assigned. Also populate other fields to be the same with WCN7850. Among them, however, QCC2072 requires different HAL ops and descriptor size, both will be updated in upcoming patches. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-7-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/core.h | 1 + drivers/net/wireless/ath/ath12k/hw.c | 87 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/hw.h | 2 + 3 files changed, 90 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 3c1e0069be1e7..5ee85f1867f67 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -155,6 +155,7 @@ enum ath12k_hw_rev { ATH12K_HW_QCN9274_HW20, ATH12K_HW_WCN7850_HW20, ATH12K_HW_IPQ5332_HW10, + ATH12K_HW_QCC2072_HW10, }; enum ath12k_firmware_mode { diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 6791ae1d64e50..6ee2e2220cc49 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1278,6 +1278,93 @@ static const struct ath12k_hw_regs wcn7850_regs = { .gcc_gcc_pcie_hot_rst = 0x1e40304, }; +static const struct ath12k_hw_regs qcc2072_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_id = 0x00000920, + .hal_tcl1_ring_misc = 0x00000928, + .hal_tcl1_ring_tp_addr_lsb = 0x00000934, + .hal_tcl1_ring_tp_addr_msb = 0x00000938, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000948, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x0000094c, + .hal_tcl1_ring_msi1_base_lsb = 0x00000960, + .hal_tcl1_ring_msi1_base_msb = 0x00000964, + .hal_tcl1_ring_msi1_data = 0x00000968, + .hal_tcl_ring_base_lsb = 0x00000b70, + .hal_tcl1_ring_base_lsb = 0x00000918, + .hal_tcl1_ring_base_msb = 0x0000091c, + .hal_tcl2_ring_base_lsb = 0x00000990, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000d50, + + .hal_wbm_idle_ring_base_lsb = 0x00000d3c, + .hal_wbm_idle_ring_misc_addr = 0x00000d4c, + .hal_wbm_r0_idle_list_cntl_addr = 0x00000240, + .hal_wbm_r0_idle_list_size_addr = 0x00000244, + .hal_wbm_scattered_ring_base_lsb = 0x00000250, + .hal_wbm_scattered_ring_base_msb = 0x00000254, + .hal_wbm_scattered_desc_head_info_ix0 = 0x00000260, + .hal_wbm_scattered_desc_head_info_ix1 = 0x00000264, + .hal_wbm_scattered_desc_tail_info_ix0 = 0x00000270, + .hal_wbm_scattered_desc_tail_info_ix1 = 0x00000274, + .hal_wbm_scattered_desc_ptr_hp_addr = 0x00000027c, + + .hal_wbm_sw_release_ring_base_lsb = 0x0000037c, + .hal_wbm_sw1_release_ring_base_lsb = ATH12K_HW_REG_UNDEFINED, + .hal_wbm0_release_ring_base_lsb = 0x00000e08, + .hal_wbm1_release_ring_base_lsb = 0x00000e80, + + /* PCIe base address */ + .pcie_qserdes_sysclk_en_sel = 0x01e0c0ac, + .pcie_pcs_osc_dtct_config_base = 0x01e0d45c, + + /* PPE release ring address */ + .hal_ppe_rel_ring_base = 0x0000046c, + + /* REO DEST ring address */ + .hal_reo2_ring_base = 0x00000578, + .hal_reo1_misc_ctrl_addr = 0x00000ba0, + .hal_reo1_sw_cookie_cfg0 = 0x0000006c, + .hal_reo1_sw_cookie_cfg1 = 0x00000070, + .hal_reo1_qdesc_lut_base0 = ATH12K_HW_REG_UNDEFINED, + .hal_reo1_qdesc_lut_base1 = ATH12K_HW_REG_UNDEFINED, + .hal_reo1_ring_base_lsb = 0x00000500, + .hal_reo1_ring_base_msb = 0x00000504, + .hal_reo1_ring_id = 0x00000508, + .hal_reo1_ring_misc = 0x00000510, + .hal_reo1_ring_hp_addr_lsb = 0x00000514, + .hal_reo1_ring_hp_addr_msb = 0x00000518, + .hal_reo1_ring_producer_int_setup = 0x00000524, + .hal_reo1_ring_msi1_base_lsb = 0x00000548, + .hal_reo1_ring_msi1_base_msb = 0x0000054c, + .hal_reo1_ring_msi1_data = 0x00000550, + .hal_reo1_aging_thres_ix0 = 0x00000b2c, + .hal_reo1_aging_thres_ix1 = 0x00000b30, + .hal_reo1_aging_thres_ix2 = 0x00000b34, + .hal_reo1_aging_thres_ix3 = 0x00000b38, + + /* REO Exception ring address */ + .hal_reo2_sw0_ring_base = 0x000008c0, + + /* REO Reinject ring address */ + .hal_sw2reo_ring_base = 0x00000320, + .hal_sw2reo1_ring_base = 0x00000398, + + /* REO cmd ring address */ + .hal_reo_cmd_ring_base = 0x000002a8, + + /* REO status ring address */ + .hal_reo_status_ring_base = 0x00000aa0, + + /* CE base address */ + .hal_umac_ce0_src_reg_base = 0x01b80000, + .hal_umac_ce0_dest_reg_base = 0x01b81000, + .hal_umac_ce1_src_reg_base = 0x01b82000, + .hal_umac_ce1_dest_reg_base = 0x01b83000, + + .gcc_gcc_pcie_hot_rst = 0x1e65304, +}; + static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = { .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, .wbm2sw_cc_enable = HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW0_EN | diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 8ce11c3e6d5c2..c83c89703a64e 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -285,6 +285,8 @@ enum ath12k_bd_ie_type { ATH12K_BD_IE_REGDB = 1, }; +#define ATH12K_HW_REG_UNDEFINED 0xdeadbeaf + struct ath12k_hw_regs { u32 hal_tcl1_ring_id; u32 hal_tcl1_ring_misc; From b1dfb61b9db55562dcffa69369a66c78f1d8e68b Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:36:23 +0800 Subject: [PATCH 10/20] BACKPORT: FROMLIST: wifi: ath12k: add hardware parameters for QCC2072 Add hardware parameters for QCC2072, these parameters are directly taken from WCN7850, with exceptions to hardware name, revision, firmware directory, iova_mask and RFKILL parameter set. Compared to WCN7850, QCC2072 doesn't require aligned IOVA when transmitting packets, hence iova_mask is set to zero. Besides, WCN7850 has a dedicated GPIO for RFKILL purpose, however QCC2072 has it coupled with WLAN_EN pin. For QCC2072, host is not allowed to send any RFKILL configuration info to firmware, or firmware crashes. Hence those parameters are all cleared to skip configuring command. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-8-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/hw.c | 90 ++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 6ee2e2220cc49..96d7f134cdf0e 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1740,6 +1740,96 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dp_primary_link_only = true, }, + { + .name = "qcc2072 hw1.0", + .hw_rev = ATH12K_HW_QCC2072_HW10, + + .fw = { + .dir = "QCC2072/hw1.0", + .board_size = 256 * 1024, + .cal_offset = 256 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, + }, + + .max_radios = 1, + .single_pdev_only = true, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850, + .internal_sleep_clock = true, + + .hw_ops = &wcn7850_ops, + .ring_mask = &ath12k_hw_ring_mask_wcn7850, + .regs = &qcc2072_regs, + + .host_ce_config = ath12k_host_ce_config_wcn7850, + .ce_count = 9, + .target_ce_config = ath12k_target_ce_config_wlan_wcn7850, + .target_ce_count = 9, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_wcn7850, + .svc_to_ce_map_len = 14, + + .hal_params = &ath12k_hw_hal_params_wcn7850, + + .rxdma1_enable = false, + .num_rxdma_per_pdev = 2, + .num_rxdma_dst_ring = 1, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + .supports_monitor = false, + + .idle_ps = true, + .download_calib = false, + .supports_suspend = true, + .tcl_ring_retry = false, + .reoq_lut_support = false, + .supports_shadow_regs = true, + + .num_tcl_banks = 7, + .max_tx_ring = 3, + + .mhi_config = &ath12k_mhi_config_wcn7850, + + .wmi_init = ath12k_wmi_init_wcn7850, + + .hal_ops = &hal_wcn7850_ops, + + .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01) | + BIT(CNSS_PCIE_PERST_NO_PULL_V01), + + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, + + .rddm_size = 0x780000, + + .def_num_link = 2, + .max_mlo_peer = 32, + + .otp_board_id_register = 0, + + .supports_sta_ps = true, + + .acpi_guid = &wcn7850_uuid, + .supports_dynamic_smps_6ghz = false, + + .iova_mask = 0, + + .supports_aspm = true, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = true, + + .dp_primary_link_only = true, + }, + }; int ath12k_hw_init(struct ath12k_base *ab) From 2860d2331bde8ab05954db249bd6c66853ece463 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:46:04 +0800 Subject: [PATCH 11/20] BACKPORT: FROMLIST: wifi: ath12k: advertise standard ELF image format for QCC2072 Firmware (amss.bin) of QCC2072 is a standard ELF image, however those of other chips are not. MHI subsystem supports both format, but by default takes it as non-standard. Flag this to MHI subsystem to avoid firmware loading failure. Also refine hardware parameter dereferencing by defining a new local pointer. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-9-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/hw.c | 5 +++++ drivers/net/wireless/ath/ath12k/hw.h | 1 + drivers/net/wireless/ath/ath12k/mhi.c | 10 ++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 96d7f134cdf0e..f2eaef1ec0f7a 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1411,6 +1411,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .board_size = 256 * 1024, .cal_offset = 128 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, + .std_elf_img = false, }, .max_radios = 1, .single_pdev_only = false, @@ -1497,6 +1498,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .board_size = 256 * 1024, .cal_offset = 256 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, + .std_elf_img = false, }, .max_radios = 1, @@ -1585,6 +1587,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .board_size = 256 * 1024, .cal_offset = 128 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, + .std_elf_img = false, }, .max_radios = 2, .single_pdev_only = false, @@ -1670,6 +1673,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .board_size = 256 * 1024, .cal_offset = 128 * 1024, .m3_loader = ath12k_m3_fw_loader_remoteproc, + .std_elf_img = false, }, .max_radios = 1, .single_pdev_only = false, @@ -1749,6 +1753,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .board_size = 256 * 1024, .cal_offset = 256 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, + .std_elf_img = true, }, .max_radios = 1, diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index c83c89703a64e..4eab0b5542215 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -147,6 +147,7 @@ struct ath12k_hw_params { size_t board_size; size_t cal_offset; enum ath12k_m3_fw_loaders m3_loader; + bool std_elf_img:1; } fw; u8 max_radios; diff --git a/drivers/net/wireless/ath/ath12k/mhi.c b/drivers/net/wireless/ath/ath12k/mhi.c index 08f44baf182a5..53e31d36be7b3 100644 --- a/drivers/net/wireless/ath/ath12k/mhi.c +++ b/drivers/net/wireless/ath/ath12k/mhi.c @@ -317,6 +317,7 @@ static void ath12k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl, int ath12k_mhi_register(struct ath12k_pci *ab_pci) { struct ath12k_base *ab = ab_pci->ab; + const struct ath12k_hw_params *hw_params = ab->hw_params; struct mhi_controller *mhi_ctrl; unsigned int board_id; int ret; @@ -331,11 +332,12 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) mhi_ctrl->cntrl_dev = ab->dev; mhi_ctrl->regs = ab->mem; mhi_ctrl->reg_len = ab->mem_len; - mhi_ctrl->rddm_size = ab->hw_params->rddm_size; + mhi_ctrl->rddm_size = hw_params->rddm_size; + mhi_ctrl->standard_elf_image = hw_params->fw.std_elf_img; - if (ab->hw_params->otp_board_id_register) { + if (hw_params->otp_board_id_register) { board_id = - ath12k_pci_read32(ab, ab->hw_params->otp_board_id_register); + ath12k_pci_read32(ab, hw_params->otp_board_id_register); board_id = u32_get_bits(board_id, OTP_BOARD_ID_MASK); if (!board_id || (board_id == OTP_INVALID_BOARD_ID)) { @@ -392,7 +394,7 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) mhi_ctrl->read_reg = ath12k_mhi_op_read_reg; mhi_ctrl->write_reg = ath12k_mhi_op_write_reg; - ret = mhi_register_controller(mhi_ctrl, ab->hw_params->mhi_config); + ret = mhi_register_controller(mhi_ctrl, hw_params->mhi_config); if (ret) { ath12k_err(ab, "failed to register to mhi bus, err = %d\n", ret); goto free_controller; From 9b2f93d303248fa17e312af063017900c6107bb9 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:47:45 +0800 Subject: [PATCH 12/20] BACKPORT: FROMLIST: wifi: ath12k: support LPASS_SHARED target memory type QCC2072 requires a new type of QMI target memory named LPASS_SHARED_V01, add support for it. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-10-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/qmi.c | 1 + drivers/net/wireless/ath/ath12k/qmi.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index b7c48b6706dfc..4966697f4e625 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2609,6 +2609,7 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab) case M3_DUMP_REGION_TYPE: case PAGEABLE_MEM_REGION_TYPE: case CALDB_MEM_REGION_TYPE: + case LPASS_SHARED_V01_REGION_TYPE: ret = ath12k_qmi_alloc_chunk(ab, chunk); if (ret) goto err; diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 7a88268aa1e9e..050dcaca1cb75 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -178,6 +178,7 @@ enum ath12k_qmi_target_mem { CALDB_MEM_REGION_TYPE = 0x4, MLO_GLOBAL_MEM_REGION_TYPE = 0x8, PAGEABLE_MEM_REGION_TYPE = 0x9, + LPASS_SHARED_V01_REGION_TYPE = 0xb, }; enum qmi_wlanfw_host_build_type { From 636c84f7506f422ef8a66bef1d655ef11fcb7141 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 18 Dec 2025 19:31:54 +0800 Subject: [PATCH 13/20] BACKPORT: FROMLIST: wifi: ath12k: support downloading auxiliary ucode image for QCC2072 QCC2072 requires another firmware image named aux_ucode.bin, add support to download it. Add a new hardware parameter download_aux_ucode to make sure other chips are not affected. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-11-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/core.h | 2 + drivers/net/wireless/ath/ath12k/fw.c | 8 ++ drivers/net/wireless/ath/ath12k/fw.h | 1 + drivers/net/wireless/ath/ath12k/hw.c | 7 +- drivers/net/wireless/ath/ath12k/hw.h | 2 + drivers/net/wireless/ath/ath12k/qmi.c | 179 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath12k/qmi.h | 15 +++ 7 files changed, 212 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 5ee85f1867f67..7cb264d9fd942 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -1192,6 +1192,8 @@ struct ath12k_base { size_t amss_dualmac_len; const u8 *m3_data; size_t m3_len; + const u8 *aux_uc_data; + size_t aux_uc_len; DECLARE_BITMAP(fw_features, ATH12K_FW_FEATURE_COUNT); bool fw_features_valid; diff --git a/drivers/net/wireless/ath/ath12k/fw.c b/drivers/net/wireless/ath/ath12k/fw.c index 5ac497f80cad8..2cf30d637c6ad 100644 --- a/drivers/net/wireless/ath/ath12k/fw.c +++ b/drivers/net/wireless/ath/ath12k/fw.c @@ -121,6 +121,14 @@ static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab, ab->fw.m3_data = data; ab->fw.m3_len = ie_len; break; + case ATH12K_FW_IE_AUX_UC_IMAGE: + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "found aux_uc image ie (%zd B)\n", + ie_len); + + ab->fw.aux_uc_data = data; + ab->fw.aux_uc_len = ie_len; + break; case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE: ath12k_dbg(ab, ATH12K_DBG_BOOT, "found dualmac fw image ie (%zd B)\n", diff --git a/drivers/net/wireless/ath/ath12k/fw.h b/drivers/net/wireless/ath/ath12k/fw.h index 7afaefed5086f..c854d88d8f7bd 100644 --- a/drivers/net/wireless/ath/ath12k/fw.h +++ b/drivers/net/wireless/ath/ath12k/fw.h @@ -15,6 +15,7 @@ enum ath12k_fw_ie_type { ATH12K_FW_IE_AMSS_IMAGE = 2, ATH12K_FW_IE_M3_IMAGE = 3, ATH12K_FW_IE_AMSS_DUALMAC_IMAGE = 4, + ATH12K_FW_IE_AUX_UC_IMAGE = 5, }; enum ath12k_fw_features { diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index f2eaef1ec0f7a..7e5142fa9fcbb 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1412,6 +1412,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .cal_offset = 128 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, .std_elf_img = false, + .download_aux_ucode = false, }, .max_radios = 1, .single_pdev_only = false, @@ -1499,6 +1500,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .cal_offset = 256 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, .std_elf_img = false, + .download_aux_ucode = false, }, .max_radios = 1, @@ -1588,6 +1590,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .cal_offset = 128 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, .std_elf_img = false, + .download_aux_ucode = false, }, .max_radios = 2, .single_pdev_only = false, @@ -1754,6 +1757,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .cal_offset = 256 * 1024, .m3_loader = ath12k_m3_fw_loader_driver, .std_elf_img = true, + .download_aux_ucode = true, }, .max_radios = 1, @@ -1804,7 +1808,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_ops = &hal_wcn7850_ops, .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01) | - BIT(CNSS_PCIE_PERST_NO_PULL_V01), + BIT(CNSS_PCIE_PERST_NO_PULL_V01) | + BIT(CNSS_AUX_UC_SUPPORT_V01), .rfkill_pin = 0, .rfkill_cfg = 0, diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 4eab0b5542215..b1d2aebe59734 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -78,6 +78,7 @@ #define ATH12K_DEFAULT_CAL_FILE "caldata.bin" #define ATH12K_AMSS_FILE "amss.bin" #define ATH12K_M3_FILE "m3.bin" +#define ATH12K_AUX_UC_FILE "aux_ucode.bin" #define ATH12K_REGDB_FILE_NAME "regdb.bin" #define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128 @@ -148,6 +149,7 @@ struct ath12k_hw_params { size_t cal_offset; enum ath12k_m3_fw_loaders m3_loader; bool std_elf_img:1; + bool download_aux_ucode:1; } fw; u8 max_radios; diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 4966697f4e625..5a80b5e6114d4 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1623,6 +1623,47 @@ static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { }, }; +static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, addr), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_req_msg_v01, size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static const struct qmi_elem_info qmi_wlanfw_aux_uc_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_aux_uc_info_resp_msg_v01, resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_4_BYTE, @@ -3237,6 +3278,130 @@ int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) return ret; } +static void ath12k_qmi_aux_uc_free(struct ath12k_base *ab) +{ + struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem; + + if (!aux_uc_mem->vaddr) + return; + + dma_free_coherent(ab->dev, aux_uc_mem->total_size, + aux_uc_mem->vaddr, aux_uc_mem->paddr); + aux_uc_mem->vaddr = NULL; + aux_uc_mem->total_size = 0; + aux_uc_mem->size = 0; +} + +static int ath12k_qmi_aux_uc_load(struct ath12k_base *ab) +{ + struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem; + const struct firmware *fw = NULL; + const void *aux_uc_data; + char path[100]; + size_t aux_uc_len; + int ret; + + if (ab->fw.aux_uc_data && ab->fw.aux_uc_len > 0) { + /* firmware-N.bin had a aux_uc firmware file so use that */ + aux_uc_data = ab->fw.aux_uc_data; + aux_uc_len = ab->fw.aux_uc_len; + } else { + /* No aux_uc file in firmware-N.bin so try to request old + * separate aux_ucode.bin. + */ + fw = ath12k_core_firmware_request(ab, ATH12K_AUX_UC_FILE); + if (IS_ERR(fw)) { + ret = PTR_ERR(fw); + ath12k_core_create_firmware_path(ab, ATH12K_AUX_UC_FILE, + path, sizeof(path)); + ath12k_err(ab, "failed to load %s: %d\n", path, ret); + return ret; + } + + aux_uc_data = fw->data; + aux_uc_len = fw->size; + } + + /* In recovery/resume cases, AUX_UC buffer is not freed, try to reuse that */ + if (aux_uc_mem->vaddr) { + if (aux_uc_mem->total_size >= aux_uc_len) + goto copy; + + /* Old buffer is too small, free and reallocate */ + ath12k_qmi_aux_uc_free(ab); + } + + aux_uc_mem->vaddr = dma_alloc_coherent(ab->dev, aux_uc_len, + &aux_uc_mem->paddr, GFP_KERNEL); + if (!aux_uc_mem->vaddr) { + ret = -ENOMEM; + goto out; + } + + aux_uc_mem->total_size = aux_uc_len; + +copy: + memcpy(aux_uc_mem->vaddr, aux_uc_data, aux_uc_len); + aux_uc_mem->size = aux_uc_len; + + ret = 0; + +out: + release_firmware(fw); + + return ret; +} + +static noinline_for_stack +int ath12k_qmi_wlanfw_aux_uc_info_send(struct ath12k_base *ab) +{ + struct m3_mem_region *aux_uc_mem = &ab->qmi.aux_uc_mem; + struct qmi_wlanfw_aux_uc_info_req_msg_v01 req = {}; + struct qmi_wlanfw_aux_uc_info_resp_msg_v01 resp = {}; + struct qmi_txn txn; + int ret = 0; + + ret = ath12k_qmi_aux_uc_load(ab); + if (ret) { + ath12k_err(ab, "failed to load aux_uc firmware: %d", ret); + return ret; + } + + req.addr = aux_uc_mem->paddr; + req.size = aux_uc_mem->size; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_aux_uc_info_resp_msg_v01_ei, &resp); + if (ret < 0) + goto out; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_AUX_UC_INFO_REQ_V01, + QMI_WLANFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_aux_uc_info_req_msg_v01_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath12k_warn(ab, "qmi failed to send AUX_UC information request, err = %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH12K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) { + ath12k_warn(ab, "qmi failed AUX_UC information request %d\n", ret); + goto out; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath12k_warn(ab, "qmi AUX_UC info request failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = -EINVAL; + goto out; + } +out: + return ret; +} + static int ath12k_qmi_wlanfw_mode_send(struct ath12k_base *ab, u32 mode) { @@ -3585,6 +3750,7 @@ static noinline_for_stack int ath12k_qmi_event_mem_request(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; + const struct ath12k_hw_params *hw_params = ab->hw_params; int ret; ret = ath12k_qmi_respond_fw_mem_request(ab); @@ -3601,6 +3767,7 @@ static noinline_for_stack int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) { struct ath12k_base *ab = qmi->ab; + const struct ath12k_hw_params *hw_params = ab->hw_params; int ret; ret = ath12k_qmi_request_target_cap(ab); @@ -3621,7 +3788,7 @@ int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) return ret; } - if (ab->hw_params->download_calib) { + if (hw_params->download_calib) { ret = ath12k_qmi_load_bdf_qmi(ab, ATH12K_QMI_BDF_TYPE_CALIBRATION); if (ret < 0) ath12k_warn(ab, "qmi failed to load calibrated data :%d\n", ret); @@ -3633,6 +3800,14 @@ int ath12k_qmi_event_load_bdf(struct ath12k_qmi *qmi) return ret; } + if (hw_params->fw.download_aux_ucode) { + ret = ath12k_qmi_wlanfw_aux_uc_info_send(ab); + if (ret < 0) { + ath12k_warn(ab, "qmi failed to send aux_uc info req: %d\n", ret); + return ret; + } + } + return ret; } @@ -3906,6 +4081,7 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab) qmi_handle_release(&ab->qmi.handle); cancel_work_sync(&ab->qmi.event_work); destroy_workqueue(ab->qmi.event_wq); + ath12k_qmi_aux_uc_free(ab); ath12k_qmi_m3_free(ab); ath12k_qmi_free_target_mem_chunk(ab); ab->qmi.ab = NULL; @@ -3914,5 +4090,6 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab) void ath12k_qmi_free_resource(struct ath12k_base *ab) { ath12k_qmi_free_target_mem_chunk(ab); + ath12k_qmi_aux_uc_free(ab); ath12k_qmi_m3_free(ab); } diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 050dcaca1cb75..b5a4a01391cba 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -154,6 +154,7 @@ struct ath12k_qmi { u8 num_radios; struct target_info target; struct m3_mem_region m3_mem; + struct m3_mem_region aux_uc_mem; unsigned int service_ins_id; struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01]; }; @@ -203,6 +204,7 @@ enum ath12k_qmi_cnss_feature { CNSS_FEATURE_MIN_ENUM_VAL_V01 = INT_MIN, CNSS_QDSS_CFG_MISS_V01 = 3, CNSS_PCIE_PERST_NO_PULL_V01 = 4, + CNSS_AUX_UC_SUPPORT_V01 = 6, CNSS_MAX_FEATURE_V01 = 64, CNSS_FEATURE_MAX_ENUM_VAL_V01 = INT_MAX, }; @@ -541,6 +543,19 @@ struct qmi_wlanfw_m3_info_resp_msg_v01 { struct qmi_response_type_v01 resp; }; +#define QMI_WLANFW_AUX_UC_INFO_REQ_MSG_V01_MAX_MSG_LEN 18 +#define QMI_WLANFW_AUX_UC_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +#define QMI_WLANFW_AUX_UC_INFO_REQ_V01 0x005A + +struct qmi_wlanfw_aux_uc_info_req_msg_v01 { + u64 addr; + u32 size; +}; + +struct qmi_wlanfw_aux_uc_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + #define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11 #define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7 #define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803 From 30f3b764ac0d74247af23a557bd479fb131b05d1 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 12 Nov 2025 10:57:49 +0800 Subject: [PATCH 14/20] BACKPORT: FROMLIST: wifi: ath12k: add HAL descriptor and ops for QCC2072 QCC2072 has different HAL descriptors hence require different HAL handling, compared to other chips. Add support for this. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-12-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/hal.c | 505 +++++++++++++++++++++ drivers/net/wireless/ath/ath12k/hal.h | 2 + drivers/net/wireless/ath/ath12k/hal_desc.h | 25 + drivers/net/wireless/ath/ath12k/hw.c | 2 +- drivers/net/wireless/ath/ath12k/rx_desc.h | 17 + 5 files changed, 550 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index b3f446b570c4f..0fecb5ff10ac4 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -1629,6 +1629,511 @@ const struct hal_ops hal_wcn7850_ops = { .get_hal_rx_compact_ops = NULL, }; +static bool ath12k_hw_qcc2072_rx_desc_get_first_msdu(struct hal_rx_desc *desc) +{ + return !!le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_FIRST_MSDU); +} + +static bool ath12k_hw_qcc2072_rx_desc_get_last_msdu(struct hal_rx_desc *desc) +{ + return !!le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_LAST_MSDU); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_l3_pad_bytes(struct hal_rx_desc *desc) +{ + return (u8)le16_get_bits(desc->u.qcc2072.msdu_end.info5, + RX_MSDU_END_INFO5_L3_HDR_PADDING); +} + +static bool ath12k_hw_qcc2072_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.mpdu_start.info4, + RX_MPDU_START_INFO4_ENCRYPT_INFO_VALID); +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_encrypt_type(struct hal_rx_desc *desc) +{ + return le32_get_bits(desc->u.qcc2072.mpdu_start.info2, + RX_MPDU_START_INFO2_ENC_TYPE); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_decap_type(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info11, + RX_MSDU_END_INFO11_DECAP_FORMAT); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_mesh_ctl(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info11, + RX_MSDU_END_INFO11_MESH_CTRL_PRESENT); +} + +static bool ath12k_hw_qcc2072_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.mpdu_start.info4, + RX_MPDU_START_INFO4_MPDU_SEQ_CTRL_VALID); +} + +static bool ath12k_hw_qcc2072_rx_desc_get_mpdu_fc_valid(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.mpdu_start.info4, + RX_MPDU_START_INFO4_MPDU_FCTRL_VALID); +} + +static u16 ath12k_hw_qcc2072_rx_desc_get_mpdu_start_seq_no(struct hal_rx_desc *desc) +{ + return (u16)le32_get_bits(desc->u.qcc2072.mpdu_start.info4, + RX_MPDU_START_INFO4_MPDU_SEQ_NUM); +} + +static u16 ath12k_hw_qcc2072_rx_desc_get_msdu_len(struct hal_rx_desc *desc) +{ + return (u16)le32_get_bits(desc->u.qcc2072.msdu_end.info10, + RX_MSDU_END_INFO10_MSDU_LENGTH); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_sgi(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info12, + RX_MSDU_END_INFO12_SGI); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_rate_mcs(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info12, + RX_MSDU_END_INFO12_RATE_MCS); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_rx_bw(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info12, + RX_MSDU_END_INFO12_RECV_BW); +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_msdu_freq(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->u.qcc2072.msdu_end.phy_meta_data); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_pkt_type(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info12, + RX_MSDU_END_INFO12_PKT_TYPE); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_nss(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.msdu_end.info12, + RX_MSDU_END_INFO12_MIMO_SS_BITMAP); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_mpdu_tid(struct hal_rx_desc *desc) +{ + return (u8)le32_get_bits(desc->u.qcc2072.mpdu_start.info2, + RX_MPDU_START_INFO2_TID); +} + +static u16 ath12k_hw_qcc2072_rx_desc_get_mpdu_peer_id(struct hal_rx_desc *desc) +{ + return __le16_to_cpu(desc->u.qcc2072.mpdu_start.sw_peer_id); +} + +static void ath12k_hw_qcc2072_rx_desc_copy_end_tlv(struct hal_rx_desc *fdesc, + struct hal_rx_desc *ldesc) +{ + memcpy(&fdesc->u.qcc2072.msdu_end, &ldesc->u.qcc2072.msdu_end, + sizeof(struct rx_msdu_end_qcn9274)); +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_mpdu_start_tag(struct hal_rx_desc *desc) +{ + return le32_get_bits(desc->u.qcc2072.mpdu_start_tag, + HAL_TLV_HDR_TAG); +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_mpdu_ppdu_id(struct hal_rx_desc *desc) +{ + return __le16_to_cpu(desc->u.qcc2072.mpdu_start.phy_ppdu_id); +} + +static void ath12k_hw_qcc2072_rx_desc_set_msdu_len(struct hal_rx_desc *desc, u16 len) +{ + u32 info = __le32_to_cpu(desc->u.qcc2072.msdu_end.info10); + + info &= ~RX_MSDU_END_INFO10_MSDU_LENGTH; + info |= u32_encode_bits(len, RX_MSDU_END_INFO10_MSDU_LENGTH); + + desc->u.qcc2072.msdu_end.info10 = __cpu_to_le32(info); +} + +static u8 *ath12k_hw_qcc2072_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) +{ + return &desc->u.qcc2072.msdu_payload[0]; +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_mpdu_start_offset(void) +{ + return offsetof(struct hal_rx_desc_qcc2072, mpdu_start_tag); +} + +static u32 ath12k_hw_qcc2072_rx_desc_get_msdu_end_offset(void) +{ + return offsetof(struct hal_rx_desc_qcc2072, msdu_end_tag); +} + +static bool ath12k_hw_qcc2072_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->u.qcc2072.mpdu_start.info4) & + RX_MPDU_START_INFO4_MAC_ADDR2_VALID; +} + +static u8 *ath12k_hw_qcc2072_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) +{ + return desc->u.qcc2072.mpdu_start.addr2; +} + +static bool ath12k_hw_qcc2072_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) +{ + return __le32_to_cpu(desc->u.qcc2072.msdu_end.info13) & + RX_MSDU_END_INFO13_MCAST_BCAST; +} + +static void ath12k_hw_qcc2072_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, + struct ieee80211_hdr *hdr) +{ + hdr->frame_control = desc->u.qcc2072.mpdu_start.frame_ctrl; + hdr->duration_id = desc->u.qcc2072.mpdu_start.duration; + ether_addr_copy(hdr->addr1, desc->u.qcc2072.mpdu_start.addr1); + ether_addr_copy(hdr->addr2, desc->u.qcc2072.mpdu_start.addr2); + ether_addr_copy(hdr->addr3, desc->u.qcc2072.mpdu_start.addr3); + if (__le32_to_cpu(desc->u.qcc2072.mpdu_start.info4) & + RX_MPDU_START_INFO4_MAC_ADDR4_VALID) { + ether_addr_copy(hdr->addr4, desc->u.qcc2072.mpdu_start.addr4); + } + hdr->seq_ctrl = desc->u.qcc2072.mpdu_start.seq_ctrl; +} + +static void ath12k_hw_qcc2072_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, + u8 *crypto_hdr, + enum hal_encrypt_type enctype) +{ + unsigned int key_id; + + switch (enctype) { + case HAL_ENCRYPT_TYPE_OPEN: + return; + case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: + case HAL_ENCRYPT_TYPE_TKIP_MIC: + crypto_hdr[0] = + HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcc2072.mpdu_start.pn[0]); + crypto_hdr[1] = 0; + crypto_hdr[2] = + HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcc2072.mpdu_start.pn[0]); + break; + 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: + crypto_hdr[0] = + HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcc2072.mpdu_start.pn[0]); + crypto_hdr[1] = + HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcc2072.mpdu_start.pn[0]); + crypto_hdr[2] = 0; + break; + 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: + return; + } + key_id = u32_get_bits(__le32_to_cpu(desc->u.qcc2072.mpdu_start.info5), + RX_MPDU_START_INFO5_KEY_ID); + crypto_hdr[3] = (u8)(0x20 | (key_id << 6)); + crypto_hdr[4] = HAL_RX_MPDU_INFO_PN_GET_BYTE3(desc->u.qcc2072.mpdu_start.pn[0]); + crypto_hdr[5] = HAL_RX_MPDU_INFO_PN_GET_BYTE4(desc->u.qcc2072.mpdu_start.pn[0]); + crypto_hdr[6] = HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcc2072.mpdu_start.pn[1]); + crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcc2072.mpdu_start.pn[1]); +} + +static int ath12k_hal_srng_create_config_qcc2072(struct ath12k_base *ab) +{ + struct ath12k_hal *hal = &ab->hal; + struct hal_srng_config *s; + + hal->srng_config = kmemdup(hw_srng_config_template, + sizeof(hw_srng_config_template), + GFP_KERNEL); + if (!hal->srng_config) + return -ENOMEM; + + s = &hal->srng_config[HAL_REO_DST]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP; + s->reg_size[0] = HAL_REO2_RING_BASE_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab); + s->reg_size[1] = HAL_REO2_RING_HP - HAL_REO1_RING_HP; + + s = &hal->srng_config[HAL_REO_EXCEPTION]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_SW0_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_SW0_RING_HP; + + s = &hal->srng_config[HAL_REO_REINJECT]; + s->max_rings = 1; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP; + + s = &hal->srng_config[HAL_REO_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP; + s->entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats_qcc2072)) >> 2, + + s = &hal->srng_config[HAL_REO_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP; + s->entry_size = (sizeof(struct hal_tlv_hdr) + + sizeof(struct hal_reo_get_queue_stats_status_qcc2072)) >> 2, + + s = &hal->srng_config[HAL_TCL_DATA]; + s->max_rings = 5; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP; + s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab); + s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP; + + s = &hal->srng_config[HAL_TCL_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP; + + s = &hal->srng_config[HAL_TCL_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + s = &hal->srng_config[HAL_CE_SRC]; + s->max_rings = 12; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + + s = &hal->srng_config[HAL_CE_DST]; + s->max_rings = 12; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + + s = &hal->srng_config[HAL_CE_DST_STATUS]; + s->max_rings = 12; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + + HAL_CE_DST_STATUS_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + + s = &hal->srng_config[HAL_WBM_IDLE_LINK]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_HP; + + s = &hal->srng_config[HAL_SW2WBM_RELEASE]; + s->max_rings = 1; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + + HAL_WBM_SW_RELEASE_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_SW_RELEASE_RING_HP; + + s = &hal->srng_config[HAL_WBM2SW_RELEASE]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM0_RELEASE_RING_HP; + s->reg_size[0] = HAL_WBM1_RELEASE_RING_BASE_LSB(ab) - + HAL_WBM0_RELEASE_RING_BASE_LSB(ab); + s->reg_size[1] = HAL_WBM1_RELEASE_RING_HP - HAL_WBM0_RELEASE_RING_HP; + + s = &hal->srng_config[HAL_RXDMA_BUF]; + s->max_rings = 2; + s->mac_type = ATH12K_HAL_SRNG_PMAC; + + s = &hal->srng_config[HAL_RXDMA_DST]; + s->max_rings = 1; + s->entry_size = sizeof(struct hal_reo_entrance_ring) >> 2; + + /* below rings are not used */ + s = &hal->srng_config[HAL_RXDMA_DIR_BUF]; + s->max_rings = 0; + + s = &hal->srng_config[HAL_PPE2TCL]; + s->max_rings = 0; + + s = &hal->srng_config[HAL_PPE_RELEASE]; + s->max_rings = 0; + + s = &hal->srng_config[HAL_TX_MONITOR_BUF]; + s->max_rings = 0; + + s = &hal->srng_config[HAL_TX_MONITOR_DST]; + s->max_rings = 0; + + s = &hal->srng_config[HAL_PPE2TCL]; + s->max_rings = 0; + + return 0; +} + +static void ath12k_hal_qcc2072_reo_init_cmd_ring(void *entry, int cmd_num) +{ + struct hal_reo_get_queue_stats *desc; + struct hal_tlv_hdr *tlv = entry; + + desc = (struct hal_reo_get_queue_stats *)tlv->value; + desc->cmd.info0 = le32_encode_bits(cmd_num, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER); +} + +static void *ath12k_hal_qcc2072_reo_cmd_encode_hdr(void *reo_desc, + u64 tag, u64 len) +{ + struct hal_tlv_hdr *tlv = reo_desc; + + tlv->tl = le32_encode_bits((u32)tag, HAL_TLV_HDR_TAG) | + le32_encode_bits((u32)len, HAL_TLV_HDR_LEN); + + return tlv->value; +} + +static u16 ath12k_hal_qcc2072_reo_status_decode_hdr(void *reo_desc, void **desc) +{ + struct hal_tlv_hdr *tlv = reo_desc; + u16 tag; + + tag = (u16)le32_get_bits(tlv->tl, HAL_SRNG_TLV_HDR_TAG); + /* actual desc starts after tlv32_padding */ + *desc = tlv->value + sizeof(u32); + + return tag; +} + +static bool ath12k_hw_qcc2072_dp_rx_h_msdu_done(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.msdu_end.info14, + RX_MSDU_END_INFO14_MSDU_DONE); +} + +static bool ath12k_hw_qcc2072_dp_rx_h_l4_cksum_fail(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.msdu_end.info13, + RX_MSDU_END_INFO13_TCP_UDP_CKSUM_FAIL); +} + +static bool ath12k_hw_qcc2072_dp_rx_h_ip_cksum_fail(struct hal_rx_desc *desc) +{ + return !!le32_get_bits(desc->u.qcc2072.msdu_end.info13, + RX_MSDU_END_INFO13_IP_CKSUM_FAIL); +} + +static bool ath12k_hw_qcc2072_dp_rx_h_is_decrypted(struct hal_rx_desc *desc) +{ + return (le32_get_bits(desc->u.qcc2072.msdu_end.info14, + RX_MSDU_END_INFO14_DECRYPT_STATUS_CODE) == + RX_DESC_DECRYPT_STATUS_CODE_OK); +} + +static u32 ath12k_hw_qcc2072_dp_rx_h_mpdu_err(struct hal_rx_desc *desc) +{ + u32 info = __le32_to_cpu(desc->u.qcc2072.msdu_end.info13); + u32 errmap = 0; + + if (info & RX_MSDU_END_INFO13_FCS_ERR) + errmap |= HAL_RX_MPDU_ERR_FCS; + + if (info & RX_MSDU_END_INFO13_DECRYPT_ERR) + errmap |= HAL_RX_MPDU_ERR_DECRYPT; + + if (info & RX_MSDU_END_INFO13_TKIP_MIC_ERR) + errmap |= HAL_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_MSDU_END_INFO13_A_MSDU_ERROR) + errmap |= HAL_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_MSDU_END_INFO13_OVERFLOW_ERR) + errmap |= HAL_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_MSDU_END_INFO13_MSDU_LEN_ERR) + errmap |= HAL_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_MSDU_END_INFO13_MPDU_LEN_ERR) + errmap |= HAL_RX_MPDU_ERR_MPDU_LEN; + + return errmap; +} + +static u32 ath12k_hw_qcc2072_get_rx_desc_size(void) +{ + return sizeof(struct hal_rx_desc_qcc2072); +} + +static u8 ath12k_hw_qcc2072_rx_desc_get_msdu_src_link(struct hal_rx_desc *desc) +{ + return 0; +} + +const struct hal_rx_ops hal_rx_qcc2072_ops = { + .rx_desc_get_first_msdu = ath12k_hw_qcc2072_rx_desc_get_first_msdu, + .rx_desc_get_last_msdu = ath12k_hw_qcc2072_rx_desc_get_last_msdu, + .rx_desc_get_l3_pad_bytes = ath12k_hw_qcc2072_rx_desc_get_l3_pad_bytes, + .rx_desc_encrypt_valid = ath12k_hw_qcc2072_rx_desc_encrypt_valid, + .rx_desc_get_encrypt_type = ath12k_hw_qcc2072_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath12k_hw_qcc2072_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath12k_hw_qcc2072_rx_desc_get_mesh_ctl, + .rx_desc_get_mpdu_seq_ctl_vld = ath12k_hw_qcc2072_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath12k_hw_qcc2072_rx_desc_get_mpdu_fc_valid, + .rx_desc_get_mpdu_start_seq_no = ath12k_hw_qcc2072_rx_desc_get_mpdu_start_seq_no, + .rx_desc_get_msdu_len = ath12k_hw_qcc2072_rx_desc_get_msdu_len, + .rx_desc_get_msdu_sgi = ath12k_hw_qcc2072_rx_desc_get_msdu_sgi, + .rx_desc_get_msdu_rate_mcs = ath12k_hw_qcc2072_rx_desc_get_msdu_rate_mcs, + .rx_desc_get_msdu_rx_bw = ath12k_hw_qcc2072_rx_desc_get_msdu_rx_bw, + .rx_desc_get_msdu_freq = ath12k_hw_qcc2072_rx_desc_get_msdu_freq, + .rx_desc_get_msdu_pkt_type = ath12k_hw_qcc2072_rx_desc_get_msdu_pkt_type, + .rx_desc_get_msdu_nss = ath12k_hw_qcc2072_rx_desc_get_msdu_nss, + .rx_desc_get_mpdu_tid = ath12k_hw_qcc2072_rx_desc_get_mpdu_tid, + .rx_desc_get_mpdu_peer_id = ath12k_hw_qcc2072_rx_desc_get_mpdu_peer_id, + .rx_desc_copy_end_tlv = ath12k_hw_qcc2072_rx_desc_copy_end_tlv, + .rx_desc_get_mpdu_start_tag = ath12k_hw_qcc2072_rx_desc_get_mpdu_start_tag, + .rx_desc_get_mpdu_ppdu_id = ath12k_hw_qcc2072_rx_desc_get_mpdu_ppdu_id, + .rx_desc_set_msdu_len = ath12k_hw_qcc2072_rx_desc_set_msdu_len, + .rx_desc_get_msdu_payload = ath12k_hw_qcc2072_rx_desc_get_msdu_payload, + .rx_desc_get_mpdu_start_offset = ath12k_hw_qcc2072_rx_desc_get_mpdu_start_offset, + .rx_desc_get_msdu_end_offset = ath12k_hw_qcc2072_rx_desc_get_msdu_end_offset, + .rx_desc_mac_addr2_valid = ath12k_hw_qcc2072_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath12k_hw_qcc2072_rx_desc_mpdu_start_addr2, + .rx_desc_is_da_mcbc = ath12k_hw_qcc2072_rx_desc_is_da_mcbc, + .rx_desc_get_dot11_hdr = ath12k_hw_qcc2072_rx_desc_get_dot11_hdr, + .rx_desc_get_crypto_header = ath12k_hw_qcc2072_rx_desc_get_crypto_hdr, + .dp_rx_h_msdu_done = ath12k_hw_qcc2072_dp_rx_h_msdu_done, + .dp_rx_h_l4_cksum_fail = ath12k_hw_qcc2072_dp_rx_h_l4_cksum_fail, + .dp_rx_h_ip_cksum_fail = ath12k_hw_qcc2072_dp_rx_h_ip_cksum_fail, + .dp_rx_h_is_decrypted = ath12k_hw_qcc2072_dp_rx_h_is_decrypted, + .dp_rx_h_mpdu_err = ath12k_hw_qcc2072_dp_rx_h_mpdu_err, + .rx_desc_get_desc_size = ath12k_hw_qcc2072_get_rx_desc_size, + .rx_desc_get_msdu_src_link_id = ath12k_hw_qcc2072_rx_desc_get_msdu_src_link, +}; + +const struct hal_ops hal_qcc2072_ops = { + .create_srng_config = ath12k_hal_srng_create_config_qcc2072, + .tcl_to_wbm_rbm_map = ath12k_hal_wcn7850_tcl_to_wbm_rbm_map, + .reo_init_cmd_ring = ath12k_hal_qcc2072_reo_init_cmd_ring, + .reo_cmd_encode_hdr = ath12k_hal_qcc2072_reo_cmd_encode_hdr, + .reo_status_decode_hdr = ath12k_hal_qcc2072_reo_status_decode_hdr, + .rxdma_ring_wmask_rx_mpdu_start = NULL, + .rxdma_ring_wmask_rx_msdu_end = NULL, + .get_hal_rx_compact_ops = NULL, +}; static int ath12k_hal_alloc_cont_rdp(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index d84f71280d0ed..d8af9dc1155a1 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1115,10 +1115,12 @@ struct hal_ops { extern const struct hal_ops hal_qcn9274_ops; extern const struct hal_ops hal_wcn7850_ops; +extern const struct hal_ops hal_qcc2072_ops; extern const struct hal_rx_ops hal_rx_qcn9274_ops; extern const struct hal_rx_ops hal_rx_qcn9274_compact_ops; extern const struct hal_rx_ops hal_rx_wcn7850_ops; +extern const struct hal_rx_ops hal_rx_qcc2072_ops; u32 ath12k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid); void ath12k_hal_reo_qdesc_setup(struct hal_rx_reo_queue *qdesc, diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 13ddac4a94125..2e321a913c4af 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -1207,6 +1207,13 @@ struct hal_reo_get_queue_stats { * Hole_count */ +struct hal_reo_get_queue_stats_qcc2072 { + struct hal_reo_cmd_hdr cmd; + __le32 queue_addr_lo; + __le32 info0; + __le32 rsvd0[6]; +} __packed; + #define HAL_REO_FLUSH_QUEUE_INFO0_DESC_ADDR_HI GENMASK(7, 0) #define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_DESC_ADDR BIT(8) #define HAL_REO_FLUSH_QUEUE_INFO0_BLOCK_RESRC_IDX GENMASK(10, 9) @@ -2728,6 +2735,24 @@ struct hal_reo_get_queue_stats_status { * entries into this Ring has looped around the ring. */ +struct hal_reo_get_queue_stats_status_qcc2072 { + __le32 tlv32_padding; + struct hal_reo_status_hdr hdr; + __le32 info0; + __le32 pn[4]; + __le32 last_rx_enqueue_timestamp; + __le32 last_rx_dequeue_timestamp; + __le32 rx_bitmap[9]; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 num_mpdu_frames; + __le32 num_msdu_frames; + __le32 total_bytes; + __le32 info4; + __le32 info5; +} __packed; + #define HAL_REO_STATUS_LOOP_CNT GENMASK(31, 28) #define HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED BIT(0) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index 7e5142fa9fcbb..d3ebd859219e9 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1805,7 +1805,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .wmi_init = ath12k_wmi_init_wcn7850, - .hal_ops = &hal_wcn7850_ops, + .hal_ops = &hal_qcc2072_ops, .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01) | BIT(CNSS_PCIE_PERST_NO_PULL_V01) | diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 6c600473b4021..8090f89254c39 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1528,11 +1528,28 @@ struct hal_rx_desc_wcn7850 { u8 msdu_payload[]; }; +struct rx_pkt_hdr_tlv_qcc2072 { + __le32 tag; + __le64 phy_ppdu_id; + u8 rx_pkt_hdr[HAL_RX_BE_PKT_HDR_TLV_LEN]; +}; + +struct hal_rx_desc_qcc2072 { + __le32 msdu_end_tag; + struct rx_msdu_end_qcn9274 msdu_end; + u8 rx_padding0[RX_BE_PADDING0_BYTES]; + __le32 mpdu_start_tag; + struct rx_mpdu_start_qcn9274 mpdu_start; + struct rx_pkt_hdr_tlv_qcc2072 pkt_hdr_tlv; + u8 msdu_payload[]; +}; + struct hal_rx_desc { union { struct hal_rx_desc_qcn9274 qcn9274; struct hal_rx_desc_qcn9274_compact qcn9274_compact; struct hal_rx_desc_wcn7850 wcn7850; + struct hal_rx_desc_qcc2072 qcc2072; } u; } __packed; From e2be85615b90475652dfb0667ca8fab82e063f1f Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:52:13 +0800 Subject: [PATCH 15/20] BACKPORT: FROMLIST: wifi: ath12k: add hardware ops support for QCC2072 Due to HAL descriptors, QCC2027 has different offsets of MPDU start tag and MSDU end tag, compared with other chips. Hence add new hardware ops structure for QCC2072. All ops are directly taken from WCN7850, with the exception to rxdma_ring_sel_config, which needs a new function ath12k_dp_rxdma_ring_sel_config_qcc2072() to handle the difference mentioned above. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-13-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/dp_rx.c | 42 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/dp_rx.h | 1 + drivers/net/wireless/ath/ath12k/hal.c | 11 +++++++ drivers/net/wireless/ath/ath12k/hal_rx.h | 2 ++ drivers/net/wireless/ath/ath12k/hw.c | 11 ++++++- 5 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 0ae4b7c36716a..757ecee730339 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -4607,6 +4607,48 @@ int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab) return ret; } +int ath12k_dp_rxdma_ring_sel_config_qcc2072(struct ath12k_base *ab) +{ + struct ath12k_dp *dp = &ab->dp; + struct htt_rx_ring_tlv_filter tlv_filter = {}; + u32 ring_id; + int ret = 0; + u32 hal_rx_desc_sz = ab->hal.hal_desc_sz; + int i; + + ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id; + + tlv_filter.rx_filter = HTT_RX_TLV_FLAGS_RXDMA_RING; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR; + tlv_filter.pkt_filter_flags3 = 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; + tlv_filter.offset_valid = true; + tlv_filter.rx_packet_offset = hal_rx_desc_sz; + + tlv_filter.rx_header_offset = offsetof(struct hal_rx_desc_qcc2072, pkt_hdr_tlv); + + tlv_filter.rx_mpdu_start_offset = + ath12k_hal_rx_desc_get_mpdu_start_offset_qcc2072(); + tlv_filter.rx_msdu_end_offset = + ath12k_hal_rx_desc_get_msdu_end_offset_qcc2072(); + + /* TODO: Selectively subscribe to required qwords within msdu_end + * and mpdu_start and setup the mask in below msg + * and modify the rx_desc struct + */ + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = dp->rx_mac_buf_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, i, + HAL_RXDMA_BUF, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + } + + return ret; +} + int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 69d0a36a91d88..053b2f69abe36 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -170,6 +170,7 @@ u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab, void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info); int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab); int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab); +int ath12k_dp_rxdma_ring_sel_config_qcc2072(struct ath12k_base *ab); int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index 0fecb5ff10ac4..b2549f5245fee 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -2134,6 +2134,17 @@ const struct hal_ops hal_qcc2072_ops = { .rxdma_ring_wmask_rx_msdu_end = NULL, .get_hal_rx_compact_ops = NULL, }; + +u32 ath12k_hal_rx_desc_get_mpdu_start_offset_qcc2072(void) +{ + return offsetof(struct hal_rx_desc_qcc2072, mpdu_start_tag); +} + +u32 ath12k_hal_rx_desc_get_msdu_end_offset_qcc2072(void) +{ + return offsetof(struct hal_rx_desc_qcc2072, msdu_end_tag); +} + static int ath12k_hal_alloc_cont_rdp(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index d1ad7747b82c4..e1f8a7cd5f2c9 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -1168,4 +1168,6 @@ void ath12k_hal_rx_msdu_list_get(struct ath12k *ar, struct hal_rx_msdu_list *msdu_list, u16 *num_msdus); +u32 ath12k_hal_rx_desc_get_mpdu_start_offset_qcc2072(void); +u32 ath12k_hal_rx_desc_get_msdu_end_offset_qcc2072(void); #endif diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index d3ebd859219e9..d144b69b33e20 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -147,6 +147,15 @@ static const struct ath12k_hw_ops wcn7850_ops = { .is_frame_link_agnostic = ath12k_is_frame_link_agnostic_wcn7850, }; +static const struct ath12k_hw_ops qcc2072_ops = { + .get_hw_mac_from_pdev_id = ath12k_hw_qcn9274_mac_from_pdev_id, + .mac_id_to_pdev_id = ath12k_hw_mac_id_to_pdev_id_wcn7850, + .mac_id_to_srng_id = ath12k_hw_mac_id_to_srng_id_wcn7850, + .rxdma_ring_sel_config = ath12k_dp_rxdma_ring_sel_config_qcc2072, + .get_ring_selector = ath12k_hw_get_ring_selector_wcn7850, + .dp_srng_is_tx_comp_ring = ath12k_dp_srng_is_comp_ring_wcn7850, +}; + #define ATH12K_TX_RING_MASK_0 0x1 #define ATH12K_TX_RING_MASK_1 0x2 #define ATH12K_TX_RING_MASK_2 0x4 @@ -1765,7 +1774,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850, .internal_sleep_clock = true, - .hw_ops = &wcn7850_ops, + .hw_ops = &qcc2072_ops, .ring_mask = &ath12k_hw_ring_mask_wcn7850, .regs = &qcc2072_regs, From 34d03f65bf3442426ce2035aca246b2fdb379e4c Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:53:38 +0800 Subject: [PATCH 16/20] BACKPORT: FROMLIST: wifi: ath12k: limit number of channels per WMI command Currently the number of channels can be sent in a single WMI command is calculated based on the maximum message length of the target, this results in WMI exchange hang for QCC2072 as its firmware can not support those many channels in a single command. Add a limit to avoid this issue. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-16-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/wmi.c | 3 ++- drivers/net/wireless/ath/ath12k/wmi.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f8dcec7492a18..f1a592e1c8fbe 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -2903,7 +2903,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar, max_chan_limit = (wmi->wmi_ab->max_msg_len[ar->pdev_idx] - len) / sizeof(*chan_info); - num_send_chans = min(arg->nallchans, max_chan_limit); + num_send_chans = min3(arg->nallchans, max_chan_limit, + ATH12K_WMI_MAX_NUM_CHAN_PER_CMD); arg->nallchans -= num_send_chans; len += sizeof(*chan_info) * num_send_chans; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index f99fced1610e6..7ac7c81163f96 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -6323,6 +6323,9 @@ struct ath12k_wmi_rssi_dbm_conv_info_arg { s8 min_nf_dbm; }; +/* each WMI cmd can hold 58 channel entries at most */ +#define ATH12K_WMI_MAX_NUM_CHAN_PER_CMD 58 + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, From d0c439bc07e6b662ef67fa8d211610d62e57ccfe Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 13 Nov 2025 13:55:35 +0800 Subject: [PATCH 17/20] BACKPORT: FROMLIST: wifi: ath12k: send peer meta data version to firmware Peer meta data version is currently not delivered to firmware, resulting in QCC2072 data path issues. Parse it from service ready ext2 event and send to firmware in WMI init command. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-17-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/wmi.c | 9 +++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f1a592e1c8fbe..10ffa7ec33b04 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -301,6 +301,11 @@ void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, config->num_multicast_filter_entries = 0x20; config->num_wow_filters = 0x16; config->num_keep_alive_pattern = 0; + + if (test_bit(WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT, ab->wmi_ab.svc_map)) + config->peer_metadata_ver = ATH12K_PEER_METADATA_V1A; + else + config->peer_metadata_ver = ab->wmi_ab.dp_peer_meta_data_ver; } #define PRIMAP(_hw_mode_) \ @@ -5573,6 +5578,10 @@ static int ath12k_wmi_svc_rdy_ext2_parse(struct ath12k_base *ab, ret); return ret; } + + ab->wmi_ab.dp_peer_meta_data_ver = + u32_get_bits(parse->arg.target_cap_flags, + WMI_TARGET_CAP_FLAGS_RX_PEER_METADATA_VERSION); break; case WMI_TAG_ARRAY_STRUCT: diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 7ac7c81163f96..703e2f7277f77 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -2782,6 +2782,8 @@ enum wmi_channel_width { #define WMI_EHT_MCS_NSS_10_11 GENMASK(11, 8) #define WMI_EHT_MCS_NSS_12_13 GENMASK(15, 12) +#define WMI_TARGET_CAP_FLAGS_RX_PEER_METADATA_VERSION GENMASK(1, 0) + struct wmi_service_ready_ext2_event { __le32 reg_db_version; __le32 hw_min_max_tx_power_2ghz; @@ -5231,6 +5233,8 @@ struct ath12k_wmi_base { struct ath12k_svc_ext_info svc_ext_info; u32 sbs_lower_band_end_freq; struct ath12k_hw_mode_info hw_mode_info; + + u8 dp_peer_meta_data_ver; }; struct wmi_pdev_set_bios_interface_cmd { From 1feb0eb91eb20d7a94fee7c78f9678047fc43f25 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Thu, 18 Dec 2025 17:47:41 +0800 Subject: [PATCH 18/20] BACKPORT: FROMLIST: wifi: ath12k: fix PCIE_LOCAL_REG_QRTR_NODE_ID definition for QCC2072 The definition of PCIE_LOCAL_REG_QRTR_NODE_ID in QCC2072 is incorrect, which causes the QMI connection to fail when ATH12K_FW_FEATURE_MULTI_QRTR_ID is enabled. To resolve this issue, move it to the hardware register table. Note IPQ5332 is not affected as it is not PCIe based device. Tested-on: QCC2072 hw1.0 PCI CI_WLAN.COL.1.0-01668.1-QCACOLSWPL_V1_TO_SILICONZ-9 Signed-off-by: Miaoqing Pan Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-18-87928cf8e547@oss.qualcomm.com/ --- drivers/net/wireless/ath/ath12k/hw.c | 8 ++++++++ drivers/net/wireless/ath/ath12k/hw.h | 2 ++ drivers/net/wireless/ath/ath12k/pci.c | 3 +-- drivers/net/wireless/ath/ath12k/pci.h | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index d144b69b33e20..cfeda87b31b93 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1017,6 +1017,8 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = { .hal_umac_ce1_dest_reg_base = 0x01b83000, .gcc_gcc_pcie_hot_rst = 0x1e38338, + + .qrtr_node_id = 0x1e03164, }; static const struct ath12k_hw_regs qcn9274_v2_regs = { @@ -1110,6 +1112,8 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { .hal_umac_ce1_dest_reg_base = 0x01b83000, .gcc_gcc_pcie_hot_rst = 0x1e38338, + + .qrtr_node_id = 0x1e03164, }; static const struct ath12k_hw_regs ipq5332_regs = { @@ -1285,6 +1289,8 @@ static const struct ath12k_hw_regs wcn7850_regs = { .hal_umac_ce1_dest_reg_base = 0x01b83000, .gcc_gcc_pcie_hot_rst = 0x1e40304, + + .qrtr_node_id = 0x1e03164, }; static const struct ath12k_hw_regs qcc2072_regs = { @@ -1372,6 +1378,8 @@ static const struct ath12k_hw_regs qcc2072_regs = { .hal_umac_ce1_dest_reg_base = 0x01b83000, .gcc_gcc_pcie_hot_rst = 0x1e65304, + + .qrtr_node_id = 0x1e03300, }; static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = { diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index b1d2aebe59734..84e0e3c6b5d25 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -368,6 +368,8 @@ struct ath12k_hw_regs { u32 hal_reo_status_ring_base; u32 gcc_gcc_pcie_hot_rst; + + u32 qrtr_node_id; }; static inline const char *ath12k_bd_ie_type_str(enum ath12k_bd_ie_type type) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index f401d0d890f72..af20c7eff63ff 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -41,7 +41,6 @@ #define QCN9274_DEVICE_ID 0x1109 #define WCN7850_DEVICE_ID 0x1107 -#define PCIE_LOCAL_REG_QRTR_NODE_ID 0x1E03164 #define DOMAIN_NUMBER_MASK GENMASK(7, 4) #define BUS_NUMBER_MASK GENMASK(3, 0) @@ -965,7 +964,7 @@ static void ath12k_pci_update_qrtr_node_id(struct ath12k_base *ab) * writes to the given register, it is available for firmware when the QMI service * is spawned. */ - reg = PCIE_LOCAL_REG_QRTR_NODE_ID & WINDOW_RANGE_MASK; + reg = PCIE_LOCAL_REG_QRTR_NODE_ID(ab) & WINDOW_RANGE_MASK; ath12k_pci_write32(ab, reg, ab_pci->qmi_instance); ath12k_dbg(ab, ATH12K_DBG_PCI, "pci reg 0x%x instance 0x%x read val 0x%x\n", diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h index 541fba4373348..8b0e47a95df8f 100644 --- a/drivers/net/wireless/ath/ath12k/pci.h +++ b/drivers/net/wireless/ath/ath12k/pci.h @@ -58,6 +58,11 @@ #define QCN9274_QFPROM_RAW_RFA_PDET_ROW13_LSB 0x1E20338 #define OTP_BOARD_ID_MASK GENMASK(15, 0) +#define PCIE_LOCAL_REG_QRTR_NODE_ID(ab) \ + ((ab)->hw_params->regs->qrtr_node_id) +#define DOMAIN_NUMBER_MASK GENMASK(7, 4) +#define BUS_NUMBER_MASK GENMASK(3, 0) + #define PCI_BAR_WINDOW0_BASE 0x1E00000 #define PCI_BAR_WINDOW0_END 0x1E7FFFC #define PCI_SOC_RANGE_MASK 0x3FFF From 6ed7ea2645b63a40b7baeb9116f7813a74545743 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Thu, 18 Dec 2025 17:50:12 +0800 Subject: [PATCH 19/20] BACKPORT: FROMLIST: wifi: ath12k: enable QCC2072 support Add QCC2072's PCI device ID to the table such that it can be probed, also populate some parameters when probing. Tested-on: QCC2072 hw1.0 PCI WLAN.COL.1.0-01560-QCACOLSWPL_V1_TO_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3 Signed-off-by: Baochen Qiang Link: https://lore.kernel.org/linux-wireless/20251218-ath12k-support-qcc2072-v1-19-87928cf8e547@oss.qualcomm.com/ Signed-off-by: Miaoqing Pan --- drivers/net/wireless/ath/ath12k/pci.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index af20c7eff63ff..11c1c3fcf59f8 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -23,6 +23,7 @@ #define WINDOW_ENABLE_BIT 0x40000000 #define WINDOW_REG_ADDRESS 0x310c +#define WINDOW_REG_ADDRESS_QCC2072 0x3278 #define WINDOW_VALUE_MASK GENMASK(24, 19) #define WINDOW_START 0x80000 #define WINDOW_RANGE_MASK GENMASK(18, 0) @@ -40,6 +41,7 @@ #define QCN9274_DEVICE_ID 0x1109 #define WCN7850_DEVICE_ID 0x1107 +#define QCC2072_DEVICE_ID 0x1112 #define DOMAIN_NUMBER_MASK GENMASK(7, 4) #define BUS_NUMBER_MASK GENMASK(3, 0) @@ -47,6 +49,7 @@ static const struct pci_device_id ath12k_pci_id_table[] = { { PCI_VDEVICE(QCOM, QCN9274_DEVICE_ID) }, { PCI_VDEVICE(QCOM, WCN7850_DEVICE_ID) }, + { PCI_VDEVICE(QCOM, QCC2072_DEVICE_ID) }, {} }; @@ -159,6 +162,11 @@ static const struct ath12k_pci_ops ath12k_pci_ops_wcn7850 = { .release = ath12k_pci_bus_release, }; +static const struct ath12k_pci_ops ath12k_pci_ops_qcc2027 = { + .wakeup = ath12k_pci_bus_wake_up, + .release = ath12k_pci_bus_release, +}; + static void ath12k_pci_select_window(struct ath12k_pci *ab_pci, u32 offset) { struct ath12k_base *ab = ab_pci->ab; @@ -1659,6 +1667,16 @@ static int ath12k_pci_probe(struct pci_dev *pdev, goto err_pci_free_region; } break; + case QCC2072_DEVICE_ID: + ab->id.bdf_search = ATH12K_BDF_SEARCH_BUS_AND_BOARD; + ab_pci->msi_config = &ath12k_msi_config[0]; + ab->static_window_map = false; + ab_pci->pci_ops = &ath12k_pci_ops_qcc2027; + ab_pci->window_reg_addr = WINDOW_REG_ADDRESS_QCC2072; + ab->hal_rx_ops = &hal_rx_qcc2072_ops; + /* there is only one version till now */ + ab->hw_rev = ATH12K_HW_QCC2072_HW10; + break; default: dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n", From 8a861bfa67fdffee41a0e63baf05c272f3b19586 Mon Sep 17 00:00:00 2001 From: Qian Zhang Date: Wed, 7 Jan 2026 14:02:55 +0800 Subject: [PATCH 20/20] FROMLIST: wifi: ath11k: Fix failure to connect to a 6 GHz AP STA fails to connect to a 6 GHz AP with the following errors: ath11k_pci 0000:01:00.0: failed to handle chan list with power type 1 wlp1s0: deauthenticating from c8:a3:e8:dd:41:e3 by local choice (Reason: 3=DEAUTH_LEAVING) ath11k_reg_handle_chan_list() treats the update as redundant and returns -EINVAL. That causes the connection attempt to fail. Avoid unnecessary validation during association. Apply the regulatory redundant check only when the power type is IEEE80211_REG_UNSET_AP, which only occurs during core initialization. Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41 Signed-off-by: Qian Zhang Link: https://lore.kernel.org/all/20260108034607.812885-1-qian.zhang@oss.qualcomm.com/ --- drivers/net/wireless/ath/ath11k/reg.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index d62a2014315a0..49b79648752cf 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -926,8 +926,11 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab, */ if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, - (char *)reg_info->alpha2, 2)) - goto retfail; + (char *)reg_info->alpha2, 2) && + power_type == IEEE80211_REG_UNSET_AP) { + ath11k_reg_reset_info(reg_info); + return 0; + } /* Intersect new rules with default regd if a new country setting was * requested, i.e a default regd was already set during initialization